Forum Moderators: coopster

Message Too Old, No Replies

Class inheritance works both ways - both up and down the chain!

(Something I hadn't realised!)

         

penders

2:50 am on Mar 28, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



May be this seems obvious, but it (reverse inheritance) wasn't to me... in fact I discovered it by accident, expecting to get an error.

Normal class inheritance:
A method in the extended/child class can reference a property declared as protected in the base/parent class.

But the reverse is also true (to my surprise):
A method in the base/parent class can reference a property declared as protected in the extended/child class!

TBH, this reverse inheritance, kind of goes against the grain in terms of good OOP IMHO. Since you can easily end up having a base/parent class that is dependent on the extended class - effectively making the base class abstract. Although you could account for this, checking for the existence of any extended properties (or methods) first, so that the base class can be instantiated independently of any extended classes - if reqd.

Any comments appreciated. Is this called reverse inheritance? Can you do this in more strict OO languages (eg. Java)?

eelixduppy

4:12 am on Mar 28, 2010 (gmt 0)



By definition of inheritance this can't be true. Can you give me an example that shows this property you describe?

penders

2:15 pm on Mar 28, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



An example... (tested on PHP 5.2.6)

class MyBaseClass { 
protected $_myBaseProperty = null;
public function __construct() {
$this->_myBaseProperty = 'Hello';
}
// Outputs 'Hello World' when called from an instance of MyExtendedClass
// - although I was expecting an error since isn't inheritance in reverse?!
// (Certainly an error if called from an instance of MyBaseClass)
public function myBaseMethod() {
echo $this->_myBaseProperty.' '.$this->_myExtendedProperty;
}
}
class MyExtendedClass extends MyBaseClass {
protected $_myExtendedProperty = null;
public function __construct() {
parent::__construct();
$this->_myExtendedProperty = 'World';
}
// Outputs 'Hello World' as expected
public function myExtendedMethod() {
echo $this->_myBaseProperty.' '.$this->_myExtendedProperty;
}
}
$MyExtendedInstance = new MyExtendedClass;
// Outputs 'Hello World' - although I was expecting an error!
$MyExtendedInstance->myBaseMethod();


I would have expected that _myExtendedProperty would need to be declared as protected in the base class, not the extended class as in this case.

myBaseMethod() is effectively abstract (since it is now dependent on being extended), although it is not declared as abstract.

eelixduppy

2:35 pm on Mar 28, 2010 (gmt 0)



Hmm that is interesting. I haven't tested it myself, but I take your word for it.

If I had to guess I'd say that PHP is just throwing all of the variables from both classes within the same scope, and since _myExtendedProperty was declared within the scope before the method call then it was valid.

This, IMO, goes against what actual inheritance is and is an example of poor programming. There should never be a case when a class is referencing variables outside its declared scope. Sure there are times when global variables may be helpful, but it is still poor programming practice.

Thanks for sharing this find, though. I'm going to play around with it a little bit later. :)

penders

4:56 pm on Mar 28, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



This, IMO, goes against what actual inheritance is and is an example of poor programming. There should never be a case when a class is referencing variables outside its declared scope. Sure there are times when global variables may be helpful, but it is still poor programming practice.


Yes, I would tend to agree. However, I'm struggling to get away from the fact that it would really help my current project. It would avoid me having to reference that global variable and would avoid me having to (unnecessarily) declare a property in the base class. The method to set this property is in the extended class.

Basically, I have a base class that can be used independently if reqd in other projects. However, the same class can also be incorporated into my 'framework' as a 'plugin'. The base class is made into a 'plugin' by extending it with a few additional plugin properties/methods (implementing a plugin interface). Occasionally, however, a method in the base class should behave slightly differently if it is part of the 'framework' (perhaps it should check a property in another plugin for instance). To make the plugin (or base class) aware of the framework, I pass a reference to the framework to each plugin (that requires it). I was thinking I was going to have to store this reference in the base class, for it to be in scope of the method in the base class, however, it seems this reference can be isolated in the extended plugin class and the base class can still see it.

So, I end up doing something like this (following the example above):
// 'Hello World' when called from an instance of MyExtendedClass 
// Or just 'Hello' when called from an instance of MyBaseClass
public function myBaseMethod() {
if (property_exists($this,'_myExtendedProperty')) {
echo $this->_myBaseProperty.' '.$this->_myExtendedProperty;
} else {
echo $this->_myBaseProperty.' (_myExtendedProperty does not exist)';
}
}


This kind of works OK for me and feels quite tidy - apart from the fact that it does go against the grain. But then may be it should have been implemented differently from the off!? Hhhmmm...

penders

8:26 am on Mar 29, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I had only tested properties above, but the same does apply to class methods as well... The base/parent class can call methods declared as protected in the extended/child class!

PCInk

9:02 am on Mar 29, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



class MyExtendedClass extends MyBaseClass


In C++ (I assume other languages are similar), extending a class makes all externally available variables and functions of both classes available through MyExtendedClass.

As an example (pseudocode):

class MyBaseClass
{
public function a { return 1; }
}

class MyExtendedClass extends MyBaseClass
{
public function b { return 2; }
}


MyBaseClass has one funtion, called a. MyExtendedClass has two functions, a and b. Is that not correct?

I understand that by extending, you are basically taking a copy of the class into the new class, thus all variables between the two are shared.

In your example, therefore, MyExtendedClass should have access to the variables you declare in MyBaseClass and MyBaseClass should have access to variables inside MyExtended class, but only when called through MyExtendedClass.

Does this work?:

$MyExtendedInstance = new MyExtendedClass; // MyExtendedClass
$MyExtendedInstance->myBaseMethod(); // should run fine as variables are shared

$MyExtendedInstance = new MyBaseClass; // Changed to mybaseclass
$MyExtendedInstance->myBaseMethod(); // should cause error because $this->_myExtendedProperty doesn't exist in MyBaseClass?

I think part of the issue is that is it a language parsed line by line, unlike C++. Using the same code in C++ should cause a compilation error.

penders

12:47 pm on Mar 29, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



MyBaseClass has one funtion, called a. MyExtendedClass has two functions, a and b. Is that not correct?


Yes, I agree. Normal class inheritance.

I understand that by extending, you are basically taking a copy of the class into the new class, thus all variables between the two are shared.


I think taking a 'copy' is a bit over simplified, I would have said more 'merged' - but yes, a similar idea.

However, MyBaseClass and MyExtendedClass are still separate in some respects, as you can override a method in MyExtendedClass and still call the (same named) method in MyBaseClass:

class MyBaseClass {  
public function a() { return 1; }
}
class MyExtendedClass extends MyBaseClass {
public function a() {
parent::a(); // Calls MyBaseClass::a() but not statically
return 3;
}
public function b() { return 2; }
}


However, I don't think you can call MyExtendedClass::a() from MyBaseClass (unless may be you call it statically? But this method isn't defined as static. And the same base class could be extended by many other classes. You could perhaps use late static binding in PHP 5.3+, but that's still a static call AFAIK?). Yet you can call b() from MyBaseClass::a() ... but yes, only through an instance of MyExtendedClass.

Does this work?:

$MyExtendedInstance = new MyExtendedClass; // MyExtendedClass
$MyExtendedInstance->myBaseMethod(); // should run fine as variables are shared

$MyExtendedInstance = new MyBaseClass; // Changed to mybaseclass
$MyExtendedInstance->myBaseMethod(); // should cause error because $this->_myExtendedProperty doesn't exist in MyBaseClass?


Yes, I agree with your output.

In your example, therefore, MyExtendedClass should have access to the variables you declare in MyBaseClass and MyBaseClass should have access to variables inside MyExtended class, but only when called through MyExtendedClass.


Yes, in some ways I do agree and understand this. Although I was a bit surprised that the 2nd part (in bold) would be OK since calling $this->method() follows the inheritance chain (child->parent) and this is the reverse of that (parent->child). So inheritance does go both ways afterall!? Although relying on a parent->child relationship is generally poor OOP as eelixduppy suggests. And I imagine would be tricky for a compiler to resolve, as I think you suggest in you last line (PCInk)?

My other concern was that this was a PHP-inheritance quirk? But if it really is the generally accepted behaviour of inheritance and implemented in C++, Java etc. then I'm better with that.

PHP is a very flexible language, but that flexibility does lend itself to bad programming practices at times IMO.

Are you saying the following (principle) would work OK in C++ as well? Or a compilation error?
$MyExtendedInstance = new MyExtendedClass; // MyExtendedClass
$MyExtendedInstance->myBaseMethod(); // should run fine as variables are shared


Many thanks for your input.

coopster

8:53 am on Mar 31, 2010 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



In some of the stricter compiler languages I have programmed this would not compile. It would error out until you corrected the code. I cannot speak for C++ but it should likely error, just like you are receiving in PHP. As mentioned, PHP is compiled at runtime. The property was never declared and therefore it is not defined so it defaults to NULL with NOTICE errors, as you mentioned. For others that want to test this, add one line to the base method to var_dump the property and add a call the base class:
public function myBaseMethod() { 
print '<pre>'; var_dump($this->_myExtendedProperty); print '</pre>';
echo $this->_myBaseProperty.' '.$this->_myExtendedProperty;
}
.
.
.
$MyBaseInstance = new MyBaseClass();
$MyBaseInstance->myBaseMethod();

There is no inheritance, but there are errors ...
Notice: Undefined property: MyBaseClass::$_myExtendedProperty in test.php on line 12 
NULL
Notice: Undefined property: MyBaseClass::$_myExtendedProperty in test.php on line 13
Hello

Properties used in the base class should be declared in the base class. If not, then they could be passed to the function as an argument rather than attempting to use properties defined in child classes. As it exists, the property has a null value from the beginning and casts to string during type juggling when it gets defined in the extended class.