com php-src: Fix compiler assumptions about self/etc wrt closures: Zend/tests/bug66811.phpt Zend/tests/no_class_const_propag ation_in_closures.phpt
Zend/tests/return_types/02 5.phpt Zend/tests/return_types/027.phpt Zend/ zend_compile.c
| From: | Nikita Popov | Date: | Wed, 06 May 2015 16:13:19 +0000 |
| Subject: | com php-src: Fix compiler assumptions about self/etc wrt closures: Zend/tests/bug66811.phpt Zend/tests/no_class_const_propag ation_in_closures.phpt Zend/tests/return_types/02 5.phpt Zend/tests/return_types/027.phpt Zend/ zend_compile.c |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-86628@lists.php.net to get a copy of this message | ||
Commit: ab97606b8a9501599cf027b13b72fc0c92e3a16e
Author: Nikita Popov <nikic@php.net> Tue, 5 May 2015 19:52:03 +0200
Parents: bc2ff4a299a182c4ab9681fa6ba52cf747f53ace
Branches: master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=ab97606b8a9501599cf027b13b72fc0c92e3a16e
Log:
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.
Bugs:
https://bugs.php.net/66811
Changed paths:
A Zend/tests/bug66811.phpt
A Zend/tests/no_class_const_propagation_in_closures.phpt
M Zend/tests/return_types/025.phpt
M Zend/tests/return_types/027.phpt
M Zend/zend_compile.c
Diff:
diff --git a/Zend/tests/bug66811.phpt b/Zend/tests/bug66811.phpt
new file mode 100644
index 0000000..6409ceb
--- /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 0000000..e446573
--- /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 16a608b..412b81e 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 1abc7f7..a5d2c32 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 2e05b35..a543821 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: