Inheritance of a private method and Late Static Binding in php - php

So I've been reading official PHP documentation on Late Static Bindings and came across a confusing example:
<?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
?>
The output of the example:
success!
success!
success!
Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
Can someone please explain why the private method foo() gets copied to B? As far as I know only public and protected properties get copied to child class. What am I missing?

Maybe the comment "foo() will be copied to B" is a bit confusing or interpreted incorrectly. foo() is still private to A and can only be accessed from methods within A.
ie. In the example if you try to execute$b->foo() its will still fail as expected.
This is as I explained the example to myself and maybe will be helpful for others:
Considering class B.
$b->test() is able to access foo() as a public member of A.
$this->foo() also succeeds within $b->test()
$static::foo() succeeds because it is calling the version of foo() defined in A, from test() which is also defined in A. No scoping conflict.
Considering class B.
When foo() is overridden in class C,
$c->test() of course is still accessible as a public member if A.
and within $c->test() $this->foo() is accessible as a private member of A. - all good.
BUT
$static::foo() is now trying the access from A, the version of foo() defined in class C and so fails because it is private in C. - as per the error message.

Related

What is the impact of visibility on late static bindings in a non static context in PHP? [duplicate]

I'm having a hard time trying to understand the output of the following code:
class Bar
{
public function test() {
$this->testPublic();
$this->testPrivate();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic\n";
}
private function testPrivate() {
echo "Foo::testPrivate\n";
}
}
$myFoo = new foo();
$myFoo->test();
Output:
Foo::testPublic
Bar::testPrivate
Class Foo overrides testPublic() and testPrivate(), and inherits test(). When I call test(), there is an explicit instruction envolving $this pseudo variable, so after I created $myFoo instance, the final calls of test() function would be $myFoo->testPublic() and $myFoo->testPrivate(). The first output is as I expected, since I overrode testPublic() method to echo Foo::testPublic. But the second output makes no sense to me. Why is it Bar::testPrivate if I overrode testPrivate() method? Also the private method from parent class wouldn't be inherited anyway, by definition! It makes no sense. Why is the parent method the one being called???
The problem with your code is that the method Bar::testPrivate is private, therefore it cannot be overridden by child classes. For starters, I recommend that you read up on visibility in PHP - http://www.php.net/manual/en/language.oop5.visibility.php. There you will learn that only public and protected class member methods/properties can be overridden, private ones cannot.
As a good example, try changing the visibility of the Bar::testPrivate method to either public or protected, without altering anything else in your example code. Now try and run your tests. What happens? This:
PHP Fatal error: Access level to Foo::testPrivate() must be protected (as in class Bar) or weaker
The big question is: "why?". Well, you have now overridden Bar::testPrivate with a private Foo:testPrivate. This new private method is out of scope for Bar::test, because private class members are visible to their current class only, NOT the parent/child classes!
Therefore, as you can see, OOP provides a certain amount of encapsulation for class members, and it can be quite confusing if you don't take the time to understand it.

how don't self keyword saved and referenced as class name of last non-forwarding call in case the class itself has the invoked function?

