1
0
mirror of https://github.com/php/php-src.git synced 2026-04-24 16:38:25 +02:00

Fixed unbound scoped closure edge cases and added tests for them

This commit is contained in:
Andrea Faulds
2014-07-30 03:21:44 +01:00
parent f65bdda469
commit 85bf8b4ff1
5 changed files with 41 additions and 6 deletions
+30 -4
View File
@@ -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)
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
+6
View File
@@ -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;
}
}
}
+3
View File
@@ -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
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);