Behavior of $this on inherited methods - php

I always thought I understood how OOP works (and I have been using it for years), but sometimes I realize some concepts are still not so clear to me.
I just came across this question about method visibility in PHP. The accepted answer explains that a private method cannot be overridden by a child class in PHP. Okay, that makes sense. However, the example made me think about the internal inheritance mechanism in PHP, and the way $this behaves on inherited methods.
Consider this code (example from the PHP Manual, also included in the question mentioned above):
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();
/*
Output:
Bar::testPrivate
Foo::testPublic
*/
Now consider this excerpt from the PHP Manual:
The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
The explanation states that "$this is a reference to the calling object", which is $myFoo. So I expected that $myFoo->test() would always invoke Foo::testPrivate, and never Bar::testPrivate (unless $myFoo were an instance of Bar). I tested $this with get_class, and it always returns Foo, even from inside Bar::testPrivate and Bar::test. However, $this behaves like an instance of Bar when Bar::test calls $this->testPrivate().
That's really confusing, and I am trying to understand why it works that way!
I thought inherited methods (public or protected) were somehow copied from the base to the child class. Private methods would not be copied at all. But this example indicates that it doesn't work like this. It looks like the instance of Foo keeps an internal instance of Bar, and delegates method calls to it when necessary.
I am trying to learn something here, and I only learn when things make sense to me. This one does not. After writing all this, I think I can summarize it with two questions:
Could someone briefly explain how inheritance works internally in PHP? Or at least point me to an article or documentation about that?
Is the behavior or $this discussed here present on other OO languages as well, or is it particular to PHP?

Inheritance in PHP works the same way it does in most object-oriented languages.
When you have a "virtual" method, the method is not bound directly to the caller. Instead, every class contains a little lookup table which says "this method name is bound to that implementation". So, when you say $this->testPublic(), what actually happens is that PHP:
Gets the virtual table for the current class
Looks up the virtual table entry for testPublic in that table
Invokes the method to which that lookup points
Since Foo overrides testPublic, its virtual table contains an entry for testPublic pointing to Foo::testPublic.
Now, with the private methods, the behavior is different. Since, as you correctly read, private methods cannot be overridden, calling a private method never results in a virtual table lookup. That is to say, private methods cannot be virtual and must always be defined in the class which uses them.
So, the effect is that the name is bound at the time of declaration: all Foo methods will call Foo::testPrivate when they say $this->testPrivate, and all Bar methods will call Bar::testPrivate.
To sum up, saying that "inherited methods are copied to the child" is not correct. What actually happens is that the child begins with its method-name-lookup-table being populated with its parent class' entries, and then adds its own functions and replaces any overridden entries. When you call $this->something, this lookup table is consulted for the current object's class. So if $this is an instance of Foo, and Foo overrides testPublic, you get Foo::testPublic. If $this is an instance of Bar, you will get Bar::testPublic.

