On Wed, 2006-03-01 at 17:37 +0300, Dmitry Stogov wrote:
> 1) I would very like to see some real example where "static" is necessary?
> 
> 2) "static" is really bad name. I suggest "caller", Marcus thought about
> "class".
> 
> 3) I COMPLETELY DISAGREE TO ADD RUNTIME DATA INTO
> zend_function/zend_op_array.
> We can try to store "caller_scope" in execute_data.
> 
> Thanks. Dmitry.
> 

Rewrote the patch to use execute_data to store caller_scope. I have not
renamed any functions or keywords (still using static::) as I did not
see any consensus on that yet.

Please review the implementation of caller_scope and I can take care of
the naming issues when names are decided on.

patch attached and also available at
http://test.ft11.net/_mlively/late-static-binding.patch

It would be nice if the patch in PAT could be changed to reference this
new version if the appropriate people have time.
diff -Naur php-src/tests/classes/caller_001.phpt php-new/tests/classes/caller_001.phpt
--- php-src/tests/classes/caller_001.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_001.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,61 @@
+--TEST--
+ZE2 Late Static Binding in a static function
+--FILE--
+<?php
+
+class TestClass {
+	protected static $staticVar = 'TestClassStatic';
+	const CLASS_CONST = 'TestClassConst';
+
+	protected static function staticFunction() {
+		return 'TestClassFunction';
+	}
+	
+	public static function testStaticVar() {
+		return static::$staticVar;
+	}
+
+	public static function testClassConst() {
+		return static::CLASS_CONST;
+	}
+
+	public static function testStaticFunction() {
+		return static::staticFunction();
+	}
+}
+
+class ChildClass1 extends TestClass {
+	protected static $staticVar = 'ChildClassStatic';
+	const CLASS_CONST = 'ChildClassConst';
+
+	protected static function staticFunction() {
+		return 'ChildClassFunction';
+	}
+}
+
+class ChildClass2 extends TestClass {}
+
+echo TestClass::testStaticVar() . "\n";
+echo TestClass::testClassConst() . "\n";
+echo TestClass::testStaticFunction() . "\n";
+
+echo ChildClass1::testStaticVar() . "\n";
+echo ChildClass1::testClassConst() . "\n";
+echo ChildClass1::testStaticFunction() . "\n";
+
+echo ChildClass2::testStaticVar() . "\n";
+echo ChildClass2::testClassConst() . "\n";
+echo ChildClass2::testStaticFunction() . "\n";
+?>
+==DONE==
+--EXPECTF--
+TestClassStatic
+TestClassConst
+TestClassFunction
+ChildClassStatic
+ChildClassConst
+ChildClassFunction
+TestClassStatic
+TestClassConst
+TestClassFunction
+==DONE==
diff -Naur php-src/tests/classes/caller_002.phpt php-new/tests/classes/caller_002.phpt
--- php-src/tests/classes/caller_002.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_002.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,66 @@
+--TEST--
+ZE2 Late Static Binding in an instance function
+--FILE--
+<?php
+
+class TestClass {
+	protected static $staticVar = 'TestClassStatic';
+	const CLASS_CONST = 'TestClassConst';
+
+	protected static function staticFunction() {
+		return 'TestClassFunction';
+	}
+	
+	public function testStaticVar() {
+		return static::$staticVar;
+	}
+
+	public function testClassConst() {
+		return static::CLASS_CONST;
+	}
+
+	public function testStaticFunction() {
+		return static::staticFunction();
+	}
+}
+
+class ChildClass1 extends TestClass {
+	protected static $staticVar = 'ChildClassStatic';
+	const CLASS_CONST = 'ChildClassConst';
+
+	protected static function staticFunction() {
+		return 'ChildClassFunction';
+	}
+}
+
+class ChildClass2 extends TestClass {}
+
+$testClass = new TestClass();
+$childClass1 = new ChildClass1();
+$childClass2 = new ChildClass2();
+
+
+echo $testClass->testStaticVar() . "\n";
+echo $testClass->testClassConst() . "\n";
+echo $testClass->testStaticFunction() . "\n";
+
+echo $childClass1->testStaticVar() . "\n";
+echo $childClass1->testClassConst() . "\n";
+echo $childClass1->testStaticFunction() . "\n";
+
+echo $childClass2->testStaticVar() . "\n";
+echo $childClass2->testClassConst() . "\n";
+echo $childClass2->testStaticFunction() . "\n";
+?>
+==DONE==
+--EXPECTF--
+TestClassStatic
+TestClassConst
+TestClassFunction
+ChildClassStatic
+ChildClassConst
+ChildClassFunction
+TestClassStatic
+TestClassConst
+TestClassFunction
+==DONE==
diff -Naur php-src/tests/classes/caller_003.phpt php-new/tests/classes/caller_003.phpt
--- php-src/tests/classes/caller_003.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_003.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,24 @@
+--TEST--
+ZE2 creating a new class with 'static'
+--FILE--
+<?php
+
+class TestClass {
+	public static function createInstance() {
+		return new static();
+	}
+}
+
+class ChildClass extends TestClass {}
+
+$testClass = TestClass::createInstance();
+$childClass = ChildClass::createInstance();
+
+echo get_class($testClass) . "\n";
+echo get_class($childClass) . "\n";
+?>
+==DONE==
+--EXPECTF--
+TestClass
+ChildClass
+==DONE==
diff -Naur php-src/tests/classes/caller_004.phpt php-new/tests/classes/caller_004.phpt
--- php-src/tests/classes/caller_004.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_004.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,21 @@
+--TEST--
+ZE2 testing get_caller_class()
+--FILE--
+<?php
+
+class TestClass {
+	public static function getClassName() {
+		return get_caller_class();
+	}
+}
+
+class ChildClass extends TestClass {}
+
+echo TestClass::getClassName() . "\n";
+echo ChildClass::getClassName() . "\n";
+?>
+==DONE==
+--EXPECTF--
+TestClass
+ChildClass
+==DONE==
diff -Naur php-src/tests/classes/caller_005.phpt php-new/tests/classes/caller_005.phpt
--- php-src/tests/classes/caller_005.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_005.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,51 @@
+--TEST--
+ZE2 stacking static callers
+--FILE--
+<?php
+
+class TestA {
+	public static function test() {
+		echo get_class(new static()) . "\n";
+		TestB::test();
+		echo get_class(new static()) . "\n";
+		TestC::test();
+		echo get_class(new static()) . "\n";
+		TestBB::test();
+		echo get_class(new static()) . "\n";
+	}
+}
+
+class TestB {
+	public static function test() {
+		echo get_class(new static()) . "\n";
+		TestC::test();
+		echo get_class(new static()) . "\n";
+	}
+}
+
+class TestC {
+	public static function test() {
+		echo get_class(new static()) . "\n";
+	}
+}
+
+class TestBB extends TestB {
+}
+
+TestA::test();
+
+?>
+==DONE==
+--EXPECTF--
+TestA
+TestB
+TestC
+TestB
+TestA
+TestC
+TestA
+TestBB
+TestC
+TestBB
+TestA
+==DONE==
diff -Naur php-src/tests/classes/caller_006.phpt php-new/tests/classes/caller_006.phpt
--- php-src/tests/classes/caller_006.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_006.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,12 @@
+--TEST--
+ZE2 ensuring extending 'static' is not allowed
+--FILE--
+<?php
+
+class Foo extends static {
+}
+
+?>
+==DONE==
+--EXPECTF--
+Fatal error: Cannot use 'static' as class name as it is reserved in %s on line %d
diff -Naur php-src/tests/classes/caller_007.phpt php-new/tests/classes/caller_007.phpt
--- php-src/tests/classes/caller_007.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_007.phpt	2006-03-06 11:43:51.000000000 -0800
@@ -0,0 +1,12 @@
+--TEST--
+ZE2 ensuring implementing 'static' is not allowed
+--FILE--
+<?php
+
+class Foo implements static {
+}
+
+?>
+==DONE==
+--EXPECTF--
+Fatal error: Cannot use 'static' as interface name as it is reserved in %s on line %d
diff -Naur php-src/tests/classes/caller_008.phpt php-new/tests/classes/caller_008.phpt
--- php-src/tests/classes/caller_008.phpt	1969-12-31 16:00:00.000000000 -0800
+++ php-new/tests/classes/caller_008.phpt	2006-03-08 10:26:41.000000000 -0800
@@ -0,0 +1,38 @@
+--TEST--
+ZE2 using static:: in functions called by non execute() calls and constructors.
+--FILE--
+<?php
+
+class Foo {
+	protected static $className = 'Foo';
+	public static function bar() {
+		echo static::$className . "::bar\n";
+	}
+	public function __construct() {
+		echo static::$className . "::__construct\n";
+	}
+	public function __destruct() {
+		echo static::$className . "::__destruct\n";
+	}
+}
+
+class FooChild extends Foo {
+	protected static $className = 'FooChild';
+}
+
+register_shutdown_function(array('Foo', 'bar'));
+register_shutdown_function(array('FooChild', 'bar'));
+
+$foo = new Foo();
+$fooChild = new FooChild();
+unset($foo);
+unset($fooChild);
+
+?>
+--EXPECTF--
+Foo::__construct
+FooChild::__construct
+Foo::__destruct
+FooChild::__destruct
+Foo::bar
+FooChild::bar
diff -Naur php-src/ZendEngine2/zend_builtin_functions.c php-new/ZendEngine2/zend_builtin_functions.c
--- php-src/ZendEngine2/zend_builtin_functions.c	2006-03-07 00:33:46.000000000 -0800
+++ php-new/ZendEngine2/zend_builtin_functions.c	2006-03-06 11:43:51.000000000 -0800
@@ -17,7 +17,7 @@
    +----------------------------------------------------------------------+
 */
 
