diff options
author | Nikita Popov <nikic@php.net> | 2015-05-05 19:52:03 +0200 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2015-05-06 18:13:19 +0200 |
commit | ab97606b8a9501599cf027b13b72fc0c92e3a16e (patch) | |
tree | 186541385b1284b6ada0b5febc53d9cd624e4005 /Zend | |
parent | bc2ff4a299a182c4ab9681fa6ba52cf747f53ace (diff) | |
download | php-git-ab97606b8a9501599cf027b13b72fc0c92e3a16e.tar.gz |
Fix compiler assumptions about self/etc wrt closures
* Don't throw an error if self/parent/static are used in a closure
(outside a class).
* Don't propagate self:: constants into closures
* Use runtime fetch for self::class in closures
Fixes bug #66811.
Diffstat (limited to 'Zend')
-rw-r--r-- | Zend/tests/bug66811.phpt | 37 | ||||
-rw-r--r-- | Zend/tests/no_class_const_propagation_in_closures.phpt | 25 | ||||
-rw-r--r-- | Zend/tests/return_types/025.phpt | 10 | ||||
-rw-r--r-- | Zend/tests/return_types/027.phpt | 11 | ||||
-rw-r--r-- | Zend/zend_compile.c | 44 |
5 files changed, 113 insertions, 14 deletions
diff --git a/Zend/tests/bug66811.phpt b/Zend/tests/bug66811.phpt new file mode 100644 index 0000000000..6409ceb4ab --- /dev/null +++ b/Zend/tests/bug66811.phpt @@ -0,0 +1,37 @@ +--TEST-- +Bug #66811: Cannot access static::class in lambda, writen outside of a class +--FILE-- +<?php +class A { + public static function f() { + return function () { + var_dump(self::class); + var_dump(static::class); + }; + } +} + +class B extends A {} + +$f = B::f(); +$f(); + +$g = $f->bindTo(null, A::class); +$g(); + +$foo = function () { + var_dump(self::class); + var_dump(static::class); +}; + +$bar = $foo->bindTo(null, A::class); +$bar(); + +?> +--EXPECT-- +string(1) "A" +string(1) "B" +string(1) "A" +string(1) "A" +string(1) "A" +string(1) "A" diff --git a/Zend/tests/no_class_const_propagation_in_closures.phpt b/Zend/tests/no_class_const_propagation_in_closures.phpt new file mode 100644 index 0000000000..e446573b9d --- /dev/null +++ b/Zend/tests/no_class_const_propagation_in_closures.phpt @@ -0,0 +1,25 @@ +--TEST-- +self:: class constants should not be propagated into closures, due to scope rebinding +--FILE-- +<?php + +class A { + const C = 'A::C'; + + public function f() { + return function() { + return self::C; + }; + } +} + +class B { + const C = 'B::C'; +} + +$f = (new A)->f(); +var_dump($f->bindTo(null, 'B')()); + +?> +--EXPECT-- +string(4) "B::C" diff --git a/Zend/tests/return_types/025.phpt b/Zend/tests/return_types/025.phpt index 16a608bacd..412b81e3c1 100644 --- a/Zend/tests/return_types/025.phpt +++ b/Zend/tests/return_types/025.phpt @@ -1,10 +1,12 @@ --TEST-- -Return type of self is not allowed in closure +Return type of self is allowed in closure --FILE-- <?php -$c = function(): self {}; +$c = function(): self { return $this; }; +var_dump($c->call(new stdClass)); ---EXPECTF-- -Fatal error: Cannot use "self" when no class scope is active in %s on line 3 +--EXPECT-- +object(stdClass)#2 (0) { +} diff --git a/Zend/tests/return_types/027.phpt b/Zend/tests/return_types/027.phpt index 1abc7f733a..a5d2c321bf 100644 --- a/Zend/tests/return_types/027.phpt +++ b/Zend/tests/return_types/027.phpt @@ -1,9 +1,14 @@ --TEST-- -Return type of parent is not allowed in closure +Return type of parent is allowed in closure --FILE-- <?php -$c = function(): parent {}; +class A {} +class B extends A {} + +$c = function(parent $x): parent { return $x; }; +var_dump($c->bindTo(null, 'B')(new A)); --EXPECTF-- -Fatal error: Cannot use "parent" when no class scope is active in %s on line 3 +object(A)#%d (0) { +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2e05b35966..a543821415 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1318,12 +1318,42 @@ static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool i } /* }}} */ +static inline zend_bool zend_is_scope_known() /* {{{ */ +{ + if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) { + /* Closures can be rebound to a different scope */ + return 0; + } + + if (!CG(active_class_entry)) { + /* Not being in a scope is a known scope */ + return 1; + } + + /* For traits self etc refers to the using class, not the trait itself */ + return (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) == 0; +} +/* }}} */ + +static inline zend_bool class_name_refers_to_active_ce(zend_string *class_name, uint32_t fetch_type) /* {{{ */ +{ + if (!CG(active_class_entry)) { + return 0; + } + if (fetch_type == ZEND_FETCH_CLASS_SELF && zend_is_scope_known()) { + return 1; + } + return fetch_type == ZEND_FETCH_CLASS_DEFAULT + && zend_string_equals_ci(class_name, CG(active_class_entry)->name); +} +/* }}} */ + static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ { uint32_t fetch_type = zend_get_class_fetch_type(class_name); zval *c; - if (CG(active_class_entry) && (fetch_type == ZEND_FETCH_CLASS_SELF || (fetch_type == ZEND_FETCH_CLASS_DEFAULT && zend_string_equals_ci(class_name, CG(active_class_entry)->name)))) { + if (class_name_refers_to_active_ce(class_name, fetch_type)) { c = zend_hash_find(&CG(active_class_entry)->constants_table, name); } else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), class_name->val, class_name->len); @@ -1623,9 +1653,9 @@ static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ } /* }}} */ -void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ +static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ { - if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry)) { + if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry) && zend_is_scope_known()) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active", fetch_type == ZEND_FETCH_CLASS_SELF ? "self" : fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static"); @@ -6348,12 +6378,12 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */ switch (fetch_type) { case ZEND_FETCH_CLASS_SELF: - if (CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) { - zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); - opline->extended_value = fetch_type; - } else { + if (CG(active_class_entry) && zend_is_scope_known()) { result->op_type = IS_CONST; ZVAL_STR_COPY(&result->u.constant, CG(active_class_entry)->name); + } else { + zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); + opline->extended_value = fetch_type; } break; case ZEND_FETCH_CLASS_STATIC: |