- Added indirect method call through array variable (FR Bug #47160)

This commit is contained in:
Felipe Pena 2011-06-06 21:42:05 +00:00
parent 32b5f8a1a3
commit 66ac89a00b
4 changed files with 456 additions and 0 deletions

1
NEWS
View File

@ -41,6 +41,7 @@ PHP NEWS
. Added support for Traits. (Stefan)
. Added closure $this support back. (Stas)
. Added array dereferencing support. (Felipe)
. Added indirect method call through array. FR #47160. (Felipe)
. Added support for object references in recursive serialize() calls.
FR #36424. (Mike)
. Added http_response_code() function. FR #52555. (Paul Dragoonis, Kalle)

150
Zend/tests/fr47160.phpt Normal file
View File

@ -0,0 +1,150 @@
--TEST--
Calling method from array
--FILE--
<?php
class Hello {
public function world($x) {
echo "Hello, $x\n"; return $this;
}
}
class Hello2 {
static public function world($x) {
echo "Hello, $x\n";
}
}
class Magic {
public function __call($f, $a) {
printf("%s called (%s)!\n", __METHOD__, $f);
}
}
class Magic2 {
public static function __callStatic($f, $a) {
printf("%s called (%s)!\n", __METHOD__, $f);
}
}
class Magic3 {
public static function __callStatic($f, $a) {
printf("%s called (%s)!\n", __METHOD__, $f);
}
public function __call($f, $a) {
printf("%s called (%s)!\n", __METHOD__, $f);
}
}
$f = array('Hello','world');
var_dump($f('you'));
var_dump(call_user_func($f, 'you'));
printf("-----\n");
$h= new Hello;
$f = array($h,'world');
var_dump($f('again'));
var_dump(call_user_func($f, 'again'));
printf("-----\n");
function bar() {
return array(new Hello,'world');
}
$f = bar();
var_dump($f('there'));
var_dump(call_user_func($f, 'there'));
printf("-----\n");
$x = function ($c,$v) { return array($c, $v); };
$c = new Hello;
$m = 'world';
$f = $x($c, $m);
var_dump($f('devs'));
var_dump(call_user_func($f, 'devs'));
printf("-----\n");
$f = array(new Magic, 'foo');
$f();
call_user_func($f);
printf("-----\n");
$f = array('Magic2', 'foo');
$f();
call_user_func($f);
printf("-----\n");
$f = array('Magic3', 'foo');
$f();
call_user_func($f);
printf("-----\n");
$f = array(new Magic3, 'foo');
$f();
call_user_func($f);
printf("-----\n");
$f = array(new Hello2, 'world');
var_dump($f('you'));
var_dump(call_user_func($f, 'you'));
?>
--EXPECTF--
Strict Standards: Non-static method Hello::world() should not be called statically in %s on line %d
Hello, you
Notice: Undefined variable: this in %s on line %d
NULL
Strict Standards: call_user_func() expects parameter 1 to be a valid callback, non-static method Hello::world() should not be called statically in %s on line %d
Hello, you
Notice: Undefined variable: this in %s on line %d
NULL
-----
Hello, again
object(Hello)#1 (0) {
}
Hello, again
object(Hello)#1 (0) {
}
-----
Hello, there
object(Hello)#2 (0) {
}
Hello, there
object(Hello)#2 (0) {
}
-----
Hello, devs
object(Hello)#4 (0) {
}
Hello, devs
object(Hello)#4 (0) {
}
-----
Magic::__call called (foo)!
Magic::__call called (foo)!
-----
Magic2::__callStatic called (foo)!
Magic2::__callStatic called (foo)!
-----
Magic3::__callStatic called (foo)!
Magic3::__callStatic called (foo)!
-----
Magic3::__call called (foo)!
Magic3::__call called (foo)!
-----
Hello, you
NULL
Hello, you
NULL

View File

@ -2382,6 +2382,67 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (OP2_TYPE != IS_CONST &&
EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
zend_class_entry *ce;
zval **method = NULL;
zval **obj = NULL;
zend_hash_index_find(Z_ARRVAL_P(function_name), 0, (void **) &obj);
zend_hash_index_find(Z_ARRVAL_P(function_name), 1, (void **) &method);
if (Z_TYPE_PP(obj) != IS_STRING && Z_TYPE_PP(obj) != IS_OBJECT) {
zend_error_noreturn(E_ERROR, "First array member is not a valid class name or object");
}
if (Z_TYPE_PP(method) != IS_STRING) {
zend_error_noreturn(E_ERROR, "Second array member is not a valid method");
}
if (Z_TYPE_PP(obj) == IS_STRING) {
ce = zend_fetch_class_by_name(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj), NULL, 0 TSRMLS_CC);
if (UNEXPECTED(ce == NULL)) {
zend_error_noreturn(E_ERROR, "Class '%s' not found", Z_STRVAL_PP(obj));
}
EX(called_scope) = ce;
EX(object) = NULL;
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
}
} else {
EX(object) = *obj;
ce = EX(called_scope) = Z_OBJCE_PP(obj);
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
}
}
}
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
}
FREE_OP2();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error_noreturn(E_ERROR, "Function name must be a string");
}

View File

