foo(); // B B B B::foo(); // B B B C::foo(); // C C (Zend outputs: C B B) (Rule 1) D::foo(); // D D (Zend outputs: D B B) (Rule 1) F::foo(); // F F (Zend outputs: F B B) (Rule 1) G::foo(); // G G (Zend outputs: G B B) (Rule 1) H::foo(); // H H (Zend outputs: H B B) (Rule 1) parent::foo(); // A B B self::foo(); // B B B static::foo(); // B B B echo "****************\n"; } public function doBar() { $this->bar(); // B B B::bar(); // B B C::bar(); // C C D::bar(); // D D F::bar(); // F F G::bar(); // G G H::bar(); // H H parent::bar(); // A B self::bar(); // B B static::bar(); // B B echo "****************\n"; } } class C extends B { public function foo() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } public static function bar() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } /** * Zend's implementation of call_user_func() and forward_static_call() has * a few corner cases where its behavior is strictly incorrect. HipHop will * match Zend's behavior except for cases that violate one of the following * invariants: * 1) If the current instance ($this) is non-null, the class of the current * instance must be the same or a descendent of the class enclosing the * current method. The PHP group is in the process of depracating this * behavior. HipHop explicitly lists this in the inconsistencies doc. * 2) If the current instance ($this) is non-null, the class of the current * instance must be the same as the current late bound class (aka the * "called class"). This invariant is stated explicitly in Zend PHP's * documentation: "In non-static contexts, the called class will be the * class of the object instance." * 3) Normal style calls and call_user_func style calls should produce * consistent results aside from any exceptions mentioned in Zend PHP's * documentation. Therefore, "B::foo()" and "call_user_func('B::foo')" * and "call_user_func(array('B', 'foo'))" should all produce consistent * results. * 4) call_user_func() and forward_static_call() must never forward the * caller's late bound class to the callee if the late bound class is * not the same or a descendent of the class enclosing the method. * Zend's implementation of forward_static_call contains a check for * this, implying intent to uphold this invariant. */ public function testFoo1() { echo "############# testFoo1 ##############\n"; B::foo(); // B D D C::foo(); // C D D D::foo(); // D D D F::foo(); // F F (Zend: F D D) (Rule 1) G::foo(); // G G (Zend: G D D) (Rule 1) H::foo(); // H H (Zend: H D D) (Rule 1) parent::foo(); // B D D self::foo(); // C D D static::foo(); // D D D echo "****************\n"; forward_static_call(array('B','foo')); // B D D forward_static_call(array('C','foo')); // C D D forward_static_call(array('D','foo')); // D D D forward_static_call(array('F','foo')); // F F forward_static_call(array('G','foo')); // G G forward_static_call(array('H','foo')); // H H forward_static_call(array('parent','foo')); // B D D forward_static_call(array('self','foo')); // C D D forward_static_call(array('static','foo')); // D D D echo "****************\n"; forward_static_call('B::foo'); // B D D forward_static_call('C::foo'); // C D D forward_static_call('D::foo'); // D D D forward_static_call('F::foo'); // F F forward_static_call('G::foo'); // G G forward_static_call('H::foo'); // H H forward_static_call('parent::foo'); // B D D forward_static_call('self::foo'); // C D D forward_static_call('static::foo'); // D D D echo "****************\n"; forward_static_call(array('B','B::foo')); // B D D forward_static_call(array('B','C::foo')); // warning forward_static_call(array('B','D::foo')); // warning forward_static_call(array('B','F::foo')); // warning forward_static_call(array('B','G::foo')); // warning forward_static_call(array('B','H::foo')); // warning forward_static_call(array('B','parent::foo')); // A D D forward_static_call(array('B','self::foo')); // B D D forward_static_call(array('B','static::foo')); // warning echo "****************\n"; forward_static_call(array('G','B::foo')); // warning forward_static_call(array('G','C::foo')); // warning forward_static_call(array('G','D::foo')); // warning forward_static_call(array('G','F::foo')); // F F forward_static_call(array('G','G::foo')); // G G forward_static_call(array('G','H::foo')); // warning forward_static_call(array('G','parent::foo')); // F F (Zend: F D D) (Rule 1) forward_static_call(array('G','self::foo')); // G G (Zend: G D D) (Rule 1) forward_static_call(array('G','static::foo')); // warning echo "****************\n"; $b = new B; forward_static_call(array($b,'foo')); // B B B (Zend: B D B) (Rule 2) forward_static_call(array($b,'B::foo')); // B B B (Zend: B D B) (Rule 2) forward_static_call(array($b,'C::foo')); // warning forward_static_call(array($b,'D::foo')); // warning forward_static_call(array($b,'F::foo')); // warning forward_static_call(array($b,'G::foo')); // warning forward_static_call(array($b,'H::foo')); // warning forward_static_call(array($b,'parent::foo')); // A B B (Zend: A D B) (Rule2) forward_static_call(array($b,'self::foo')); // B B B (Zend: B D B) (Rule2) forward_static_call(array($b,'static::foo')); // warning echo "****************\n"; $g = new G; forward_static_call(array($g,'foo')); // G G G forward_static_call(array($g,'B::foo')); // warning forward_static_call(array($g,'C::foo')); // warning forward_static_call(array($g,'D::foo')); // warning forward_static_call(array($g,'F::foo')); // F G G forward_static_call(array($g,'G::foo')); // G G G forward_static_call(array($g,'H::foo')); // warning forward_static_call(array($g,'parent::foo')); // F G G forward_static_call(array($g,'self::foo')); // G G G forward_static_call(array($g,'static::foo')); // warning echo "****************\n"; } public static function testFoo2() { echo "############# testFoo2 ##############\n"; B::foo(); // B B C::foo(); // C C D::foo(); // D D F::foo(); // F F G::foo(); // G G H::foo(); // H H parent::foo(); // B D self::foo(); // C D static::foo(); // D D echo "****************\n"; forward_static_call(array('B','foo')); // B D forward_static_call(array('C','foo')); // C D forward_static_call(array('D','foo')); // D D forward_static_call(array('F','foo')); // F F forward_static_call(array('G','foo')); // G G forward_static_call(array('H','foo')); // H H forward_static_call(array('parent','foo')); // B D forward_static_call(array('self','foo')); // C D forward_static_call(array('static','foo')); // D D echo "****************\n"; forward_static_call('B::foo'); // B D forward_static_call('C::foo'); // C D forward_static_call('D::foo'); // D D forward_static_call('F::foo'); // F F forward_static_call('G::foo'); // G G forward_static_call('H::foo'); // H H forward_static_call('parent::foo'); // B D forward_static_call('self::foo'); // C D forward_static_call('static::foo'); // D D echo "****************\n"; forward_static_call(array('B','B::foo')); // B D forward_static_call(array('B','C::foo')); // warning forward_static_call(array('B','D::foo')); // warning forward_static_call(array('B','F::foo')); // warning forward_static_call(array('B','G::foo')); // warning forward_static_call(array('B','H::foo')); // warning forward_static_call(array('B','parent::foo')); // A D forward_static_call(array('B','self::foo')); // B D forward_static_call(array('B','static::foo')); // warning echo "****************\n"; forward_static_call(array('G','B::foo')); // warning forward_static_call(array('G','C::foo')); // warning forward_static_call(array('G','D::foo')); // warning forward_static_call(array('G','F::foo')); // F F forward_static_call(array('G','G::foo')); // G G forward_static_call(array('G','H::foo')); // warning forward_static_call(array('G','parent::foo')); // F F (Zend: F D) (Rule 4) forward_static_call(array('G','self::foo')); // G G (Zend: G D) (Rule 4) forward_static_call(array('G','static::foo')); echo "****************\n"; $b = new B; forward_static_call(array($b,'foo')); // B B B forward_static_call(array($b,'B::foo')); // B B B forward_static_call(array($b,'C::foo')); // warning forward_static_call(array($b,'D::foo')); // warning forward_static_call(array($b,'F::foo')); // warning forward_static_call(array($b,'G::foo')); // warning forward_static_call(array($b,'H::foo')); // warning forward_static_call(array($b,'parent::foo')); // A B B forward_static_call(array($b,'self::foo')); // B B B forward_static_call(array($b,'static::foo')); // warning echo "****************\n"; $g = new G; forward_static_call(array($g,'foo')); // G G G forward_static_call(array($g,'B::foo')); // warning forward_static_call(array($g,'C::foo')); // warning forward_static_call(array($g,'D::foo')); // warning forward_static_call(array($g,'F::foo')); // F G G forward_static_call(array($g,'G::foo')); // G G G forward_static_call(array($g,'H::foo')); // warning forward_static_call(array($g,'parent::foo')); // F G G forward_static_call(array($g,'self::foo')); // G G G forward_static_call(array($g,'static::foo')); // warning echo "****************\n"; } public function testBar1() { echo "############# testBar1 ##############\n"; B::bar(); // B B C::bar(); // C C D::bar(); // D D F::bar(); // F F G::bar(); // G G H::bar(); // H H parent::bar(); // B D self::bar(); // C D static::bar(); // D D echo "****************\n"; forward_static_call(array('B','bar')); // B D forward_static_call(array('C','bar')); // C D forward_static_call(array('D','bar')); // D D forward_static_call(array('F','bar')); // F F forward_static_call(array('G','bar')); // G G forward_static_call(array('H','bar')); // H H forward_static_call(array('parent','bar')); // B D forward_static_call(array('self','bar')); // C D forward_static_call(array('static','bar')); // D D echo "****************\n"; forward_static_call('B::bar'); // B D forward_static_call('C::bar'); // C D forward_static_call('D::bar'); // D D forward_static_call('F::bar'); // F F forward_static_call('G::bar'); // G G forward_static_call('H::bar'); // H H forward_static_call('parent::bar'); // B D forward_static_call('self::bar'); // C D forward_static_call('static::bar'); // D D echo "****************\n"; forward_static_call(array('B','B::bar')); // B D forward_static_call(array('B','C::bar')); // warning forward_static_call(array('B','D::bar')); // warning forward_static_call(array('B','F::bar')); // warning forward_static_call(array('B','G::bar')); // warning forward_static_call(array('B','H::bar')); // warning forward_static_call(array('B','parent::bar')); // A D forward_static_call(array('B','self::bar')); // B D forward_static_call(array('B','static::bar')); // warning echo "****************\n"; forward_static_call(array('G','B::bar')); forward_static_call(array('G','C::bar')); forward_static_call(array('G','D::bar')); forward_static_call(array('G','F::bar')); // F F forward_static_call(array('G','G::bar')); // G G forward_static_call(array('G','H::bar')); forward_static_call(array('G','parent::bar')); // F F (Zend: F D) (Rule 4) forward_static_call(array('G','self::bar')); // G G (Zend: G D) (Rule 4) forward_static_call(array('G','static::bar')); echo "****************\n"; $b = new B; forward_static_call(array($b,'bar')); // B D forward_static_call(array($b,'B::bar')); // B D forward_static_call(array($b,'C::bar')); // warning forward_static_call(array($b,'D::bar')); // warning forward_static_call(array($b,'F::bar')); // warning forward_static_call(array($b,'G::bar')); // warning forward_static_call(array($b,'H::bar')); // warning forward_static_call(array($b,'parent::bar')); // A D forward_static_call(array($b,'self::bar')); // B D forward_static_call(array($b,'static::bar')); // warning echo "****************\n"; $g = new G; forward_static_call(array($g,'bar')); // G G forward_static_call(array($g,'B::bar')); // warning forward_static_call(array($g,'C::bar')); // warning forward_static_call(array($g,'D::bar')); // warning forward_static_call(array($g,'F::bar')); // F G forward_static_call(array($g,'G::bar')); // G G forward_static_call(array($g,'H::bar')); // warning forward_static_call(array($g,'parent::bar')); // F G forward_static_call(array($g,'self::bar')); // G G forward_static_call(array($g,'static::bar')); // warning echo "****************\n"; } public static function testBar2() { echo "############# testBar2 ##############\n"; B::bar(); // B B C::bar(); // C C D::bar(); // D D F::bar(); // F F G::bar(); // G G H::bar(); // H H parent::bar(); // B D self::bar(); // C D static::bar(); // D D echo "****************\n"; forward_static_call(array('B','bar')); // B D forward_static_call(array('C','bar')); // C D forward_static_call(array('D','bar')); // D D forward_static_call(array('F','bar')); // F F forward_static_call(array('G','bar')); // G G forward_static_call(array('H','bar')); // H H forward_static_call(array('parent','bar')); // B D forward_static_call(array('self','bar')); // C D forward_static_call(array('static','bar')); // D D echo "****************\n"; forward_static_call('B::bar'); // B D forward_static_call('C::bar'); // C D forward_static_call('D::bar'); // D D forward_static_call('F::bar'); // F F forward_static_call('G::bar'); // G G forward_static_call('H::bar'); // H H forward_static_call('parent::bar'); // B D forward_static_call('self::bar'); // C D forward_static_call('static::bar'); // D D echo "****************\n"; forward_static_call(array('B','B::bar')); // B D forward_static_call(array('B','C::bar')); // warning forward_static_call(array('B','D::bar')); // warning forward_static_call(array('B','F::bar')); // warning forward_static_call(array('B','G::bar')); // warning forward_static_call(array('B','H::bar')); // warning forward_static_call(array('B','parent::bar')); // A D forward_static_call(array('B','self::bar')); // B D forward_static_call(array('B','static::bar')); // warning echo "****************\n"; forward_static_call(array('G','B::bar')); forward_static_call(array('G','C::bar')); forward_static_call(array('G','D::bar')); forward_static_call(array('G','F::bar')); // F F forward_static_call(array('G','G::bar')); // G G forward_static_call(array('G','H::bar')); forward_static_call(array('G','parent::bar')); // F F (Zend: F D) (Rule 4) forward_static_call(array('G','self::bar')); // G G (Zend: G D) (Rule 4) forward_static_call(array('G','static::bar')); echo "****************\n"; $b = new B; forward_static_call(array($b,'bar')); // B D forward_static_call(array($b,'B::bar')); // B D forward_static_call(array($b,'C::bar')); forward_static_call(array($b,'D::bar')); forward_static_call(array($b,'F::bar')); forward_static_call(array($b,'G::bar')); forward_static_call(array($b,'H::bar')); forward_static_call(array($b,'parent::bar')); // A D forward_static_call(array($b,'self::bar')); // B D forward_static_call(array($b,'static::bar')); echo "****************\n"; $g = new G; forward_static_call(array($g,'bar')); // G G forward_static_call(array($g,'B::bar')); // warning forward_static_call(array($g,'C::bar')); // warning forward_static_call(array($g,'D::bar')); // warning forward_static_call(array($g,'F::bar')); // F G forward_static_call(array($g,'G::bar')); // G G forward_static_call(array($g,'H::bar')); // warning forward_static_call(array($g,'parent::bar')); // F G forward_static_call(array($g,'self::bar')); // G G forward_static_call(array($g,'static::bar')); // warning echo "****************\n"; } } class D extends C { public function foo() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } public static function bar() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } } class F { public function foo() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } public static function bar() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } } class G extends F { public function foo() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } public static function bar() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } public function doFoo() { $this->foo(); // G G G B::foo(); // B B (Zend: B G G) (Rule 1) C::foo(); // C C (Zend: C G G) (Rule 1) D::foo(); // D D (Zend: D G G) (Rule 1) F::foo(); // F G G G::foo(); // G G G H::foo(); // H H (Zend: H G G) (Rule 1) parent::foo(); // F G G self::foo(); // G G G static::foo(); // G G G echo "****************\n"; } public function doBar() { $this->bar(); // G G B::bar(); // B B C::bar(); // C C D::bar(); // D D F::bar(); // F F G::bar(); // G G H::bar(); // H H parent::bar(); // F G self::bar(); // G G static::bar(); // G G echo "****************\n"; } } class H extends G { public function foo() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } public static function bar() { echo __CLASS__ . ' ' . get_called_class() . ' ' . (isset($this) ? get_class($this) : '') . "\n"; } } $d = new D; $d->testFoo1(); D::testFoo2(); $d->testBar1(); D::testBar2(); $b = new B; $g = new G; echo "############# doFoo ##############\n"; $b->doFoo(); $g->doFoo(); echo "############# doBar ##############\n"; $b->doBar(); $g->doBar();