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
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
43 + 50 = ?
Subscribe to this entry?

 
 [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

Add a Patch

Pull Requests

Add a Pull Request

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-2024 The PHP Group
All rights reserved.
Last updated: Tue Apr 30 20:01:30 2024 UTC