felipe                                   Mon, 06 Jun 2011 21:42:05 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=311871

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

Bug: http://bugs.php.net/47160 (Assigned) $foo() syntax is too limited
      
Changed paths:
    U   php/php-src/branches/PHP_5_4/NEWS
    A   php/php-src/branches/PHP_5_4/Zend/tests/fr47160.phpt
    U   php/php-src/branches/PHP_5_4/Zend/zend_vm_def.h
    U   php/php-src/branches/PHP_5_4/Zend/zend_vm_execute.h
    A   php/php-src/trunk/Zend/tests/fr47160.phpt
    U   php/php-src/trunk/Zend/zend_vm_def.h
    U   php/php-src/trunk/Zend/zend_vm_execute.h

Modified: php/php-src/branches/PHP_5_4/NEWS
===================================================================
--- php/php-src/branches/PHP_5_4/NEWS	2011-06-06 21:28:16 UTC (rev 311870)
+++ php/php-src/branches/PHP_5_4/NEWS	2011-06-06 21:42:05 UTC (rev 311871)
@@ -41,6 +41,7 @@
   . 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)

Added: php/php-src/branches/PHP_5_4/Zend/tests/fr47160.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/fr47160.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/Zend/tests/fr47160.phpt	2011-06-06 21:42:05 UTC (rev 311871)
@@ -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

Modified: php/php-src/branches/PHP_5_4/Zend/zend_vm_def.h
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_vm_def.h	2011-06-06 21:28:16 UTC (rev 311870)
+++ php/php-src/branches/PHP_5_4/Zend/zend_vm_def.h	2011-06-06 21:42:05 UTC (rev 311871)
@@ -2382,6 +2382,67 @@
 			}
 			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");
 		}

Modified: php/php-src/branches/PHP_5_4/Zend/zend_vm_execute.h
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_vm_execute.h	2011-06-06 21:28:16 UTC (rev 311870)
+++ php/php-src/branches/PHP_5_4/Zend/zend_vm_execute.h	2011-06-06 21:42:05 UTC (rev 311871)
@@ -1246,6 +1246,67 @@
 			}
 			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 @@
 			}
 			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 @@
 			}
 			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 @@
 			}
 			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");
 		}

Added: php/php-src/trunk/Zend/tests/fr47160.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/fr47160.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/fr47160.phpt	2011-06-06 21:42:05 UTC (rev 311871)
@@ -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

Modified: php/php-src/trunk/Zend/zend_vm_def.h
===================================================================
--- php/php-src/trunk/Zend/zend_vm_def.h	2011-06-06 21:28:16 UTC (rev 311870)
+++ php/php-src/trunk/Zend/zend_vm_def.h	2011-06-06 21:42:05 UTC (rev 311871)
@@ -2382,6 +2382,67 @@
 			}
 			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");
 		}

Modified: php/php-src/trunk/Zend/zend_vm_execute.h
===================================================================
--- php/php-src/trunk/Zend/zend_vm_execute.h	2011-06-06 21:28:16 UTC (rev 311870)
+++ php/php-src/trunk/Zend/zend_vm_execute.h	2011-06-06 21:42:05 UTC (rev 311871)
@@ -1246,6 +1246,67 @@
 			}
 			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 @@
 			}
 			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 @@
 			}
 			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 @@
 			}
 			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");
 		}
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to