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.
Related
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.
Looking for a clean way to determine the class (in this case, either parent or child class) of the method that calls a method in the parent class.
I thought late static binding could handle this, but seems like that only really works for calling a static method directly, and not from within an instantiated object's method.
Consider the following:
abstract class ParentClass {
public function parentMethod() {
self::_log("parent.non.static");
}
public static function parentStatic() {
self::_log("parent.static");
}
public static function getClassName() {
return __CLASS__;
}
protected static function _log($key) {
$prefix = 'graphite.key.prefix';
$class = static::getClassName(); // gets the object's class, not calling class
$g_key = "{$prefix}.{$class}.{$key}";
echo "{$g_key} \n";
// Graphite::increment($g_key);
}
}
class ChildClass extends ParentClass {
public function childMethod() {
self::_log("child.non.static");
}
public static function childStatic() {
self::_log("child.static");
}
public static function getClassName() {
return __CLASS__;
}
}
$obj = new ChildClass;
$obj->childMethod(); // graphite.key.prefix.ChildClass.child.non.static
$obj->parentMethod(); // graphite.key.prefix.ChildClass.parent.non.static
ParentClass::parentStatic(); // graphite.key.prefix.ParentClass.parent.static
ChildClass::childStatic(); // graphite.key.prefix.ChildClass.child.static
Looking for a clean way to get the class that calls the _log() method without having to pass it in as a parameter. Doesn't have to be static at all, but I was playing around with the late static binding, because I thought that would work, but it just gets the name of the instantiated object, not the child/parent class of the method that calls the _log() method :-/
Edit:
Just to be clear, I'm after getting the class name of the method that called _log() from within the instantiated object (like parentMethod() and childMethod()) Don't care if _log() is static or not. If that makes it easier, fine. But the static ParentClass::parentStatic() and ChildClass::childStatic() were just to show late static bindings and what I figured might work, but not from calling within an instantiated object
http://php.net/manual/en/function.get-called-class.php
class One {
public static function test() {
echo get_called_class() . PHP_EOL;
}
}
class Two extends One {}
One::test();
Two::test();
Output:
One
Two
Also, according to the top comment in the docs static::class also works as of PHP 5.5.
get_class will get the class name of a class instance. This can also be called on $this within a class. If you have a class that extends/implements another, $this will refer the the instantiated class, meaning the child class.
Another option is to use debug_backtrace to get the stack of functions that lead up to where you currently are. You can parse the returned array to get whatever you need including line numbers, classes, functions, methods, whatever.
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();
//Foo::testPublic
//Bar::testPrivate
I'm having a lot of trouble understanding this output. Would someone be able to give me a clear succinct explanation of what is going on? I'm learning OOP and wanted to know how to use extensions to override the parent class functions.
The test() method calls 2 methods:
testPublic - it's a public one, so it was overriden in the Foo. So the Foo::testPublic is called
testPrivate - it's a private one, so it's only visible for each class itself. For the caller method (it's Bar) - it's a Bar::testPrivate
So - if the method is public or protected - it can be overriden and called from the ancestor/child; if it's private - it cannot.
The $this references the current object. So when you do the following.
$this->testPublic();
It will call testPublic() for the top most class that implements that function.
If you want to only call the parent class, then there is the parent keyword.
parent::testPublic();
That will call testPublic() on the class below the current object.
Becare full not to confuse the -> operator with the :: operator.
The :: operator references the class definition of an object, where as -> references the instance of an object.
self::testPublic();
$foo::testPublic();
That references a static function called testPublic(), and static methods are defined at the class level.
$foo->testPublic();
$this->testPublic();
That references a function as part of the instance of an object, and there is a vtable used to look up which object instance level should be called.
Check below given code. I could not get that how it had called testPrivate() method of Class Bar class. As per my assumption it should call method from Foo class i.e. Foo::testPrivate.
Check the demo here
<?php
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "<br>Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "<br>Foo::testPublic\n";
}
private function testPrivate() {
echo "<br>Foo::testPrivate\n";
}
}
$myFoo = new foo();
$myFoo->test(); // Bar::testPrivate
// Foo::testPublic
?>
If you want a child class to be able to overload a method defined in a parent class, that method has to be declared as protected -- and not as private.
Here, if you change your testPrivate methods definitions to :
protected function testPrivate() {
echo "<br>Bar::testPrivate\n";
}
and :
protected function testPrivate() {
echo "<br>Foo::testPrivate\n";
}
You'll get the output you expected :
Foo::testPrivate
Foo::testPublic
For more informations, you should take a look at the Visibility section of the manual -- quoting the first sentences :
Class members declared public can be
accessed everywhere. Members
declared protected can be accessed
only within the class itself and by
inherited and parent classes.
Members declared as private may
only be accessed by the class that
defines the member.
I think you misunderstand what a private method is. Foo::testPrivate() can only be call from inside Foo itself. You can acheive the behaviour you describe with a protected method. Protected means visible to the class and any classes which extend it.
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.