-/* $Id: zend_builtin_functions.c,v 1.310 2006/03/07 08:33:46 andrei Exp $ */
+/* $Id: zend_builtin_functions.c,v 1.307 2006/03/03 10:09:35 dmitry Exp $ */
 
 #include "zend.h"
 #include "zend_API.h"
@@ -41,6 +41,7 @@
 static ZEND_FUNCTION(define);
 static ZEND_FUNCTION(defined);
 static ZEND_FUNCTION(get_class);
+static ZEND_FUNCTION(get_caller_class);
 static ZEND_FUNCTION(get_parent_class);
 static ZEND_FUNCTION(method_exists);
 static ZEND_FUNCTION(property_exists);
@@ -101,6 +102,7 @@
 	ZEND_FE(define,				NULL)
 	ZEND_FE(defined,			NULL)
 	ZEND_FE(get_class,			NULL)
+	ZEND_FE(get_caller_class, 	NULL)
 	ZEND_FE(get_parent_class,	NULL)
 	ZEND_FE(method_exists,		NULL)
 	ZEND_FE(property_exists,	NULL)
@@ -592,6 +594,26 @@
 }
 /* }}} */
 
+/* {{{ proto string get_caller_class() U
+   Retrieves the class that was used to call a static function */
+ZEND_FUNCTION(get_caller_class)
+{
+	zend_execute_data *ptr;
+	if (!ZEND_NUM_ARGS()) {
+		ptr = EG(current_execute_data);
+		ptr = ptr->prev_execute_data;
+		if (ptr && ptr->caller_scope) {
+			RETURN_TEXTL(ptr->caller_scope->name, 
+				ptr->caller_scope->name_length, 1);
+		} else {
+			zend_error(E_ERROR, "get_caller_class() called from outside a class");
+		}
+	} else {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+}
+/* }}} */
+
 
 /* {{{ proto string get_parent_class([mixed object]) U
    Retrieves the parent class name for object or class or current scope. */
diff -Naur php-src/ZendEngine2/zend_compile.c php-new/ZendEngine2/zend_compile.c
--- php-src/ZendEngine2/zend_compile.c	2006-03-07 00:43:21.000000000 -0800
+++ php-new/ZendEngine2/zend_compile.c	2006-03-06 11:43:51.000000000 -0800
@@ -1514,6 +1514,7 @@
 		switch (fetch_type) {
 			case ZEND_FETCH_CLASS_SELF:
 			case ZEND_FETCH_CLASS_PARENT:
+			case ZEND_FETCH_CLASS_STATIC:
 				SET_UNUSED(opline->op2);
 				opline->extended_value = fetch_type;
 				zval_dtor(&class_name->u.constant);
@@ -2857,6 +2858,9 @@
 			case ZEND_FETCH_CLASS_PARENT:
 				zend_error(E_COMPILE_ERROR, "Cannot use 'parent' as class name as it is reserved");
 				break;
+			case ZEND_FETCH_CLASS_STATIC:
+				zend_error(E_COMPILE_ERROR, "Cannot use 'static' as class name as it is reserved");
+				break;
 			default:
 				break;
 		}
@@ -2966,6 +2970,9 @@
 		case ZEND_FETCH_CLASS_PARENT:
 			zend_error(E_COMPILE_ERROR, "Cannot use 'parent' as interface name as it is reserved");
 			break;
+		case ZEND_FETCH_CLASS_STATIC:
+			zend_error(E_COMPILE_ERROR, "Cannot use 'static' as interface name as it is reserved");
+			break;
 		default:
 			if (CG(active_op_array)->last > 0) {
 				opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
@@ -4285,6 +4292,9 @@
 	} else if ((class_name_len == sizeof("parent")-1) &&
 	    ZEND_U_EQUAL(type, class_name, class_name_len, "parent", sizeof("parent")-1)) {
 		return ZEND_FETCH_CLASS_PARENT;
+	} else if ((class_name_len == sizeof("static")-1) &&
+	    ZEND_U_EQUAL(type, class_name, class_name_len, "static", sizeof("static")-1)) {
+		return ZEND_FETCH_CLASS_STATIC;
 	} else {
 		return ZEND_FETCH_CLASS_DEFAULT;
 	}
diff -Naur php-src/ZendEngine2/zend_compile.h php-new/ZendEngine2/zend_compile.h
--- php-src/ZendEngine2/zend_compile.h	2006-03-07 00:43:21.000000000 -0800
+++ php-new/ZendEngine2/zend_compile.h	2006-03-06 11:43:51.000000000 -0800
@@ -291,6 +292,7 @@
 	struct _zend_op *opline;
 	zend_function_state function_state;
 	zend_function *fbc; /* Function Being Called */
+	zend_class_entry *caller_scope; /* Scope of the calling class */
 	zend_op_array *op_array;
 	zval *object;
 	union _temp_variable *Ts;
@@ -602,6 +603,7 @@
 #define ZEND_FETCH_CLASS_GLOBAL		4
 #define ZEND_FETCH_CLASS_AUTO		5
 #define ZEND_FETCH_CLASS_INTERFACE	6
+#define ZEND_FETCH_CLASS_STATIC		7
 #define ZEND_FETCH_CLASS_NO_AUTOLOAD 0x80
 
 /* variable parsing type (compile-time) */
diff -Naur php-src/ZendEngine2/zend_execute_API.c php-new/ZendEngine2/zend_execute_API.c
--- php-src/ZendEngine2/zend_execute_API.c	2006-03-03 15:20:29.000000000 -0800
+++ php-new/ZendEngine2/zend_execute_API.c	2006-03-08 10:20:30.000000000 -0800
@@ -714,6 +714,16 @@
 					found = (*ce != NULL?SUCCESS:FAILURE);
 					fci->object_pp = EG(This)?&EG(This):NULL;
 					EX(object) = EG(This);
+				} else if (EG(current_execute_data) && EG(current_execute_data)->prev_execute_data &&
+				           Z_UNILEN_PP(fci->object_pp) == sizeof("static")-1 &&
+				           ZEND_U_EQUAL(Z_TYPE_PP(fci->object_pp), Z_UNIVAL_PP(fci->object_pp), Z_UNILEN_PP(fci->object_pp), "static", sizeof("static")-1)) {
+
+					if (!EG(current_execute_data)->prev_execute_data->caller_scope) {
+						zend_error(E_ERROR, "Cannot access static:: when no called scope is active");
+					}
+					ce = &(EG(current_execute_data)->prev_execute_data->caller_scope);
+					found = (*ce != NULL?SUCCESS:FAILURE);
+					fci->object_pp = EG(This)?&EG(This):NULL;
 				} else {
 					zend_class_entry *scope;
 					scope = EG(active_op_array) ? EG(active_op_array)->scope : NULL;
@@ -782,6 +792,9 @@
 			} else if (calling_scope && clen == sizeof("parent") - 1 && 
 			    ZEND_U_EQUAL(Z_TYPE_P(fci->function_name), lcname, clen, "parent", sizeof("parent")-1)) {
 				ce_child = EG(active_op_array) && EG(active_op_array)->scope ? EG(scope)->parent : NULL;
+			} else if (calling_scope && clen == sizeof("static") - 1 && 
+			    ZEND_U_EQUAL(Z_TYPE_P(fci->function_name), lcname, clen, "static", sizeof("static")-1)) {
+				ce_child = EG(current_execute_data) && EG(current_execute_data)->prev_execute_data && EG(current_execute_data)->prev_execute_data->caller_scope ? EG(current_execute_data)->prev_execute_data->caller_scope : NULL;
 			} else if (zend_u_lookup_class(Z_TYPE_P(fci->function_name), Z_UNIVAL_P(fci->function_name), clen, &pce TSRMLS_CC) == SUCCESS) {
 				ce_child = *pce;
 			}
