]> granicus.if.org Git - php/commitdiff
- Added indirect method call through array variable (FR Bug #47160)
authorFelipe Pena <felipe@php.net>
Mon, 6 Jun 2011 21:42:05 +0000 (21:42 +0000)
committerFelipe Pena <felipe@php.net>
Mon, 6 Jun 2011 21:42:05 +0000 (21:42 +0000)
NEWS
Zend/tests/fr47160.phpt [new file with mode: 0644]
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/NEWS b/NEWS
index 42e555de7590e90e184a504d460b666d3f98d5e6..d551e0d59e185c24ae18c9fcf3cb51bc5f99e539 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -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 (file)
index 0000000..809e1f1
--- /dev/null
@@ -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
index c25b7a7a7471eabb583f5ce3de9b6e7ae4b06529..77d208ce53268e192684fb13594a75cb5f963045 100644 (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");
                }
index 63db4987d679fb335a2f556f6ff06fa34486da37..fd726eb880a72ef9d53a342361f85ebc8a2e30a3 100644 (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 {