While studying Late Static Binding in PHP , i read the below quotations:
A “forwarding” call is a static call that is introduced by parent::, static:: or one called
by the function forward_static_call().
A call to self:: can also be a forwarding call if the class falls back to an inherited
class because it does not have the method defined
Late static binding works by storing the class in the last “non-forwarding call”. In
other words, late static binding resolution will stop at a fully resolved static call.
According to the cited two points , look at the below example :
class A {
public static function foo() {
echo static::who();
}
public static function who() {
return 'A';
}
}
class B extends A {
public static function test() {
self::foo();//this is non forward call because self will resolve to the current class because it has the function foo , so now the last stored class name of non forward call became B , then now the foo will be called and static must resolve to B not C.
}
public static function foo() {
echo static::who();
}
public static function who() {
return 'B';
}
}
class C extends B {
public static function who() {
echo 'C';
}
}
C::test(); //
At the point of self::foo() , self here is non-forwarding call because the class itself has the invoked function foo (which is the cited rule in above quotations) and according to the concept of Late Static Binding which stores class name of the last non-forwarding call, so now B is the last saved referenced class name, so now static in foo() must resolve to B, not C.
So, How do this wrong and static resolves to the initial non-forwarding call not the last?
Ref: PHP Zend Certification Study Guide.pdf.
The quote contains the answer:
A call to self:: can also be a forwarding call if the class falls back to an inherited class because it does not have the method defined
In test you are calling foo which doesn't exist in class C, so the class falls back to inherited class B, and self:: will forward the calling information.
There is a similar exemple in the page for Late Static Binding (example #4) accompanied by a similar note:
Late static bindings' resolution will stop at a fully resolved static call with no fallback. On the other hand, static calls using keywords like parent:: or self:: will forward the calling information.

PHP late static binding scope confusion

From PHP mannual second paragraph, it says that:
static:: introduces its scope.
I tried the following example accordingly:
class Father {
public function test(){
echo static::$a;
}
}
class Son extends Father{
protected static $a='static forward scope';
public function test(){
parent::test();
}
}
$son = new Son();
$son->test(); // print "static forward scope"
It works as described. However, the following example will raise a fatal error:
class Father {
public function test(){
echo static::$a;
}
}
class Son extends Father{
private static $a='static forward scope';
public function test(){
parent::test();
}
}
// print "Fatal erro: Cannot access private property Son::$a"
$son = new Son();
$son->test();
My main question is how to interpret the word scope here? If static introduces Son's scope to Father, then why private variables are still invisible to Father?
Are there two things variable scope and visibility scope? I'm new to PHP sorry if this sounds funny.
There are two things at play here: scope and visibility. Both together decide if you can access the property.
As you found in your first test, late static binding lets $a be available in the scope of the Father class. That simply means the variable (not necessarily its value) is "known" to this class.
Visibility decides whether the variables in scope can be accessed by particular classes and instances. A private property is only visible to the class in which it is defined. In your second example, $a is defined private within Son. Whether or not any other class is aware it exists, it can not be accessed outside of Son.
static makes $a a property which is known to Father, but the property's visibility decides whether or not its value can be accessed.
As a test to further help understand it, try using self instead of static. You'll get back a different error that $a is not a property of Father.

when using self, parent, static and how?

If by now I understood a little in ststic Now I realize I do not understand anything. I'm so confused and I struggle to understand and I can not. Someone can explain this program when using self, parent, static and how
All the smallest change I do changes the result without that I can not understand what's going on.
thanks a lot ..
the code from http://docs.php.net/language.oop5.late-static-bindings
<?php
class A {
public static function foo() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
?>
The out put are:
A
C
C
You'll need to understand the concept of Late Static Binding, which determines when an identifier is bound to code/data. You can tell PHP to bind it early (self::) or later (static::).
Slimming the example down to two classes we get:
class A {
public static function foo() {
self::who(); // PHP binds this to A::who() right away
static::who(); // PHP waits to resolve this (hence, late)!
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
B::test();
Look at http://php.net/manual/en/language.oop5.static.php. It says:
Declaring class properties or methods as static makes them accessible without needing an instantiation of the class.
...
Because static methods are callable without an instance of the object created, the pseudo-variable $this is not available inside the method declared as static.
You cannot use $this because when calling a static method, there is no instantiated class object to be placed in the $this variable. So you use self::.
parent:: is referring to the parent class that the current class is extending. For example, for Class C the parent class is Class B, and for Class B, the parent class is Class A. See http://php.net/manual/en/keyword.parent.php.
You use static methods when you want the function to be accessible without actually having an instance of that class declared.
Upon closer inspection of your question, your link points to Late Static Bindings. The first two examples in that page pretty clearly indicate the need for the static:: syntax, but to clarify for the example you posted:
Take a look at the foo() method in Class A. It calls static::who(). This means that the method who() will be called in the scope of the class that called the function, instead of the scope of the class where the function is defined. So if you were to call C::foo(), it would echo C.
If, instead, it called self::who(), it would be calling A::who(). Because within Class A, self:: refers to A.
Hopefully that helps.
The key to the answer is static::who(), remember static:: means you call the method with the actual class (in our case - C).
so C::test() run as follows:
A::foo(); -> calls to A::foo() therefor echo A
parent::foo(); -> calls to C parent (which is B), B::foo() inherits A::foo() which calls to static::who(), but our actual class is C, therefor echo C
self::foo(); -> again calls to foo() which calls to static::who() with our actual class C
if instead of static::who() foo was calling self::who() you would have get three A as a result.
When one inherited class call inherit class's method which methods are use these key word; what will happened!! Explained by this flowchart. I think it will help.

PHP manual OOP visibility example - can someone explain it

I saw this in the PHP OOP manual http://www.php.net/manual/en/language.oop5.visibility.php and I can't get my head around why the output is not: Foo::testPrivate Foo::testPublic
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic\n";
}
private function testPrivate() {
echo "Foo::testPrivate\n";
}
}
$myFoo = new foo();
$myFoo->test(); // Bar::testPrivate
// Foo::testPublic
It's all about the visibility of the variables / methods.
You'll notice that in the Bar class, the method testPrivate() is private. That means that ONLY itself can access that method. No children.
So when Foo extends Bar, and then asks to run the test() method, it does two things:
It overrides the testPublic() method because it's public, and Foo has the right to override it with it's own version.
It calls test() on Bar (since test() only exists on Bar()).
testPrivate() is not overridden, and is part of the class that holds test(). Therefore, Bar::testPrivate is printed.
testPublic() is overridden, and is part of the inheriting class. Therefore, Foo::testPublic is printed.
In some cases, it is easy to notice that you want a private method on the Bar class, but you also wants the Foo class to access it.
But wait, it is public or private?
Here comes the protected modifier.
When a method is private, only the class itself can call the method.
When a method is public, everyone can call it, like a free party.
When a method is protected, the class itself can call it and also whoever inhered this method (children) will be able to call it as a method of their own.
I posted the same question few days ago... because this behaviour wasnt logical for me either.
$this is always referred to the current object which it is used in. In my opinion example like this should throw an error or warning or something. Because in the above example you are actually accessing private members :SSS WHICH SUPPOSED TO BE INACCESSIBLE!

Categories