I'm used to put method as protected in my PHP classes. But playing with private I'm starting to have doubts.
I know that it might be a duplicate but I can't find clarity from the SO-answers I've read.
Checked the documentation and SO answers but still my tests are incoherent with what is being said there (see below).
Especially is not often explained the kind of "tangling" among public and private method, when extending the same type class, in the context of PHP.
For example:
<?php
class A
{
private function ab() { echo 'test'.PHP_EOL; }
public function test() { $this->ab(); }
public function accessprivate($obj) { $obj->ab(); }
}
class B extends A
{
public function ab() { echo 'overridden-public'.PHP_EOL; } // expect notice overriding private parent method
}
$a = new A;
$a2 = new A;
$b = new B;
$a->test(); // expect 'test'
$b->test(); // expect access to B::ab() and print 'overridden-public'
$b->ab(); // expect access to B::ab() and print 'overridden-public'
$a2->accessprivate($a); // expect 'test' since is the same class
$b->accessprivate($a); // expect cannotaccess private of A from class B
When running this is the result:
test test overridden-public test test
The main point is that I expected that a private method is inherited but not accessible to child classes; hence:
I shouldn't be able to change the visibility of ab() to public
with the override of ab() in B I would expect test() to call ab() on $this as B instance, and get printed "overridden-public"
accessprivate() from $a2 on $a is fine because they are the same class
accessprivate() from $b on $a should NOT be fine because they are different classes (parent & child)
So the questions are:
Why am I wrong? What am I misunderstanding?
Is this visibility model the same on other languages or PHP is doing it differently? And in that case, are my expectation fitting more the visibility model of some other language?
I shouldn't be able to change the visibility of ab() to public
An overriding implementation must have the same or higher visibility in order to not violate the LSP. The overriding implementation doesn't expose the parent implementation, so no fundamental concern here. If the parent's implementation is non-public, for all intents and purposes it doesn't exist. For all intents and purposes the child is adding a new method to the class, which is fine.
with the override of ab() in B I would expect test() to call ab() on $this as B instance, and get printed "overridden-public"
private methods are "hard bound" to the declaring class. A::test will call a private A::ab preferably. This is specifically so class internals can remain private. Should an extending class implement an identical method unknowingly, there's no surprising behaviour within A.
accessprivate() from $b on $a should NOT be fine because they are different classes (parent & child)
It's still calling A::accessprivate since B doesn't implement any such method, which rather works the same as the test method and the explanation in the previous paragraph.
The purpose of private is largely to guarantee no interference from outside or extending code. If you're marking methods as private, you can be very sure which implementation of code will be called (always the declaring class), regardless of whether methods are overridden in children. If you keep this in mind, the behaviour is pretty expected and self-explanatory. protected methods explicitly allow and expect overriding to take place and behave accordingly.
Related
This question already has answers here:
Is subclass inherits private members from parent class? [duplicate]
(2 answers)
Closed 5 years ago.
Accept my apologies for being beginner for OOP,
according to the below code :
Class Test{
private $name = "youhana";
function setPrivatePropFromInside(){
$this->name = "mina";
}
function getPrivate(){
var_dump(__CLASS__);
echo $this->name ;
}
}
Class Test2 extends Test {
}
$obj2 = new Test2();
$obj2->getPrivate();
My Question is that Is Inheritance means to copy from parent to child or the child gain access to the parent visible members?
let me describe why I have confusion by mentioning my thoughts with both of the question members :
In case its a copy, so the invoking the method getPrivate from the child object must return null because the private members will not be copied from the parent to the child, and also the CLASS constant must return Test2 not TEST in case the Inheritance means copying.
so the code mentioned above would equals :
Class Test{
private $name = "youhana";
function getPrivate(){
var_dump(__CLASS__);
echo $this->name ;
}
}
Class Test2 extends Test {
function getPrivate(){
var_dump(__CLASS__);
echo $this->name ;
}
}
In case Inheritance means to gain access to the parent visible members, that doesn't refer or point to the concept of inheritance (the children get the characteristics of the parent) despite this concept is logically correct with the results upon executing the mentioned code snippet.
I Read more than 20 References and I still have the confusion, and again accept my apologies for being beginner searching for the correct approach.
Note that: I asked a question Here but after studying more references, I have been returned back to the confusion again, so I need a solid answer.
It's nothing to do with "copying".... a class defines methods and properties together with a visibility indicating from where those methods and properties can be accessed.
When one class extends another, then you are creating an inheritence hierarchy or tree, or a set of class contexts.
When you instantiate a child class, you are actually instantiating an object that inherits that full tree.
When you call a method against that instance, it looks to see if that method exists (and is accessible) in the child class definition. If so, it executes it in that context: if not, it looks to see if the method exists in the parent class; and if so, will execute it in that context.
When a method executes in a specific context within the hierarchy of class extensions, it has access to everything visible in that context. Private properties are only accessible in the context where they are defined/the class where they are defined; but methods in that context/class have access to those private properties.
If (as in this case) your public getters and setters exist in the same class/context as the private property, then those methods have access to the private property (because they are in the same class/context), and can be used to access it from outside of that context because the getters/setters themselves are public.
Is Inheritance means copy from parent to child or the child gain
access to the parent visible members?
The child gain access to the visible members of parent class. Also, you get flexibility to override the feature (method) of parent class.
I think your confusion comes from thinking that
private $name = "youhana";
is a static value which exists in class declaration. In any case, that is just a shorthand for declaring variable values in constructor so even if inheritance "copy" from parent its a wrong example to show that. What you should be asking is: do static properties get copied from the parent? Static properties exists in class declaration and any modifications are visible to all objects of that class.
php docs on static properties
Answer is no. Only reference to parent's value is passed.
I wrote a simple example to illustrate
<?php
class Foo
{
public static $my_static = 'foo';
public function staticValue() {
return self::$my_static;
}
public function modifyFooStatic(){
return self::$my_static .= '+';
}
}
class Bar extends Foo
{
public function fooStatic() {
return self::$my_static;
}
public function modifyBarStatic(){
return self::$my_static .= '-';
}
}
$foo = new Foo();
$foo->modifyFooStatic();
print $foo->staticValue() . "\n"; // foo+
$bar = new Bar();
$bar->modifyBarStatic();
print $bar->fooStatic() . "\n"; // foo+-
?>
As you can see both child and parent modifies the same variable even if child calls it with sellf::$my_static
I encountered the following structure:
// parent class
class A
{
public function myFunc1()
{
$this->myFunc2();
}
private function myFunc2()
{
echo "called from class A";
}
}
// sub-class
class B extends A
{
private function myFunc2()
{
echo "called from class B";
}
}
$foo = new B();
$foo->myFunc1();
I expected, since there is no myFunc1() in class B, that the parent-function is called. This seems to be correct.
Now, to play it safe, I var_dump $this in myFunc1() and it shows, that this is an object from type B.
From my understanding, it should call myFunc2() from the Class B, but this isn't happening. It is calling the method from class A.
called from class A
instead of
called from class B
Yes, the functions are private and if I change it to protected, it works as expected. But private implies, that I have access to this function(s), when I'm in the specific context, doesn't it?
Private functions are only available within the class they are defined in, not in sub classes.
This means that your Object has two completely different functions, that happen to have the same name. Object B is aware of having one of them while Object A is only aware of having the other one. They do not interact in any way.
Since these functions are entirely private to the specific class they were defined in, there is no way to override them, or for any other class to call them.
If you need behavior that allows you to override how the class works, you'll have to use protected instead, which is designed specifically to be callable and changeable from extensions (and as such is a completely different beast all-together).
private always belongs to the same class you can't inherit it. That's the reason why myFunc1() calls myFunc2() from class A. If you want to overwrite it you have to change the visibility to protected.
I reveal iteresting behavior of php >= 5.2
class A {
protected $a = 'A';
public function __get($f){ return 'field_'.$f; }
}
class B extends A {}
class C extends A {
public function foo() {
$b = new B();
echo $b->a;
}
}
$c = new C();
$c->foo();
I expect it print field_a, but it print A.
Also. If I remove magic from A - I expect fatal error, but it still print A in php>=5.2.
If we overwrite B::$a we get another behavior - fatal error.
Why?
Is it feature or bug?
Fiddles:
- http://3v4l.org/tiOC5 - get foreign field
- http://3v4l.org/uT9PC - get fatal error
This is because of PHP's very funky rules for who can and cannot access class properties.
Read about it here:
http://php.net/manual/en/language.oop5.visibility.php
The key part is this:
The visibility of a property or method can be defined by prefixing the
declaration with the keywords public, protected or private. 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.
Emphasis mine. You are allowed to access the protected variables of any object that inherits from the same class you inherit from, even on another object. This also includes accessing the private properties of other objects of the exact same class.
Whether this is a good idea or a weird feature is up for debate, but it seems to be intended.
I believe if you declare a variable it won't use the __get magic method.
So by declaring protected $a = 'A';, you are excluding a from the __get cycle. It will skip the magic method and go straight for the actual property.
If you look at the documentation for the magic methods __get() and __set() you'll see that they only run in the case of reading/writing to an inaccessible field.
In your example $a is accessible from class C as it's defined as a protected field (inheriting/parent classes can view protected fields/methods). If you change $a to a private field it should have to call the magic method for your example.
Currently there is a class say A, which has final method say mainMethod() and it calls three methods a(), b() and c(). Now, the child class has to implement a and b so I made them abstract. But c may not be needed. How do I achieve it? I am using PHP.
Just create an empty non-abstract method and in the comments describe that it may be overwritten. E.g:
abstract class A
{
public abstract function a();
public abstract function b();
/**
* This method may be overwritten for purpose X ...
*/
public function c()
{
/* empty */
}
}
This way you can call c(), and it may or may not have any function. The inheriting class is forced to implement a() and b(), and can optionally overwrite c(). A good php editor like NetBeans or whatever is capable of code hinting, where also the comment will be shown, so the programmer is informed about the purpose of the function.
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.