I'm learning the differences between self, static and this and ran across this example from php.net:
<?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 result is
success!
success!
success!
Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
My question is this: when creating the new object C, shouldn't the call $this->foo(); call the newly replaced private function foo inside class C (and return an error since it's private)?
You should use protected rather than public to get this behaviour.
This is covered in a lot of detail here: What is the difference between public, private, and protected?
A quick excerpt:
public scope to make that variable/function available from anywhere, other classes and instances of the object.
private scope when you want your variable/function to be visible in its own class only.
protected scope when you want to make your variable/function visible in all classes that extend current class including the parent class.
Related
In this example it first searches in B (because static:: resolves to B) and as it does not find it, it now searches in "A" and that is why it succeeds, right? or am I wrong?
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo(); // Why does it execute method of "A" if static:: be resolved to "B"?
}
}
class B extends A {
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
$b = new B();
$b->test();
OUTPUT:
success! success!
Late static binding allows you to refer to the called object's class instead of the class where the method is defined in. But it doesn't change how the scope is resolved, which means that the property/method visibility rules are obeyed as they would if you used self. When you use static, the name of the class will be resolved using runtime information.
For more information read late static bindings on php.net.
In your case when you have this:
public function test()
{
$this->foo();
static::foo();
}
It's actually the same as this when called on an instance of B:
public function test()
{
$this->foo();
B::foo();
}
Upon execution, PHP will start resolving the scope starting with B class. If B doesn't have a method with that name, it will check its parent, and so on. Your B class doesn't have method foo, but A does. So when you execute it, PHP will effectively try to execute something like this:
public function test()
{
$this->foo();
A::foo();
}
Now you can see that both lines are the same. The both call the same private method of A class. Yes, the method is private but it's called from within its own class.
static:: is late binding. If you aren't overriding then it works like same as $this. Classes are blue print of object there are no any Class A or B when you create Object. First it search override methods then public and protected methods of Object and last it search private methods of block.
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo(); // Why does it execute method of "A" if static:: be resolved to "B"?
}
}
class B extends A {
public function foo() {
echo " b success!\n";
}
}
$b = new B();
$b->test();
Check This
success!
b success!
There are several questions about weird behaviour when a child has an implementation of a private parent method like in the following example:
class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
public function callInA() {
$this->toOverridePrivate();
echo "\n";
}
}
class B extends A {
private function toOverridePrivate() {
echo "private toOverridePrivate B\n";
}
public function callInB() {
$this->toOverridePrivate();
echo "\n";
}
}
$a = new A;
$b = new B;
$a->callInA(); // private toOverridePrivate A
$b->callInA(); // private toOverridePrivate A
$b->callInB(); // private toOverridePrivate B
When calling $b->callPrintsInA(), As implementation of toOverridePrivate is called, because B::toOverridePrivate is not accessible from A.
In summary it can be said, that the method must be accessible from the scope it is called, therefore the following fails:
class A {
public function callInA() {
$this->toOverridePrivate();
echo "\n";
}
}
class B extends A {
private function toOverridePrivate() {
echo "private toOverridePrivate B\n";
}
}
$b = new B;
$b->callInA(); // ERROR: Call to private method B::toOverridePrivate() from context 'A'
In the last example toOverridePrivate is not accessible in the scope of A, despite the fact that $this is in fact an object of type B.
But one aspect which varies between answers is, if having another implementation of a private parent method in a child class in overriding. In the most popular question I could find "Strange behavior when overriding private methods" the accepted answer says it is overriding...
Now if you override the private method, its new scope will not be A, it will be B
...while the second most voted answer states that...
A private method is not overridable
... because it is unkown outside of its class scope.
The last statement is questionable, because declaring a private method final makes overriding it not possible with the exact message that it can not be overridden:
class A {
final private function someMethod() { }
}
class B extends A {
// Fatal error: Cannot override final method A::someMethod()
private function someMethod() { }
}
So...
Does a child class override a private parent method, if it has an implementation of that method? If this isn't overriding, what vocabulary would you use to describe it?
The child class simply has no knowledge of any private methods from parent class. Any such method in the scope of child class is undefined. It can't be overridden if it is not defined in the first place.
If the method is public it is visible to everyone and any code can call it. When the method is protected in is only known to the class itself and any of its descendants. When a method is private it is only known in the scope of this class. To any other class this method is simply undefined.
You can define another method with the same name in the child class, but it is not overriding anything. You could still call it overriding, but it would not make much sense. Overriding means that if you do not redefine the method in the child class it will call the definition from parent class. With private methods it is not possible because they are not accessible from the child class.
Consider this example:
class A {
private function toOverridePrivate() {
echo "private toOverridePrivate A\n";
}
}
class B extends A {
public function toOverridePrivate() {
parent::toOverridePrivate();
}
}
$o = new B;
$o->toOverridePrivate();
Fatal error: Uncaught Error: Call to private method A::toOverridePrivate() from context 'B'
Whether the class B defines method toOverridePrivate or not, it makes no difference, because the method toOverridePrivate from A is always inaccessible.
I've found something that appears to be a strange inheritance issue in PHP.
From the PHP manual:
Members declared protected can be accessed only within the class
itself and by inherited and parent classes.
To me this means:
A can access the protected members of B if A instanceof B or B instanceof A.
However, if both A and B extend Foo, and Foo has a protected constructor which is not overwritten in B, then I can create an instance of B from within A. This does not make sense to me, because A is not an instance of B and B is not an instance of A. I can also call the protected method $b->test() from within A, which executes the method implemented in B. (If B does not redeclare test() then the implementation in Foo is executed.) To me this is even more strange because I cannot create an instance of B from within A if B directly implements a protected constructor. It seems strange that I cannot access a protected constructor (also declared in the parent class) but accessing a protected method (also declared in the parent class) is no problem.
Note that I do get the expected behavior when I use a class C which does not extend Foo. If I try to instantiate B from within C, I get a fatal error because I'm trying to access a protected constructor. If I add a public constructor to B it is possible to instantiate it (which is expected) and I still cannot access the protected method test() (this is also expected behavior). I expect the same behavior when using A instead of C.
Sample code which explains again:
class Foo {
protected function __construct() {
echo('Constructing ' . get_called_class());
}
protected function test() {
echo('Hello world ' . __METHOD__);
}
}
class A extends Foo {
public function __construct() {
parent::__construct();
}
public function testB() {
// Both of these lines work
$b = new B();
$b->test();
}
}
class B extends Foo {
protected function test() {
echo('Hello world Again ' . __METHOD__);
}
}
class C {
public function __construct() {
}
public function testB() {
// Both of these lines cause fatal errors
$b = new B();
$b->test();
}
}
$a = new A();
$a->testB();
$c = new C();
$c->testB();
I'm probably not seeing something, but I can't find what. Could anyone explain the behavior to me?
You can access those methods because there is a declaration of them as protected in Foo, which is your parent and that gives you permission to access it. If you remove the declaration from the parent and declare the protected method in B you will get a Fatal Error.
This is reported as a bug in PHP https://bugs.php.net/bug.php?id=50892
There is no rationale about this, it has been reported 2 years ago: https://bugs.php.net/bug.php?id=52120
I've recently been working on some class files and I've noticed that the member variables had been set in a protected static mode like protected static $_someVar and accessed like static::$_someVar.
I understand the concept of visibility and that having something set as protected static will ensure the member variable can only be accessed in the super class or derived classes but can I access protected static variables only in static methods?
Thanks
If I understand correctly, what you are referring to is called late-static bindings. If you have this:
class A {
protected static $_foo = 'bar';
protected static function test() {
echo self::$_foo;
}
}
class B extends A {
protected static $_foo = 'baz';
}
B::test(); // outputs 'bar'
If you change the self bit to:
echo static::$_foo;
Then do:
B::test(); // outputs 'baz'
Because self refers to the class where $_foo was defined (A), while static references the class that called it at runtime (B).
And of course, yes you can access static protected members outside a static method (i.e.: object context), although visibility and scope still matters.
Static variables exist on the class, rather than on instances of the class. You can access them from non-static methods, invoking them something like:
self::$_someVar
The reason this works is that self is a reference to the current class, rather than to the current instance (like $this).
By way of demonstration:
<?
class A {
protected static $foo = "bar";
public function bar() {
echo self::$foo;
}
}
class B extends A { }
$a = new A();
$a->bar();
$b = new B();
$b->bar();
?>
Output is barbar. However, if you try to access it directly:
echo A::$foo;
Then PHP will properly complain at you for trying to access a protected member.
Consider the following piece of code:
class foo {
private function m() {
echo 'foo->m() ';
}
public function call() {
$this->m();
}
}
class bar extends foo {
private function m() {
echo 'bar->m() ';
}
public function callbar() {
$this->m();
}
}
$bar = new bar;
$bar->call();
$bar->callbar();
Now, changing the visibility of the m() method, I get:
(+ for public, - for private)
Visibility bar->call() bar->callbar()
======================================================
-foo->m(), -bar->m() foo->m() bar->m()
-foo->m(), +bar->m() foo->m() bar->m()
+foo->m(), -bar->m() ERROR ERROR
+foo->m(), +bar->m() bar->m() bar->m()
(protected seems to behave like public).
I was expecting everything to behave like it does when both are declared public. But although foo->call() and bar->callbar() are essentially the same thing, they yield different results depending on the visibility of m() in foo and bar. Why does this happen?
Inheriting/overriding private methods
In PHP, methods (including private ones) in the subclasses are either:
Copied; the scope of the original function is maintained.
Replaced ("overridden", if you want).
You can see this with this code:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
//not necessary; just to make explicit what's happening
function callH() { parent::callH(); }
}
$b = new B;
$b->callH();
Now if you override the private method, its new scope will not be A, it will be B, and the call will fail because A::callH() runs in scope A:
<?php
class A {
//calling B::h, because static:: resolves to B::
function callH() { static::h(); }
private function h() { echo "in A::h"; }
}
class B extends A {
private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'
Calling methods
Here the rules are as follows:
Look in the method table of the actual class of the object (in your case, bar).
If this yields a private method:
If the scope where the method was defined is the same as the scope of the calling function and is the same as the class of the object, use it.
Otherwise, look in the parent classes for a private method with the same scope as the one of the calling function and with the same name.
If no method is found that satisfies one of the above requirements, fail.
If this yields a public/protected method:
If the scope of the method is marked as having changed, we may have overridden a private method with a public/protected method. So in that case, and if, additionally, there's a method with the same name that is private as is defined for the scope of the calling function, use that instead.
Otherwise, use the found method.
Conclusion
(Both private) For bar->call(), the scope of call is foo. Calling $this->m() elicits a lookup in the method table of bar for m, yielding a private bar::m(). However, the scope of bar::m() is different from the calling scope, which foo. The method foo:m() is found when traversing up the hierarchy and is used instead.
(Private in foo, public in bar) The scope of call is still foo. The lookup yields a public bar::m(). However, its scope is marked as having changed, so a lookup is made in the function table of the calling scope foo for method m(). This yields a private method foo:m() with the same scope as the calling scope, so it's used instead.
Nothing to see here, error because visibility was lowered.
(Both public) The scope of call is still foo. The lookup yields a public bar::m(). Its scope isn't marked as having changed (they're both public), so bar::m() is used.
A private method is not overridable, as a private method is not visible even to its subclasses. Defining a method as protected means it is not visible outside of the class itself or its subclasses.
If you have a method that you want to use from your parent class but want children to able to modify its behaviour, and don't want this method available externally, use protected. If you want functionality in your parent class that cannot be modified in any way by subclasses, define the method as private.
EDIT: to clarify further, if you have two methods with the same name in a parent and subclass, and these methods are defined as private, essentially the subclass method has absolutely no relation to the parent method. As stated, a private method is COMPLETELY INVISIBLE to the subclass.
Consider this:
class foo {
private function m() {
echo 'foo->m() ';
}
private function z() { echo "foo->z();"; }
public function call() {
$this->m();
}
}
class bar extends foo {
private function m() {
echo 'bar->m() ';
}
public function callbar() {
$this->m();
}
public function callz()
{
$this->z();
}
}
Calling $bar->callz(); is going to produce an ERROR, because z does not exist in the subclass at all, not even as an inherited method.
According to the PHP manual:
Members declared as private may only
be accessed by the class that defines
the member.
http://www.php.net/manual/en/language.oop5.visibility.php
EDIT
they yield different results depending
on the visibility of m() in foo and
bar. Why does this happen?
If m() in foo is public, it is overridable. When this is the case m() from bar overrides m() in foo.