Fix static closure error in call_user_func opcode

I'm assuming this is the only error that is_callable() can generate
with retval=1.

This problem manifested after making closures in static methods
not implicitly static, but would also occur when binding any non-static
closure to a scope without a $this.
This commit is contained in:
Nikita Popov 2015-05-08 15:05:59 +02:00
parent 9d4465e480
commit 8f9f21e8d2
3 changed files with 42 additions and 60 deletions

View file

@ -0,0 +1,22 @@
--TEST--
call_user_func() on non-static closure without $this inside a static method
--FILE--
<?php
class A {
public static function exec(callable $c) {
return call_user_func($c);
}
public static function doSomething() {
return self::exec(function(){
return "okay";
});
}
}
var_dump(A::doSomething());
?>
--EXPECT--
string(4) "okay"

View file

@ -3314,9 +3314,6 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
SAVE_OPLINE(); SAVE_OPLINE();
function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); function_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
if (error) {
efree(error);
}
func = fcc.function_handler; func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) { if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */ /* Delay closure destruction until its invocation */
@ -3332,20 +3329,13 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
if (object) { if (object) {
call_info |= ZEND_CALL_RELEASE_THIS; call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */ GC_REFCOUNT(object)++; /* For $this pointer */
} else if (func->common.scope && }
!(func->common.fn_flags & ZEND_ACC_STATIC)) { if (error) {
if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { efree(error);
zend_error(E_DEPRECATED, /* This is the only soft error is_callable() can generate */
zend_error(E_DEPRECATED,
"Non-static method %s::%s() should not be called statically", "Non-static method %s::%s() should not be called statically",
func->common.scope->name->val, func->common.function_name->val); func->common.scope->name->val, func->common.function_name->val);
} else {
zend_error(
E_EXCEPTION | E_ERROR,
"Non-static method %s::%s() cannot be called statically",
func->common.scope->name->val, func->common.function_name->val);
FREE_OP2();
HANDLE_EXCEPTION();
}
} }
} else { } else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);

View file

@ -5649,9 +5649,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
SAVE_OPLINE(); SAVE_OPLINE();
function_name = EX_CONSTANT(opline->op2); function_name = EX_CONSTANT(opline->op2);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
if (error) {
efree(error);
}
func = fcc.function_handler; func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) { if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */ /* Delay closure destruction until its invocation */
@ -5667,20 +5664,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS
if (object) { if (object) {
call_info |= ZEND_CALL_RELEASE_THIS; call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */ GC_REFCOUNT(object)++; /* For $this pointer */
} else if (func->common.scope && }
!(func->common.fn_flags & ZEND_ACC_STATIC)) { if (error) {
if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { efree(error);
zend_error(E_DEPRECATED, /* This is the only soft error is_callable() can generate */
zend_error(E_DEPRECATED,
"Non-static method %s::%s() should not be called statically", "Non-static method %s::%s() should not be called statically",
func->common.scope->name->val, func->common.function_name->val); func->common.scope->name->val, func->common.function_name->val);
} else {
zend_error(
E_EXCEPTION | E_ERROR,
"Non-static method %s::%s() cannot be called statically",
func->common.scope->name->val, func->common.function_name->val);
HANDLE_EXCEPTION();
}
} }
} else { } else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
@ -9296,9 +9286,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
SAVE_OPLINE(); SAVE_OPLINE();
function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
if (error) {
efree(error);
}
func = fcc.function_handler; func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) { if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */ /* Delay closure destruction until its invocation */
@ -9314,20 +9301,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H
if (object) { if (object) {
call_info |= ZEND_CALL_RELEASE_THIS; call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */ GC_REFCOUNT(object)++; /* For $this pointer */
} else if (func->common.scope && }
!(func->common.fn_flags & ZEND_ACC_STATIC)) { if (error) {
if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { efree(error);
zend_error(E_DEPRECATED, /* This is the only soft error is_callable() can generate */
zend_error(E_DEPRECATED,
"Non-static method %s::%s() should not be called statically", "Non-static method %s::%s() should not be called statically",
func->common.scope->name->val, func->common.function_name->val); func->common.scope->name->val, func->common.function_name->val);
} else {
zend_error(
E_EXCEPTION | E_ERROR,
"Non-static method %s::%s() cannot be called statically",
func->common.scope->name->val, func->common.function_name->val);
HANDLE_EXCEPTION();
}
} }
} else { } else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);
@ -11032,9 +11012,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
SAVE_OPLINE(); SAVE_OPLINE();
function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) {
if (error) {
efree(error);
}
func = fcc.function_handler; func = fcc.function_handler;
if (func->common.fn_flags & ZEND_ACC_CLOSURE) { if (func->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */ /* Delay closure destruction until its invocation */
@ -11050,20 +11027,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV
if (object) { if (object) {
call_info |= ZEND_CALL_RELEASE_THIS; call_info |= ZEND_CALL_RELEASE_THIS;
GC_REFCOUNT(object)++; /* For $this pointer */ GC_REFCOUNT(object)++; /* For $this pointer */
} else if (func->common.scope && }
!(func->common.fn_flags & ZEND_ACC_STATIC)) { if (error) {
if (func->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { efree(error);
zend_error(E_DEPRECATED, /* This is the only soft error is_callable() can generate */
zend_error(E_DEPRECATED,
"Non-static method %s::%s() should not be called statically", "Non-static method %s::%s() should not be called statically",
func->common.scope->name->val, func->common.function_name->val); func->common.scope->name->val, func->common.function_name->val);
} else {
zend_error(
E_EXCEPTION | E_ERROR,
"Non-static method %s::%s() cannot be called statically",
func->common.scope->name->val, func->common.function_name->val);
zval_ptr_dtor_nogc(free_op2);
HANDLE_EXCEPTION();
}
} }
} else { } else {
zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error); zend_internal_type_error(EX_USES_STRICT_TYPES(), "%s() expects parameter 1 to be a valid callback, %s", Z_STRVAL_P(EX_CONSTANT(opline->op1)), error);