@ -1244,6 +1244,67 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
} else {
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (IS_CONST != IS_CONST &&
EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
zend_class_entry *ce;
zval **method = NULL;
zval **obj = NULL;
zend_hash_index_find(Z_ARRVAL_P(function_name), 0, (void **) &obj);
zend_hash_index_find(Z_ARRVAL_P(function_name), 1, (void **) &method);
if (Z_TYPE_PP(obj) != IS_STRING && Z_TYPE_PP(obj) != IS_OBJECT) {
zend_error_noreturn(E_ERROR, "First array member is not a valid class name or object");
}
if (Z_TYPE_PP(method) != IS_STRING) {
zend_error_noreturn(E_ERROR, "Second array member is not a valid method");
}
if (Z_TYPE_PP(obj) == IS_STRING) {
ce = zend_fetch_class_by_name(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj), NULL, 0 TSRMLS_CC);
if (UNEXPECTED(ce == NULL)) {
zend_error_noreturn(E_ERROR, "Class '%s' not found", Z_STRVAL_PP(obj));
}
EX(called_scope) = ce;
EX(object) = NULL;
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
}
} else {
EX(object) = *obj;
ce = EX(called_scope) = Z_OBJCE_PP(obj);
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
}
}
}
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
@ -1486,6 +1547,67 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (IS_TMP_VAR != IS_CONST &&
EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
zend_class_entry *ce;
zval **method = NULL;
zval **obj = NULL;
zend_hash_index_find(Z_ARRVAL_P(function_name), 0, (void **) &obj);
zend_hash_index_find(Z_ARRVAL_P(function_name), 1, (void **) &method);
if (Z_TYPE_PP(obj) != IS_STRING && Z_TYPE_PP(obj) != IS_OBJECT) {
zend_error_noreturn(E_ERROR, "First array member is not a valid class name or object");
}
if (Z_TYPE_PP(method) != IS_STRING) {
zend_error_noreturn(E_ERROR, "Second array member is not a valid method");
}
if (Z_TYPE_PP(obj) == IS_STRING) {
ce = zend_fetch_class_by_name(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj), NULL, 0 TSRMLS_CC);
if (UNEXPECTED(ce == NULL)) {
zend_error_noreturn(E_ERROR, "Class '%s' not found", Z_STRVAL_PP(obj));
}
EX(called_scope) = ce;
EX(object) = NULL;
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
}
} else {
EX(object) = *obj;
ce = EX(called_scope) = Z_OBJCE_PP(obj);
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
}
}
}
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
}
zval_dtor(free_op2.var);
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error_noreturn(E_ERROR, "Function name must be a string");
}
@ -1588,6 +1710,67 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (IS_VAR != IS_CONST &&
EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
zend_class_entry *ce;
zval **method = NULL;
zval **obj = NULL;
zend_hash_index_find(Z_ARRVAL_P(function_name), 0, (void **) &obj);
zend_hash_index_find(Z_ARRVAL_P(function_name), 1, (void **) &method);
if (Z_TYPE_PP(obj) != IS_STRING && Z_TYPE_PP(obj) != IS_OBJECT) {
zend_error_noreturn(E_ERROR, "First array member is not a valid class name or object");
}
if (Z_TYPE_PP(method) != IS_STRING) {
zend_error_noreturn(E_ERROR, "Second array member is not a valid method");
}
if (Z_TYPE_PP(obj) == IS_STRING) {
ce = zend_fetch_class_by_name(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj), NULL, 0 TSRMLS_CC);
if (UNEXPECTED(ce == NULL)) {
zend_error_noreturn(E_ERROR, "Class '%s' not found", Z_STRVAL_PP(obj));
}
EX(called_scope) = ce;
EX(object) = NULL;
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
}
} else {
EX(object) = *obj;
ce = EX(called_scope) = Z_OBJCE_PP(obj);
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
}
}
}
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
}
if (free_op2.var) {zval_ptr_dtor(&free_op2.var);};
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
zend_error_noreturn(E_ERROR, "Function name must be a string");
}
@ -1721,6 +1904,67 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
} else {
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (IS_CV != IS_CONST &&
EXPECTED(Z_TYPE_P(function_name) == IS_ARRAY) &&
zend_hash_num_elements(Z_ARRVAL_P(function_name)) == 2) {
zend_class_entry *ce;
zval **method = NULL;
zval **obj = NULL;
zend_hash_index_find(Z_ARRVAL_P(function_name), 0, (void **) &obj);
zend_hash_index_find(Z_ARRVAL_P(function_name), 1, (void **) &method);
if (Z_TYPE_PP(obj) != IS_STRING && Z_TYPE_PP(obj) != IS_OBJECT) {
zend_error_noreturn(E_ERROR, "First array member is not a valid class name or object");
}
if (Z_TYPE_PP(method) != IS_STRING) {
zend_error_noreturn(E_ERROR, "Second array member is not a valid method");
}
if (Z_TYPE_PP(obj) == IS_STRING) {
ce = zend_fetch_class_by_name(Z_STRVAL_PP(obj), Z_STRLEN_PP(obj), NULL, 0 TSRMLS_CC);
if (UNEXPECTED(ce == NULL)) {
zend_error_noreturn(E_ERROR, "Class '%s' not found", Z_STRVAL_PP(obj));
}
EX(called_scope) = ce;
EX(object) = NULL;
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
}
} else {
EX(object) = *obj;
ce = EX(called_scope) = Z_OBJCE_PP(obj);
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
}
}
}
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {