php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #46850 compile time depends on runtime constants ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION
Submitted: 2008-12-12 14:57 UTC Modified: 2009-01-11 09:32 UTC
From: moo dot tinys at gmail dot com Assigned: dmitry (profile)
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.3CVS-2008-12-12 (CVS) OS: *
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: moo dot tinys at gmail dot com
New email:
PHP Version: OS:

 

 [2008-12-12 14:57 UTC] moo dot tinys at gmail dot com
Description:
------------
let's focus our eyes on
====  zend_compile.c function zend_do_receive_arg
void zend_do_receive_arg(zend_uchar op, const znode *var, const znode
*offset, const znode *initialization, znode *class_type, const znode
*varname, zend_uchar pass_by_reference TSRMLS_DC)
{
...........
           if (op == ZEND_RECV_INIT) {
               if (Z_TYPE(initialization->u.constant) == IS_NULL ||
(Z_TYPE(initialization->u.constant) == IS_CONSTANT &&
!strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))) {
                   cur_arg_info->allow_null = 1;
               } else {
                   zend_error(E_COMPILE_ERROR, "Default value for
parameters with a class type hint can only be NULL");
               }
           }
======================
(the following gdb input/output is still using macro for your reading,
expand the macro if you want to execute)

test case 1
precondition: CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; before zend_compile_file
break at function zend_do_receive_arg and
(gdb) print Z_TYPE(initialization->u.constant) == IS_NULL
1 (true)
which means php still subst "null" to "IS_NULL"

test case 2
precondtion: let's assume ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION is
working for zend_do_receive_arg, simply empty hash table:
EG(zend_constants) = &an_empty_hash_table; before zend_compile_file
break at function zend_do_receive_arg and
(gdb) print Z_TYPE(initialization->u.constant) == IS_NULL
0 (false)
(gdb) print Z_TYPE(initialization->u.constant) == IS_CONSTANT
0 (false)

so what is that?
(gdb) print Z_TYPE(initialization->u.constant)
24
(gdb) print (Z_TYPE(initialization->u.constant) &
IS_CONSTANT_TYPE_MASK) == IS_CONSTANT
1 (true)
************ ok, we get the first bug
(gdb) p !strcasecmp(Z_STRVAL(initialization->u.constant), "NULL"))
0 (false)
why?
(gdb) p Z_STRVAL(initialization->u.constant)
"My\\NS\\null"
this is the reason. looks likestrcasecmp is not enough here at compile
time. or you could just handle abc(array $a = null) as a special case?

Reproduce code:
---------------
test.php
<?php
namespace My\NS;

use My\NS;

class A {
   public function test(A $obj = null) {
       var_dump($obj);
   }
}

?>

i don't think it easy to make a reproduce code, because
in case 1: ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION is disabled until you change it in extension or patch php for testing
and in case 2: even if you enable ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION, php is still free to access EG(zend_constants), EG(zend_constants) has to be empty to see the bug. see "actual result"

there's a simple patch to reproduce it. (not fix)
Index: zend_language_scanner.l
===================================================================
RCS file: /repository/ZendEngine2/zend_language_scanner.l,v
retrieving revision 1.131.2.11.2.13.2.32
diff -u -r1.131.2.11.2.13.2.32 zend_language_scanner.l
--- zend_language_scanner.l 4 Nov 2008 15:58:51 -0000   1.131.2.11.2.13.2.32
+++ zend_language_scanner.l 12 Dec 2008 14:54:47 -0000
@@ -311,6 +311,8 @@

 ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC)
 {
+   HashTable empty_hash, *old_hash;
+
    zend_lex_state original_lex_state;
    zend_op_array *op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
    zend_op_array *original_active_op_array = CG(active_op_array);
@@ -320,6 +322,12 @@
    znode retval_znode;
    zend_bool original_in_compilation = CG(in_compilation);

+   CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
+   zend_hash_init(&empty_hash, 1, NULL, NULL, 0);
+   old_hash = EG(zend_constants);
+   EG(zend_constants) = &empty_hash;
+   fprintf(stderr, "asdfasdf\n\n");
+
    retval_znode.op_type = IS_CONST;
    retval_znode.u.constant.type = IS_LONG;
    retval_znode.u.constant.value.lval = 1;
@@ -364,6 +372,10 @@
    if (compilation_successful) {
        zend_restore_lexical_state(&original_lex_state TSRMLS_CC);
    }
+
+   zend_hash_destroy(&empty_hash);
+   EG(zend_constants) = old_hash;
+
    return retval;
 }



Expected result:
----------------
in case 1: when ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION is enabled, "null" (code) should not be resolved to "null" (value, which is IS_NULL) at compile time, and should be resolve at runtime.

in case 2: at compile time, php should behavoir the same regardless to what EG(zend_constants). when EG(zend_constants) is empty, it should not raise ""Default value for
parameters with a class type hint can only be NULL" for the reproduce code above


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-01-09 19:07 UTC] stas@php.net
I'm not sure why you need to clean up EG(zend_constants). Together with runtime constants it also contains such constants as null, which has special "privilege" of being resolved at compile-time, since they are always the same and have special meaning. 
 [2009-01-11 09:32 UTC] dmitry@php.net
NULL, TRUE and FALSE may never be redefined in namespaces. So it's safe to always substitute them at compile time.

 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Sun Jun 15 14:01:35 2025 UTC