diff options
author | Andrea Faulds <ajf@ajf.me> | 2014-07-30 03:21:44 +0100 |
---|---|---|
committer | Andrea Faulds <ajf@ajf.me> | 2014-07-30 03:21:44 +0100 |
commit | 85bf8b4ff19b9ab4f30be0265db9a18805b0ebc0 (patch) | |
tree | 08f6fbd6e4046a8e1b2c05ff9b886160d4cec38c | |
parent | f65bdda469dfc30bd28652a6aa94d369eac1b6b2 (diff) | |
download | php-git-85bf8b4ff19b9ab4f30be0265db9a18805b0ebc0.tar.gz |
Fixed unbound scoped closure edge cases and added tests for them
-rw-r--r-- | Zend/tests/closure_bind_unbound_scoped.phpt | 34 | ||||
-rw-r--r-- | Zend/zend_closures.c | 6 | ||||
-rw-r--r-- | Zend/zend_compile.h | 3 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 2 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 2 |
5 files changed, 41 insertions, 6 deletions
diff --git a/Zend/tests/closure_bind_unbound_scoped.phpt b/Zend/tests/closure_bind_unbound_scoped.phpt index a8347f8d90..2975d39ae6 100644 --- a/Zend/tests/closure_bind_unbound_scoped.phpt +++ b/Zend/tests/closure_bind_unbound_scoped.phpt @@ -2,6 +2,12 @@ Closure::bind and ::bindTo unbound_scoped parameter --FILE-- <?php + +$foo = function () {}; +$foo = $foo->bindTo(NULL, "StdClass", true); +// We need to check that you can call an unbound scoped closure without binding it (good idea or no) +$foo(); + class FooBar { private $x = 3; } @@ -13,14 +19,34 @@ $foo = function () { $foo = $foo->bindTo(NULL, "FooBar", true); // As it is unbound, not static, this will work -$foo->apply(new FooBar); +$foo->call(new FooBar); $foo = function () { var_dump($this->x); }; +// Make sure ::bind() works the same $foo = Closure::bind($foo, NULL, "FooBar", true); -$foo->apply(new FooBar); +$foo->call(new FooBar); + +// Ensure that binding and having it unscoped will not prevent an E_NOTICE for non-static methods called statically +class Qux { + function notStatic() {} +} +$x = new ReflectionMethod("Qux::notStatic"); +$x = $x->getClosure(new Qux); +$x = $x->bindTo(NULL, "Qux", true); +$x(); + +// Ensure that binding and having it unscoped will not prevent fatal error for trying to call an internal class's method with $this as NULL +$x = new ReflectionMethod("Closure::bindTo"); +$x = $x->getClosure(function () {}); +$x = $x->bindTo(NULL, "Closure", true); +$x(); ?> ---EXPECT-- +--EXPECTF-- +int(3) int(3) -int(3)
\ No newline at end of file + +Strict Standards: Non-static method Qux::notStatic() should not be called statically in %s on line 35 + +Fatal error: Non-static method Closure::bindTo() cannot be called statically in %s on line 41
\ No newline at end of file diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 72823a5a0b..a5735f4c79 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -563,6 +563,12 @@ ZEND_API void zend_create_closure_ex(zval *res, zend_function *func, zend_class_ If an unbound scoped closure is desired, the parameter must be set to 1*/ } else if (!unbound_scoped) { closure->func.common.fn_flags |= ZEND_ACC_STATIC; + /* Unbound but scoped was explicitly specified and we wish to avoid E_ERROR when calling without object + We don't do this if it has implict allowed static (i.e. is a method, should E_STRICT) + Nor do we do this if it's an internal function (which would blow up if $this was NULL) + (In that case, we're actually creating a closure which can't be called without apply) */ + } else if ((func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) == 0 && func->type == ZEND_USER_FUNCTION) { + closure->func.common.fn_flags |= ZEND_ACC_ALLOW_STATIC_EXPLICIT; } } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index f86a41ae2b..1b2bb7309e 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -183,6 +183,9 @@ typedef struct _zend_try_catch_element { /* method flag (bc only), any method that has this flag can be used statically and non statically. */ #define ZEND_ACC_ALLOW_STATIC 0x10000 +/* method flag (for explicitly unbound scoped closures which shouldn't warn) */ +#define ZEND_ACC_ALLOW_STATIC_EXPLICIT 0x20000000 + /* shadow of parent's private method/property */ #define ZEND_ACC_SHADOW 0x20000 diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d882086692..5b2af033f8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1929,7 +1929,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - } else { + } else if (!(fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC_EXPLICIT)){ /* FIXME: output identifiers properly */ /* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */ zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically", fbc->common.scope->name, fbc->common.function_name); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 81aeb43295..27fa59737f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -509,7 +509,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } - } else { + } else if (!(fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC_EXPLICIT)){ /* FIXME: output identifiers properly */ /* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */ zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically", fbc->common.scope->name, fbc->common.function_name); |