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 <dmi...@zend.com> 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 <bobw...@hotmail.com> wrote:
>
>> Am 2.7.2014 um 15:43 schrieb Dmitry Stogov <dmi...@zend.com>:
>>
>> 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
>>
>
>

Reply via email to