3

Please go through below given code it is from php manual

<?php
class A {
    private function foo() {
        echo "success!\n";
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
   /* foo() will be copied to B, hence its scope will still be A and
    * the call be successful */
}

class C extends A {
    private function foo() {
        /* original method is replaced; the scope of the new one is C */
    }
}

$b = new B();
$b->test();
$c = new C();
$c->test();   //fails
?> 

can any one explain what is exactly going on here?

How come foo() will be copied to B ?

Poonam Bhatt
  • 10,154
  • 16
  • 53
  • 72
  • `foo()` will not be copied, it's inherited by its parent `A`. I don't understand your concrete problem. Of course it fails, because you made your method `foo` in `C` private. Make it public and it will work. – dan-lee Jun 17 '12 at 15:20
  • how come foo will be inherited? ...since it is private method...this is example from php manual – Poonam Bhatt Jun 17 '12 at 15:22
  • Yeah, it's just not accessible. – dan-lee Jun 17 '12 at 15:22
  • 1
    The [entire page you copied the example from in the Manual explains Late Static Binding](http://www.php.net/manual/en/language.oop5.late-static-bindings.php). Can you please clarify what you are not understanding or how the page is unclear to you? – Gordon Jun 17 '12 at 15:31
  • in above example ...in class B...it is written that "foo() will be copied to B"...that is what confuse me... – Poonam Bhatt Jun 17 '12 at 15:33
  • 1
    @PoonamBhatt see the chapter on http://www.php.net/manual/en/language.oop5.visibility.php – Gordon Jun 17 '12 at 15:37
  • tl;dr. If you declare C::foo as protected, problem goes away – madfriend Jun 17 '12 at 17:15

2 Answers2

4

I remember now why late static binding is useful. Unfortunately, the php.net example is kind of poorly explained. See this (modified) example:

<?php
class A {
    private function foo() {
        echo __CLASS__;
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
    public function foo()
    {
        echo __CLASS__;
    }
}

$b = new B();
$b->test();
?>

If you run the above code, you will notice that it works and you will get AB. Why?

  • It works because test() is a public getter for foo(), so it does not matter if you call test() from an object of type A, or from an object of type B, that inherits from A, because test() will always have access to the private members of the class in which it is defined.
  • In the first case $this->foo(); will always call A::foo() because the binding is done locally, inside A's scope, which is quite undesirable sometimes. See this comment: http://www.php.net/manual/en/language.oop5.late-static-bindings.php#83502
  • In the second case, static::foo(); instructs the interpreter to determine $b's type and see in which class to try and find foo(). In this case, B::foo() is seen as an override method for A::foo(), so, basically, if B::foo() exists, it will be called, otherwise, the interpreter will look for A::foo().
  • Try to mark B::foo() as private and run the example I provided, to see what happens. I think this test and my rants above will clarify the issue for you ;)

Also, I accept any comments to my above points, because I haven't used PHP in quite a while.

Niko
  • 26,516
  • 9
  • 93
  • 110
Mihai Todor
  • 8,014
  • 9
  • 49
  • 86
  • Spoiler for my question: It might be a bit odd at first that it will try to access B::foo() from `A`'s perspective (don't forget that `test()` is defined in `A`), but, otherwise, it breaks encapsulation, so that's why `B::foo()` needs to be public, in order to work as an override method for `A::foo()`. – Mihai Todor Jun 17 '12 at 16:23
  • 1
    The second point in your list is only true for this example, not in general. If A::foo() is declared public or protected, the output would be "BB". This is because private functions cannot be overwritten. – Niko Jun 17 '12 at 17:22
  • Well, yes. I hope my example wasn't suggesting such a fallacy :) By the way, thanks for the edit. – Mihai Todor Jun 17 '12 at 17:51
2

foo isn't copied to B per se (it is inherited but invisible; see Gordon's comment below). B inherits A->foo, which calls A->test. To demonstrate, look at what happens when you echo __CLASS__ from within test and foo (and remove the static::foo() call, which is causing the error):

class A {
    private function foo() {
        echo __CLASS__."->foo\n";
        echo "success!\n";
    }
    public function test() {
        echo __CLASS__."->test\n";
        $this->foo();
    }
}

Output:

A->test
A->foo
success!
A->test
A->foo
success!

This is one of the fundamentals of inheritance as it relates to information hiding/encapsulation. This allows you to do things like this:

class ListOfThings {
    // internal structure (top secret!)
    private $_list = array();

    // add item to list
    public function add($item) {
        $this->_list[] = $item;
    }

    // return number of items in list
    public function count() {
        return count($this->_list);
    }
}

class ListOfStates extends ListOfThings {

    // do we have all 50 states?
    public function allStatesListed() {
        return $this->count() === 50;
    }

    // try to access internal structure of ListOfThings
    public function accessInternalStructure() {
        sort($this->_list);
    }
}

$states = new ListOfStates;
$states->add('ME');
$states->add('NH');
$states->add('VT');
$states->add('RI');
$states->add('CT');
var_dump($states->count());
var_dump($states->allStatesListed());
$states->accessInternalStructure();

Output:

int(5)
bool(false)

Warning: sort() expects parameter 1 to be array, null given...

As you can see, ListOfStates is able to use all the public functionality of ListOfThings, even though those functions all depend on the private variable $_list. That said, ListOfStates cannot directly manipulate $_list; it can only act on $_list indirectly through the public functions defined in ListOfThings.

Check out the Visibility page in the PHP documentation for more details on such things.

Jonathan S.
  • 2,238
  • 16
  • 16
  • foo is private method...and as per visibility private method are not inherited to child class – Poonam Bhatt Jun 17 '12 at 15:40
  • 1
    @Poonam it *is* inherited. But it is not visible, which is why you cannot access it. See http://codepad.viper-7.com/B7OWyr – Gordon Jun 17 '12 at 15:44
  • @Gordon That's not really a good example, because you're using a variable, not a function. Variables contain data that for sure needs to be bound to the instance, so there is a difference. – Niko Jun 17 '12 at 17:34