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

Reply via email to