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:
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user