--- /dev/null
+--TEST--
+Closure::call() or Closure::bind() to independent class must fail
+--FILE--
+<?php
+
+class foo {
+ public $var;
+
+ function initClass() {
+ $this->var = __CLASS__;
+ }
+}
+
+class bar {
+ public $var;
+
+ function initClass() {
+ $this->var = __CLASS__;
+ }
+
+ function getVar() {
+ assert($this->var !== null); // ensure initClass was called
+ return $this->var;
+ }
+}
+
+class baz extends bar {
+ public $var;
+
+ function initClass() {
+ $this->var = __CLASS__;
+ }
+}
+
+function callMethodOn($class, $method, $object) {
+ $closure = (new ReflectionMethod($class, $method))->getClosure((new ReflectionClass($class))->newInstanceWithoutConstructor());
+ $closure = $closure->bindTo($object, $class);
+ return $closure();
+}
+
+$baz = new baz;
+
+callMethodOn("baz", "initClass", $baz);
+var_dump($baz->getVar());
+
+callMethodOn("bar", "initClass", $baz);
+var_dump($baz->getVar());
+
+callMethodOn("foo", "initClass", $baz);
+var_dump($baz->getVar());
+
+?>
+--EXPECTF--
+string(3) "baz"
+string(3) "bar"
+
+Warning: Cannot bind function foo::initClass to object of class baz in %s on line %d
+
+Fatal error: Uncaught Error: Using $this when not in object context in %s:%d
+Stack trace:
+#0 %s(%d): initClass()
+#1 %s(%d): callMethodOn('foo', 'initClass', Object(baz))
+#2 {main}
+ thrown in %s on line %d
return;
}
- if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
- /* verify that we aren't binding internal function to a wrong object */
- if ((closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0 &&
- !instanceof_function(Z_OBJCE_P(newthis), closure->func.common.scope)) {
+ if (closure->func.type != ZEND_USER_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_REAL_CLOSURE) == 0) {
+ /* verify that we aren't binding methods to a wrong object */
+ if (closure->func.common.scope == NULL) {
+ zend_error(E_WARNING, "Cannot bind function %s to an object", ZSTR_VAL(closure->func.common.function_name));
+ return;
+ } else if (!instanceof_function(Z_OBJCE_P(newthis), closure->func.common.scope)) {
zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s", ZSTR_VAL(closure->func.common.scope->name), ZSTR_VAL(closure->func.common.function_name), ZSTR_VAL(Z_OBJCE_P(newthis)->name));
return;
}
RETURN_NULL();
}
- closure = (zend_closure *)Z_OBJ_P(zclosure);
+ closure = (zend_closure *) Z_OBJ_P(zclosure);
if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
zend_error(E_WARNING, "Cannot bind an instance to a static closure");
}
zend_string_release(class_name);
}
- if(ce && ce != closure->func.common.scope && ce->type == ZEND_INTERNAL_CLASS) {
+ if (ce && ce != closure->func.common.scope && ce->type == ZEND_INTERNAL_CLASS) {
/* rebinding to internal class is not allowed */
zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s", ZSTR_VAL(ce->name));
return;
called_scope = ce;
}
+ /* verify that we aren't binding methods to a wrong object */
+ if (closure->func.type != ZEND_USER_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_REAL_CLOSURE) == 0) {
+ if (!closure->func.common.scope) {
+ if (ce) {
+ zend_error(E_WARNING, "Cannot bind function %s to an object", ZSTR_VAL(closure->func.common.function_name));
+ return;
+ }
+ } else if (!ce) {
+ zend_error(E_WARNING, "Cannot bind function %s::%s to no class", ZSTR_VAL(closure->func.common.scope->name), ZSTR_VAL(closure->func.common.function_name));
+ return;
+ } else if (!instanceof_function(ce, closure->func.common.scope)) {
+ zend_error(E_WARNING, "Cannot bind function %s::%s to class %s", ZSTR_VAL(closure->func.common.scope->name), ZSTR_VAL(closure->func.common.function_name), ZSTR_VAL(ce->name));
+ return;
+ }
+ }
+
zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
new_closure = (zend_closure *) Z_OBJ_P(return_value);
* and we won't check arguments on internal function. We also set
* ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
invoke->type = ZEND_INTERNAL_FUNCTION;
- invoke->internal_function.fn_flags =
- ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
+ invoke->internal_function.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
- invoke->internal_function.fn_flags |=
- ZEND_ACC_USER_ARG_INFO;
+ invoke->internal_function.fn_flags |= ZEND_ACC_USER_ARG_INFO;
}
invoke->internal_function.handler = ZEND_MN(Closure___invoke);
invoke->internal_function.module = 0;
object_init_ex(res, zend_ce_closure);
- closure = (zend_closure *)Z_OBJ_P(res);
+ closure = (zend_closure *) Z_OBJ_P(res);
memcpy(&closure->func, func, func->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
- closure->func.common.prototype = (zend_function*)closure;
+ closure->func.common.prototype = (zend_function *) closure;
closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
if (closure->func.op_array.refcount) {
(*closure->func.op_array.refcount)++;
}
- } else {
+ }
+ if (closure->func.type != ZEND_USER_FUNCTION || (func->common.fn_flags & ZEND_ACC_REAL_CLOSURE) == 0) {
/* verify that we aren't binding internal function to a wrong scope */
if(func->common.scope != NULL) {
if(scope && !instanceof_function(scope, func->common.scope)) {
!instanceof_function(Z_OBJCE_P(this_ptr), closure->func.common.scope)) {
zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name), ZSTR_VAL(Z_OBJCE_P(this_ptr)->name));
scope = NULL;
- this_ptr = NULL;
}
} else {
/* if it's a free function, we won't set scope & this since they're meaningless */