diff --git a/Zend/tests/bug37632.phpt b/Zend/tests/bug37632.phpt new file mode 100755 index 00000000000..fb72f893457 --- /dev/null +++ b/Zend/tests/bug37632.phpt @@ -0,0 +1,135 @@ +--TEST-- +Bug #37632 (Protected method access problem) +--FILE-- +test(); + } +} + +class C1 extends A1 +{ + protected function test() + { + echo __METHOD__ . "\n"; + } +} + +$b = new B1; +$b->doTest(new C1); + +class A2 +{ + static protected function test() + { + echo __METHOD__ . "\n"; + } +} + +class B2 extends A2 +{ + static public function doTest(A2 $obj) + { + echo __METHOD__ . "\n"; + $obj->test(); + } +} + +class C2 extends A2 +{ + static protected function test() + { + echo __METHOD__ . "\n"; + } +} + +B2::doTest(new C2); + +/* Right now Ctor's cannot be made protected when defined in a ctor. That is + * we cannot decrease visibility. + * + +interface Ctor +{ + function __construct($x); +} + +class A3 implements Ctor +{ + protected function __construct() + { + echo __METHOD__ . "\n"; + } +} + +class B3 extends A3 +{ + static public function doTest() + { + echo __METHOD__ . "\n"; + new C3; + } +} + +class C3 extends A3 +{ + protected function __construct() + { + echo __METHOD__ . "\n"; + } +} + +B3::doTest(); + +*/ + +class A4 +{ + protected function __construct() + { + echo __METHOD__ . "\n"; + } +} + +class B4 extends A4 +{ + static public function doTest() + { + echo __METHOD__ . "\n"; + new C4; + } +} + +class C4 extends A4 +{ + protected function __construct() + { + echo __METHOD__ . "\n"; + } +} + +B4::doTest(); + +?> +===DONE=== +--EXPECTF-- +B1::doTest +C1::test +B2::doTest +C2::test +B4::doTest + +Fatal error: Call to protected C4::__construct() from context 'B4' in %sbug37632.php on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8eb69d8f113..cf2c7fe3d0d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2028,8 +2028,9 @@ static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_f if (parent_flags & ZEND_ACC_ABSTRACT) { child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT; child->common.prototype = parent; - } else { - child->common.prototype = parent->common.prototype; + } else if (!(parent->common.fn_flags & ZEND_ACC_CTOR) || (parent->common.prototype && parent->common.prototype->common.scope->ce_flags && ZEND_ACC_INTERFACE)) { + /* ctors only have a prototype if it comes from an interface */ + child->common.prototype = parent->common.prototype ? parent->common.prototype : parent; } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index bdcebd3d43e..57fdff3674d 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -724,6 +724,12 @@ ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) } +static inline zend_class_entry * zend_get_function_root_class(zend_function *fbc) +{ + return fbc->common.prototype ? fbc->common.prototype->common.scope : fbc->common.scope; +} + + static union _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) { zend_object *zobj; @@ -784,7 +790,7 @@ static union _zend_function *zend_std_get_method(zval **object_ptr, char *method } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { /* Ensure that if we're calling a protected function, we're allowed to do so. */ - if (!zend_check_protected(fbc->common.scope, EG(scope))) { + if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) { zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : ""); } } @@ -822,7 +828,7 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *f } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { /* Ensure that if we're calling a protected function, we're allowed to do so. */ - if (!zend_check_protected(EG(scope), fbc->common.scope)) { + if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) { zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), function_name_strval, EG(scope) ? EG(scope)->name : ""); } } @@ -898,7 +904,7 @@ static union _zend_function *zend_std_get_constructor(zval *object TSRMLS_DC) } else if ((constructor->common.fn_flags & ZEND_ACC_PROTECTED)) { /* Ensure that if we're calling a protected function, we're allowed to do so. */ - if (!zend_check_protected(constructor->common.scope, EG(scope))) { + if (!zend_check_protected(zend_get_function_root_class(constructor), EG(scope))) { zend_error(E_ERROR, "Call to protected %s::%s() from context '%s'", constructor->common.scope->name, constructor->common.function_name, EG(scope) ? EG(scope)->name : ""); } }