hi Bob,
I still think that current array usage in constant expressions is not
consistent and dangerous. It "smells" to me, and I think it may bring
troubles in the future even if the existing known bugs are fixed.
I see few issues:
1) It is possible to declare array class constants however they can't be
used. I can't remember why array in constants were prohibited before and
what problems they brought. The following script works without any warnings.
<?php
class Foo {
const BAR = [1];
}
?>
2) In some cases array constants may be used, but not in the others.
<?php
class Foo {
const BAR = [0];
static $a = Foo::BAR; // constant array usage
}
var_dump(Foo::$a); // prints array
var_dump(Foo::BAR); // emits fatal error
?>
3) The fact that constants are allowed in compile time and even stored, but
can't be used confuses me as well as the error message "PHP Fatal error:
Arrays are not allowed in constants at run-time".
4) Zend/tests/constant_expressions_arrays.phpt crashes whit
opcache.protect_memory=1 (that indicates petential SHM memory corruption)
This may be fixed with the following patch:
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 144930e..f1aab9a 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -4323,6 +4323,16 @@ static int ZEND_FASTCALL
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCOD
c.value = *tmp_ptr;
} else {
INIT_PZVAL_COPY(&c.value, val);
+ if (Z_TYPE(c.value) == IS_ARRAY) {
+ HashTable *ht;
+
+ ALLOC_HASHTABLE(ht);
+ zend_hash_init(ht,
zend_hash_num_elements(Z_ARRVAL(c.value)), NULL, ZVAL_PTR_DTOR, 0);
+ zend_hash_copy(ht, Z_ARRVAL(c.value),
(copy_ctor_func_t) zval_deep_copy, NULL, sizeof(zval *));
+ Z_ARRVAL(c.value) = ht;
+ } else {
+ zval_copy_ctor(&c.value);
+ }
zval_copy_ctor(&c.value);
}
c.flags = CONST_CS; /* non persistent, case sensetive */
5) Circular constant references crash (found by Nikita)
<?php
class A {
const FOO = [self::BAR];
const BAR = [self::FOO];
}
var_dump(A::FOO); // crashes because of infinity recursion
?>
I didn't find any useful way to fix it. One of the ideas with following
hack seemed to work, but it breaks another test
(Zend/tests/constant_expressions_classes.phpt)
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 12f9405..8798737 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -251,10 +251,22 @@ ZEND_API void zend_ast_evaluate(zval *result,
zend_ast *ast, zend_class_entry *s
zval_dtor(&op2);
break;
case ZEND_CONST:
- *result = *ast->u.val;
- zval_copy_ctor(result);
- if (IS_CONSTANT_TYPE(Z_TYPE_P(result))) {
- zval_update_constant_ex(&result, 1, scope
TSRMLS_CC);
+ if (EG(in_execution) && EG(opline_ptr) &&
*EG(opline_ptr) &&
+ ((*EG(opline_ptr))->opcode == ZEND_RECV_INIT ||
+ (*EG(opline_ptr))->opcode ==
ZEND_DECLARE_CONST)) {
+ *result = *ast->u.val;
+ zval_copy_ctor(result);
+ if (IS_CONSTANT_TYPE(Z_TYPE_P(result))) {
+ zval_update_constant_ex(&result, 1,
scope TSRMLS_CC);
+ }
+ } else {
+ if (IS_CONSTANT_TYPE(Z_TYPE_P(ast->u.val)))
{
+
zval_update_constant_ex(&ast->u.val, 1, scope TSRMLS_CC);
+ *result = *ast->u.val;
+ } else {
+ *result = *ast->u.val;
+ zval_copy_ctor(result);
+ }
}
break;
case ZEND_BOOL_AND:
I spent few hours trying to find a solution, but failed. May be my ideas
could lead you to something...
Otherwise, I would recommend to remove this feature from PHP-5.6.
Thanks. Dmitry.
On Tue, Jul 22, 2014 at 10:00 AM, Dmitry Stogov <[email protected]> wrote:
> Hi Bob,
>
> Now I think it's not fixable by design :(
>
> I'll try to think about it later today.
> Could you please collect all related issues.
>
> Thanks. Dmitry.
>
>
> On Mon, Jul 21, 2014 at 8:36 PM, Bob Weinand <[email protected]> wrote:
>
>> Am 2.7.2014 um 15:43 schrieb Dmitry Stogov <[email protected]>:
>>
>> I don't have good ideas out of the box and I probably won't be able to
>> look into this before next week.
>>
>>
>> Hey, I still have no real idea how to solve it without breaking opcache.
>>
>> This one seems to be considered like a blocking bug for 5.6.
>>
>> Could you please try to fix this in a sane manner?
>>
>> Bob
>>
>
>