Well, private methods and properties are exactly that - private. For all intents and purposes, you can consider them "internal", meaning internal to the class they're defined in. This means that they're never inherited, and can never be overridden.
Thus, when using $this in combination with a private method or property, it will always be the method or property within the same class as the reference to $this. This happens because $this called within a parent class cannot access private methods or properties in another class (because they're private), even from child classes.
Hope this helps.

Related

PHP static methods

I understand that static methods have no access to state of instance objects of their class types and hence referencing $this inside them results in an error.But objects can reference static methods using object to member operator ->
$obj->staticMethod();
and can even pass it their state via paramaters.
$para1 = $obj->para1;
$para2 = $obj->para2;
$obj->staticMethod($para1, $para2);
How is this last example possible when statics are resolved in static context. If someone can explain to me the general behaviour of statics in php code. you can even talk about C related concepts if it will help.
Since you state that you already understand what static means, I'll skip over that.
However, it may still be good to reference PHP's documentation on the static keyword. In particular the following two alerts are important (and hard to glance over, really).
Caution In PHP 5, calling non-static methods statically generates an E_STRICT level warning.
And this one (italic emphasis mine).
Warning In PHP 7, calling non-static methods statically is deprecated, and will generate an E_DEPRECATED warning. Support for calling non-static methods statically may be removed in the future.
So, to cut a long story short: yes, your example will run (for now), because the PHP interpreter will try to fix up your mistake for you. You should however never do this. What the PHP interpreter will do is:
Say your $obj is of type Foo. Then it will read
$obj->staticMethod($para1, $para2);
conclude that staticMethod is static and instead execute
Foo::staticMethod($para1, $para2);
It is of course perfectly fine to pass parameters that are properties of an instance of Foo. It doesn't matter to staticMethod where the parameters come from.
To elaborate a bit more on why this works, while using $this in a static method is not allowed.
You can think of normal methods as static functions that have one extra: they receive an implicit parameter $this. The value of $this is simply the object on which the method is called. Thus, $obj->do($a, $b, $c) is equivalent to calling Foo::do($obj, $a, $b, $c) and naming the first argument of do, $this. This is convenient, because we can now easily define methods that work on an instance of an object without having to explicitly state over and over again that this instance is a parameter of our methods. Great.
Now back to static functions. The only difference with normal methods is that they do not receive this implicit $this parameter. Thus, using $this inside of them is invalid. Not because it is forbidden, but because it does not reference anything. PHP does not (and cannot) have a clue what $this should refer to.
Another way to look at it. Say that our Foo class has two properties: $para1 and $para2, both numbers. Say that you write a method that returns the sum of these numbers. One way is to do this:
public static function sum($para1, $para2) {
return $para1 + $para2;
}
Great. Works. However, it is annoying to have to call it like this
$sum = Foo::sum($obj->para1, $obj->para2);
So, this is what methods are for!
public function sum(/* implicit $this parameter */) {
// write looking up the properties once inside the function, instead
// of having to write it every time we call the function!
return $this->para1 + $this->para2;
}
// ...
$sum = $obj->sum(); // $obj is passed implicitly as $this
Because static functions do not receive an implicit $this parameter, using $this inside of them is like trying to use $undefined when you have never defined it. Thus, invalid.
Static means class members in simple terms , A static data member is accessible within a class regardless object is created or not . The static function are also functions dedicated to whole class . Static function works with static data only bit it can sometimes vary . Though statics are class dedicated, you can access them using object. It is allowed in all languages. Why ? Because of feasibility . If an object is not being able to access static members , that is a limitation.

Singleton design pattern in PHP

I have a question about singleton design pattern.
When we apply singleton for our class, we need to set class' constructor private so we can not instantiate the class normally, but we also instantiate it in class context (if the object we need doesnt exist). The question is why dont private constructor prevent us from instantiate class in class context?
Visibility modifiers are for specifying who can be trusted to interact with this method or property. The idea being that each method and property has a specific purpose and should be used in specific ways; e.g. certain methods should only be called at certain times and certain properties should only ever be set to certain values. If each method and property is public, any code can interact with it at any time, even if it's not "qualified" to do so correctly. To minimise problems resulting from that, visibility can be restricted to a family of classes only (protected) or one specific class alone (private).
There is no difference whether this interaction is in a static or object context, or even whether it's the "current" object or another instance of it. The class is expected to "be qualified" to interact with the method correctly, so it is allowed to do so. This might surprise you, but even this works just fine according to this philosophy:
class Foo {
private $bar;
public static baz() {
$obj = new Foo;
$obj->bar = 42;
}
}
The class is not manipulating $this, it's manipulating a private property of an instance of itself, and that's allowed. So is calling its own constructor.
If a class method is set to private you cannot access it outside the class, neither in the child class, but you can access the private method with-in that particular class only.
__construct() is no different, when you set __construct() to private you cannot access it outside the class.
now to clarify a little bit more, when we instantiate an object of a class, PHP automatically invokes its constructor.. which, if set to private, prevents instantiation of a new object. BUT.. if we instantiate an object of a class from with-in that that class, PHP tries to invoke __construct() on it & nothing is preventing it there..
I hope I was able to make it clear for you. :)

