diff options
| author | Felipe Pena <felipe@php.net> | 2011-06-06 21:42:05 +0000 |
|---|---|---|
| committer | Felipe Pena <felipe@php.net> | 2011-06-06 21:42:05 +0000 |
| commit | be3889456be0bc848c53c71521a42ecef8a08478 (patch) | |
| tree | 4ee3a99477cca07ea07857a10719b4ee90e33902 | |
| parent | 4737910b69eba83b5ba6dd2d43f88a7bd3461ccf (diff) | |
| download | php-git-be3889456be0bc848c53c71521a42ecef8a08478.tar.gz | |
- Added indirect method call through array variable (FR Bug #47160)
| -rw-r--r-- | Zend/tests/fr47160.phpt | 150 | ||||
| -rw-r--r-- | Zend/zend_vm_def.h | 61 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.h | 244 |
3 files changed, 455 insertions, 0 deletions
diff --git a/Zend/tests/fr47160.phpt b/Zend/tests/fr47160.phpt new file mode 100644 index 0000000000..809e1f1603 --- /dev/null +++ b/Zend/tests/fr47160.phpt @@ -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 diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c25b7a7a74..77d208ce53 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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"); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 63db4987d6..fd726eb880 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1246,6 +1246,67 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE } 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 { zend_error_noreturn(E_ERROR, "Function name must be a string"); } @@ -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"); } @@ -1723,6 +1906,67 @@ static int ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA } 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 { zend_error_noreturn(E_ERROR, "Function name must be a string"); } |