@@ -870,6 +883,7 @@
 		fci->object_pp = fci_cache->object_pp;
 		EX(object) = fci->object_pp ? *fci->object_pp : NULL;
 	}
+	EX(caller_scope) = calling_scope;
 
 	if (EX(function_state).function->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) {
 		if (EX(function_state).function->common.fn_flags & ZEND_ACC_ABSTRACT) {
@@ -1530,6 +1544,11 @@
 				zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent");
 			}
 			return EG(scope)->parent;
+		case ZEND_FETCH_CLASS_STATIC:
+			if (!EG(current_execute_data)->prev_execute_data || !EG(current_execute_data)->prev_execute_data->caller_scope) {
+				zend_error(E_ERROR, "Cannot access static:: when no called scope is active");
+			}
+			return EG(current_execute_data)->prev_execute_data->caller_scope;
 		case ZEND_FETCH_CLASS_AUTO: {
 				fetch_type = zend_get_class_fetch_type(type, class_name, class_name_len);
 				if (fetch_type!=ZEND_FETCH_CLASS_DEFAULT) {
diff -Naur php-src/ZendEngine2/zend_language_parser.y php-new/ZendEngine2/zend_language_parser.y
--- php-src/ZendEngine2/zend_language_parser.y	2006-03-07 00:43:21.000000000 -0800
+++ php-new/ZendEngine2/zend_language_parser.y	2006-03-06 11:43:51.000000000 -0800
@@ -642,10 +642,12 @@
 
 fully_qualified_class_name:
 		T_STRING { zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
+	|	T_STATIC { znode static_string;  static_string.op_type = IS_CONST; static_string.u.constant.type = IS_STRING; static_string.u.constant.value.str.val = (char *)estrndup("static", 6); static_string.u.constant.value.str.len = 6; zend_do_fetch_class(&$$, &static_string TSRMLS_CC); }
 ;
 
 class_name_reference:
 		T_STRING				{ zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
+	|	T_STATIC				{ znode static_string;  static_string.op_type = IS_CONST; static_string.u.constant.type = IS_STRING; static_string.u.constant.value.str.val = (char *)estrndup("static", 6); static_string.u.constant.value.str.len = 6; zend_do_fetch_class(&$$, &static_string TSRMLS_CC); }
 	|	dynamic_class_name_reference	{ zend_do_end_variable_parse(BP_VAR_R, 0 TSRMLS_CC); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
 ;
 
diff -Naur php-src/ZendEngine2/zend_vm_def.h php-new/ZendEngine2/zend_vm_def.h
--- php-src/ZendEngine2/zend_vm_def.h	2006-03-01 08:14:37.000000000 -0800
+++ php-new/ZendEngine2/zend_vm_def.h	2006-03-08 10:29:36.000000000 -0800
@@ -1660,6 +1660,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1, free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -1675,6 +1677,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -1686,6 +1689,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -1750,6 +1755,8 @@
 		EX(fbc) = ce->constructor;
 	}
 
+	EX(caller_scope) = ce;
+
 	if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
 		EX(object) = NULL;
 	} else {
@@ -2438,6 +2445,7 @@
 	zend_op *opline = EX(opline);
 	zval *object_zval;
 	zend_function *constructor;
+	zend_object *zobj;
 
 	if (EX_T(opline->op1.u.var).class_entry->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
 		char *class_type;
@@ -2474,6 +2482,9 @@
 		EX(object) = object_zval;
 		EX(fbc) = constructor;
 
+		zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
+		EX(caller_scope) = zobj->ce;
+
 		ZEND_VM_NEXT_OPCODE();
 	}
 }
diff -Naur php-src/ZendEngine2/zend_vm_execute.skl php-new/ZendEngine2/zend_vm_execute.skl
--- php-src/ZendEngine2/zend_vm_execute.skl	2006-02-21 00:00:39.000000000 -0800
+++ php-new/ZendEngine2/zend_vm_execute.skl	2006-03-06 11:43:52.000000000 -0800
@@ -13,6 +13,7 @@
 
 	/* Initialize execute_data */
 	EX(fbc) = NULL;
+	EX(caller_scope) = NULL;
 	EX(object) = NULL;
 	EX(old_error_reporting) = NULL;
 	if (op_array->T < TEMP_VAR_STACK_LIMIT) {
diff -Naur php-src/ZendEngine2/zend_vm_execute.h php-new/ZendEngine2/zend_vm_execute.h
--- php-src/ZendEngine2/zend_vm_execute.h	2006-03-03 08:47:55.000000000 -0800
+++ php-new/ZendEngine2/zend_vm_execute.h	2006-03-08 10:29:50.000000000 -0800
@@ -42,6 +42,7 @@
 
 	/* Initialize execute_data */
 	EX(fbc) = NULL;
+	EX(caller_scope) = NULL;
 	EX(object) = NULL;
 	EX(old_error_reporting) = NULL;
 	if (op_array->T < TEMP_VAR_STACK_LIMIT) {
@@ -400,6 +401,7 @@
 	zend_op *opline = EX(opline);
 	zval *object_zval;
 	zend_function *constructor;
+	zend_object *zobj;
 
 	if (EX_T(opline->op1.u.var).class_entry->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
 		char *class_type;
@@ -436,6 +438,9 @@
 		EX(object) = object_zval;
 		EX(fbc) = constructor;
 
+		zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
+		EX(caller_scope) = zobj->ce;
+
 		ZEND_VM_NEXT_OPCODE();
 	}
 }
@@ -684,6 +689,8 @@
 		EX(fbc) = ce->constructor;
 	}
 
+	EX(caller_scope) = ce;
+
 	if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
 		EX(object) = NULL;
 	} else {
@@ -871,6 +878,8 @@
 		EX(fbc) = ce->constructor;
 	}
 
+	EX(caller_scope) = ce;
+
 	if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
 		EX(object) = NULL;
 	} else {
@@ -995,6 +1004,8 @@
 		EX(fbc) = ce->constructor;
 	}
 
+	EX(caller_scope) = ce;
+
 	if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
 		EX(object) = NULL;
 	} else {
@@ -1118,6 +1129,8 @@
 		EX(fbc) = ce->constructor;
 	}
 
+	EX(caller_scope) = ce;
+
 	if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
 		EX(object) = NULL;
 	} else {
@@ -1199,6 +1212,8 @@
 		EX(fbc) = ce->constructor;
 	}
 
