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 | 66ac89a00be2edab54bb492223c497adb5433f23 (patch) | |
| tree | cfcf04a6d38075004cbf8036fe4f95174c92114f | |
| parent | 32b5f8a1a3552f48d6e91c17b6d9d441c665a44d (diff) | |
| download | php-git-66ac89a00be2edab54bb492223c497adb5433f23.tar.gz | |
- Added indirect method call through array variable (FR Bug #47160)
| -rw-r--r-- | NEWS | 1 | ||||
| -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 | 
4 files changed, 456 insertions, 0 deletions
| @@ -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) 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");  		} | 