"Overloading" a private method in PHP

I know I cannot overload methods in PHP. And, as far as I know, private methods in a class are invisible to classes that extend the base class. So why this does not work?
class Base {
private function foo($arg) {
print "Base $arg";
}
}
class Child extends Base {
public function foo() {
print "Child";
}
}
$c = new Child;
print $c->foo();
The error:
PHP Strict Standards: Declaration of Child::foo() should be compatible with Base::foo($arg) in /var/www/boludo.php on line 17
I assumed that foo($arg) method is invisible in Child class because is private. So, I'm not overloading foo, I'm just creating a method called foo.
To fix the Notice, simply change foo() in the Child to
public function foo($arg = null) {
As to the question "why does this not work":
Visibility in PHP is strictly about runtime access. It doesn't affect how you can extend/compose/overload classes and methods. Loosening the visibility of a private method from a Supertype in a Subtype will add a separate method in the subtype with no access to the same named method in the supertype. However, PHP will assume a parent-child relationship for these. That didn't cause the Notice though. At least, not on it's own.
The reason why you get the Notice, is that you are then also trying to change the method signature. Your foo() does no longer require $arg to be passed to it. When you assume a parent-child relationship between the methods, this is a problem because the Liskov Substitution Principle states that "if S is a subtype of T, then objects of type T may be replaced with objects of type S" without breaking the program. In other words: if you have code that uses Base, you should be able to replace Base with Child and the program should still work as if it was using Base.
Assume your Base also has a public method bar().
class SomeClientUsingBase
{
public function doSomethingWithBase(Base $base)
{
$result = $base->bar();
// …
Now imagine Child changes bar() to require an argument. If you then pass Child for Base into the client, you will break the client, because the client calls $base->bar(); without an argument.
Obviously, you could change the client to pass an argument, but then the code really depends on how Child defined the method, so the Typehint is wrong. In fact, Child is not a Base then, because it doesn't behave like a Base. It's broken inheritance then.
Now the funny thing is, if you remove that $arg from foo(), you are technically not violating LSP, because the client would still work. The Notice is wrong here. Calling $base->foo(42) in a client that previously used Base will still work with a Child because the Child can simply ignore the argument. But PHP wants you to make the argument optional then.
Note that LSP also applies to what a method may return. PHP just doesn't include the return type in the signature, so you have take that into account yourself. Your methods have to return what the Supertype returned or something that is behaviorally equivalent.
You can do function overloading in PHP using __call function: http://www.php.net/manual/en/language.oop5.overloading.php#object.call
Apart from that, your problem is that in that way, you violate the Substitutability principle:
http://en.wikipedia.org/wiki/Liskov_substitution_principle
Something that PHP uses. In that way, if you replace an object of Base class type to one with a Child class type, Substitutability is violated. You are changing the interface of the base class in the derived one, removing the argument of method foo(...) and in this way, objects of Base class type can not be replaced with objects of Child class type without breaking the program, thus violating Liskov's Substitutability Principle (LSP).

Calling static method non-statically

I have a child class that extends a class with only static methods. I would like to make this child class a singleton rather than static because the original developer really wanted a singleton but used static instead (obvious because every method in the static class calls the Init() function (basically a constructor)).
Most of the methods in the parent don't need to be overwritten in the child, but I would like to avoid having to write methods like this:
public function Load($id)
{
return parent::Load($id);
}
when I would prefer not to overwrite the method at all and just use:
$child->Load($id);
Is it possible to call a static method non-statically? Is it possible to extend a static object with an instance object? I know I can try it and it will likely work (PHP is very forgiving), but I don't know if there is anything I should be concerned about.
Can you inherit static methods?
Yes
Can you override static methods?
Yes, but only as of PHP 5.3 do they work as you would expect: http://www.php.net/manual/en/language.oop5.static.php (ie. self binds to the actual class and not the class it's defined in).
Is it possible to call a static method non-statically?
Yes, but will lose $this. You don't get a warning (yet) but there also isn't really a reason to call it the wrong way.
Two part answer.
First, about the titular question: calling a static method non-statically is perfectly fine; #SamDark's comment is correct. It does not produce a warning, nor does it cause any kitten murdering. Try it:
<?php
class test {
public static function staticwarnings(){
echo "YOU ARE (statically) WARNED!\n";
}
}
error_reporting(E_ALL);
$test = new test();
echo "\n\ncalling static non-statically\n";
$test->staticwarnings();
If you had an instance reference, $this, in that static method, then you would get a fatal error. But that is true regardless of how you call it.
Once again, there isn't a warning, nor any kitten killed.
Second part of the answer:
Calling an overridden parent function from an overriding child class requires something called "scope resolution". What the OP is doing in their method is NOT calling a static method. (Or at least, it doesn't have to be; we can't see the parent implementation). The point is, using the parent keyword is not a static call. Using the :: operator on an explicit parent class name is also not a static call, if it is used from an extending class.
Why is that documentation link so strangely named? It's literally Hebrew. If you've ever run into an error related to it, you might have observed the delightfully-named parser error code T_PAAMAYIM_NEKUDOTAYIM.

multiple ways of calling parent method in php

At first I was confused why both of the method calls in the constructor work, but now I think I understand. The extending classes inherit the parent's methods as if they were declared in the class itself, AND the methods exist in the parent, so both should work.
Now I'm wondering if there is a preferred way (i.e. best practice) of calling the method (via parent or this), and whether or not these are truly identical ways of executing the same code, or if there are any caveats when using one over the other.
Sorry, I'm probably over thinking this.
abstract class Animal {
function get_species() {
echo "test";
}
}
class Dog extends Animal {
function __construct(){
$this->get_species();
parent::get_species();
}
}
$spike = new Dog;
There are three scenarios (that I can think of) where you would call a method in a subclass where the method exists in the parent class:
Method is not overwritten by subclass, only exists in parent.
This is the same as your example, and generally it's better to use $this->get_species(); You are right that in this case the two are effectively the same, but the method has been inherited by the subclass, so there is no reason to differentiate. By using $this you stay consistent between inherited methods and locally declared methods.
Method is overwritten by the subclass and has totally unique logic from the parent.
In this case, you would obviously want to use $this->get_species(); because you don't want the parent's version of the method executed. Again, by consistently using $this, you don't need to worry about the distinction between this case and the first.
Method extends parent class, adding on to what the parent method achieves.
In this case, you still want to use $this->get_species(); when calling the method from other methods of the subclass. The one place you will call the parent method would be from the method that is overwriting the parent method. Example:
abstract class Animal {
function get_species() {
echo "I am an animal.";
}
}
class Dog extends Animal {
function __construct(){
$this->get_species();
}
function get_species(){
parent::get_species();
echo "More specifically, I am a dog.";
}
}
The only scenario I can imagine where you would need to call the parent method directly outside of the overriding method would be if they did two different things and you knew you needed the parent's version of the method, not the local. This shouldn't be the case, but if it did present itself, the clean way to approach this would be to create a new method with a name like get_parentSpecies() where all it does is call the parent method:
function get_parentSpecies(){
parent::get_species();
}
Again, this keeps everything nice and consistent, allowing for changes/modifications to the local method rather than relying on the parent method.
Unless I am misunderstanding the question, I would almost always use $this->get_species because the subclass (in this case dog) could overwrite that method since it does extend it. If the class dog doesn't redefine the method then both ways are functionally equivalent but if at some point in the future you decide you want the get_species method in dog should print "dog" then you would have to go back through all the code and change it.
When you use $this it is actually part of the object which you created and so will always be the most up-to-date as well (if the property being used has changed somehow in the lifetime of the object) whereas using the parent class is calling the static class method.

Categories