Hi all, The fix to bug 37212 (http://bugs.php.net/bug.php?id=37632) introduced an unusual method accessibility rule. A class can access a protected method declared outside of its own direct class hierarchy if that method has a prototype in a common superclass.
<?php class A { static protected function f() {return 'A::f()';} } class B1 extends A { static protected function f() {return 'B1::f()';} } class B2 extends A { static public function test() {echo B1::f();} } B2::test(); // prints B1::f() ?> This is achieved using by zend_get_function_root_class() when invoking zend_check_protected(), e.g.: zend_check_protected(zend_get_function_root_class(fbc), EG(scope)) Looking at other uses of zend_check_protected() reveals at least 5 cases where this rule is not enforced. They are illustrated below. So is the rule itself incorrect? or should the inconsistent cases be fixed? The examples below were tested on 5.2.5 and the latest 5.3 and 6.0 snaps. 1. The visibility rule does not apply to properties (static or not): <?php class A { protected $p = 'A::$p'; static protected $sp = 'A::$sp'; } class B1 extends A { protected $p = 'B1::$p'; static protected $sp = 'B1::$sp'; } class B2 extends A { static public function test() { $b1 = new B1; echo $b1->p; //Fatal error: Cannot access protected property B1::$p echo B1::$sp; //Fatal error: Cannot access protected property B1::$sp } } B2::test(); ?> 2. It doesn't apply to callbacks either: <?php class A { static protected function f() {return 'A::f()';} } class B1 extends A { static protected function f() {return 'B1::f()';} } class B2 extends A { static public function test() { echo call_user_func('B1::f'); } } B2::test(); // Warning: call_user_func() expects parameter 1 to be a valid callback, cannot access protected method B1::f() ?> 3. is_callable() doesn't know about this visibility rule: <?php class A { static protected function f() {return 'A::f()';} } class B1 extends A { static protected function f() {return 'B1::f()';} } class B2 extends A { static public function test() { var_dump(is_callable('B1::f')); // returns false B1::f(); // works } } B2::test(); ?> 4. The rule does not apply to the clone magic method: <?php class A { protected function f() {return 'A::f()';} protected function __clone() {} } class B1 extends A { protected function f() {return 'B1::f()';} protected function __clone() {} } class B2 extends A { static public function test($obj) { echo $obj->f(); // works clone $obj; // Fatal error: Call to protected B1::__clone() from context 'B2' } } B2::test(new B1); ?> 5. The rule does not apply to destructors: <?php class A { protected function __destruct() {} } class B1 extends A { protected function __destruct() {} } class B2 extends A { static public function test() { $obj = new B1; } // Fatal error: Call to protected B1::__destruct() from context 'B2' } B2::test(); ?> Many thanks, Robin -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php