+	EX(caller_scope) = ce;
+
 	if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
 		EX(object) = NULL;
 	} else {
@@ -5046,6 +5061,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -5061,6 +5078,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_tmp(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -5072,6 +5090,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -5480,6 +5500,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1, free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -5495,6 +5517,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_tmp(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -5506,6 +5529,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -5916,6 +5941,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1, free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -5931,6 +5958,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_tmp(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -5942,6 +5970,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -6433,6 +6463,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -6448,6 +6480,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_tmp(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -6459,6 +6492,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -8992,6 +9027,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -9007,6 +9044,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -9018,6 +9056,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -10473,6 +10513,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1, free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -10488,6 +10530,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -10499,6 +10542,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -11994,6 +12039,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1, free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -12009,6 +12056,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -12020,6 +12068,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -13920,6 +13970,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op1;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -13935,6 +13987,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -13946,6 +13999,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -15121,6 +15176,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -15136,6 +15193,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_obj_zval_ptr_unused(TSRMLS_C);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -15147,6 +15205,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -16257,6 +16317,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -16272,6 +16334,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_obj_zval_ptr_unused(TSRMLS_C);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -16283,6 +16346,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -17351,6 +17416,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -17366,6 +17433,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_obj_zval_ptr_unused(TSRMLS_C);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -17377,6 +17445,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -18810,6 +18880,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -18825,6 +18897,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_obj_zval_ptr_unused(TSRMLS_C);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -18836,6 +18909,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -21474,6 +21549,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -21489,6 +21566,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -21500,6 +21578,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -22947,6 +23027,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -22962,6 +23044,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -22973,6 +23056,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -24459,6 +24544,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 	zend_free_op free_op2;
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -24474,6 +24561,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -24485,6 +24573,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}
@@ -26375,6 +26465,8 @@
 	zstr function_name_strval;
 	int function_name_strlen;
 
+	zend_object *zobj;
+
 	/* FIXME: type is default */
 	zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING;
 
@@ -26390,6 +26482,7 @@
 	function_name_strlen = Z_UNILEN_P(function_name);
 
 	EX(object) = _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
+	zobj = zend_objects_get_address(EX(object) TSRMLS_CC);
 
 	if (EX(object) && Z_TYPE_P(EX(object)) == IS_OBJECT) {
 		if (Z_OBJ_HT_P(EX(object))->get_method == NULL) {
@@ -26401,6 +26494,8 @@
 		if (!EX(fbc)) {
 			zend_error_noreturn(E_ERROR, "Call to undefined method %R::%R()", type, Z_OBJ_CLASS_NAME_P(EX(object)), Z_TYPE_P(function_name), function_name_strval);
 		}
+
+		EX(caller_scope) = zobj->ce;
 	} else {
 		zend_error_noreturn(E_ERROR, "Call to a member function %R() on a non-object", Z_TYPE_P(function_name), function_name_strval);
 	}

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to