PHP内核之zend_try_ct_eval_const
我们来看看这么一段代码:
1 2 3 4
| <?php namespace foo { var_dump(true); }
|
执行结果如下:
1 2
| php other.php bool(true)
|
还算是符合我们的预期对吧。
我们再来看看这段代码:
1 2 3
| <?php define('foo\true', 'test'); require_once __DIR__ . '/other.php';
|
执行结果如下:
1 2
| php test.php string(4) "test"
|
因为在其他文件里面定义了foo\true
常量,导致引入other.php
文件的时候,true
的值就被无情的修改了。
问题是出在了zend_try_ct_eval_const
这个函数里面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qualified) { zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name); if (c && can_ct_eval_const(c)) { ZVAL_COPY_OR_DUP(zv, &c->value); return 1; }
{ const char *lookup_name = ZSTR_VAL(name); size_t lookup_len = ZSTR_LEN(name);
if (!is_fully_qualified) { zend_get_unqualified_name(name, &lookup_name, &lookup_len); }
if ((c = zend_get_special_const(lookup_name, lookup_len))) { ZVAL_COPY_VALUE(zv, &c->value); return 1; }
return 0; } }
|
可以看到,前面先调用can_ct_eval_const
来判断常量是否能够被替换。因为我们这里定义了foo\true
常量,所以这里就判断能够被替换。所以这里拿到的值就是test
了。
在PHP8
中,这被当作了BUG
来处理,解决方法也很简单,把zend_get_special_const
放到can_ct_eval_const
前面即可。special_const
的值有三个,true
、false
、null
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| s`tatic bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qualified) {
const char *lookup_name = ZSTR_VAL(name); size_t lookup_len = ZSTR_LEN(name);
if (!is_fully_qualified) { zend_get_unqualified_name(name, &lookup_name, &lookup_len); }
zend_constant *c; if ((c = zend_get_special_const(lookup_name, lookup_len))) { ZVAL_COPY_VALUE(zv, &c->value); return 1; } c = zend_hash_find_ptr(EG(zend_constants), name); if (c && can_ct_eval_const(c)) { ZVAL_COPY_OR_DUP(zv, &c->value); return 1; } return 0; }`
|