Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185026] |
Mon, 24 February 2014 19:49 |
kurtk(at)pobox(dot)com
Messages: 10 Registered: May 2012
Karma: 0
|
Junior Member |
|
|
Why can I (quite surprisingly) call a member function that only exists in a derived class through a reference to a base class
I stumbled upon this unexpected behaviour when implementing the Observer pattern in PHP.
The observer is class Quackologist
class Quackologist implements \SplObserver {
public function update(\SplSubject $subject)
{
echo ". $subject->whoami() . " just quacked.\n\n";
}
}
who observes various types of ducks that implement both \SplSubject (using the Observable trait below) and another interface, Quackable.
interface Quackable {
function quack();
}
trait Observable {
private $observers = array();
public function attach ( \SplObserver $observer )
{
$this->observers[spl_object_hash($observer)] = $observer;
}
public function detach( \SplObserver $observer )
{
unset($this->observers[spl_object_hash($observer)]);
}
public function notify()
{
foreach($this->observers as $observer) {
$observer->update($this);
}
}
}
class RedheadDuck implements Quackable, \SplSubject {
use Observable;
public function __construct() {}
public function quack()
{
echo "Quack";
$this->notify();
}
public function whoami() { return "Redhead Duck"; }
}
class RubberDuck implements Quackable, \SplSubject {
use Observable;
public function __construct() {}
public function quack()
{
echo "Squeak";
$this->notify();
}
public function whoami() { return "Rubber Duck"; }
}
The main logic is:
$array_of_duck = array();
$array_of_ducks[] = new RedheadDuck();
$array_of_ducks[] = new RubberDuck();
$quackologist = new Quackologist();
foreach ($array_of_ducks as $duck) {
$duck->attach($quackologist);
}
foreach ($array_of_ducks as $duck) {
$duck->quack();
}
The output is
Quack. Redhead Duck just quacked.
Squeak. Rubber Duck just quacked.
Calling $duck->quack() calls $this->notify(), which calls $observer->update($this). $this is a derived duck class; however, the update method of Quackologist takes \SplSubject, which does not contain the whoamI() method. So I expected a runtime error message similar to what you get with this
simple example.
class Base {}
class Derived extends Base {
public function whoami() { echo "I am Derived\n"; }
}
function test(Base $b)
{
$b->whoami();
}
$b = new Base();
$d = new Derived();
test($b);
test($d);
This expectedly results in the error message **Call to undefined method Base::whoami() in /home/kurt/public_html/spl/observer/test.php**.
If you try to implement similar Observer Pattern code in C++, you will get the expected compile error on the call to whoami() in Quackologist::update(Subject&)
class Subject;
class Observer { // abstract base
public:
virtual void update(Subject&) = 0;
};
class ObservableBase : public Subject { // Adds the virtual method whoami() not in Subject
public:
virtual string whoami() = 0;
};
// mixin: Reusable observer code
class Observable : public ObservableBase {
map<Observer *, Observer *> assoc_array;
public:
void registerObserver(Observer& obs);
void unregisterObserver(Observer& obs);
void notifyObservers();
};
void Observable::notifyObservers()
{
for ( auto current : assoc_array) {
(current.second)->update(*this);
}
}
class Subject { // abstract base
public:
virtual void notifyObservers() = 0;
virtual void registerObserver(Observer& obs) = 0;
virtual void unregisterObserver(Observer& obs) = 0;
};
class Quackologist : public Observer {
public:
void update(Subject& subject)
{
// This line below will not compile, which is expected.
cout << subject.whoami() << "\n" << endl;
}
};
Quackologist::update(Subject&) gives the expected compile error **error: no member named 'whoami' in 'Subject'**.
Why does in the simple Base/Derived PHP example give the expected runtime error but not the first example?
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185027 is a reply to message #185026] |
Mon, 24 February 2014 21:16 |
kurtk(at)pobox(dot)com
Messages: 10 Registered: May 2012
Karma: 0
|
Junior Member |
|
|
Evidently, PHP just works differently. Even though Quackologist::update(\SplSubject) takes a reference to \SplSubject, this fine provide you pass (at runtime) a class derived from SplSubject that implements whoami(). Only if were to pass an SplSubject instance that does not implement whoami() will you get an runtime error.
C++, on the other hand, would complain
error: no member named 'whoami' in 'Base'
because Base has no whoami() method.
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185045 is a reply to message #185027] |
Tue, 25 February 2014 02:43 |
Robert Heller
Messages: 60 Registered: December 2010
Karma: 0
|
Member |
|
|
Note a compiled language (like C++ or Java) need up-front knowledge about
things, while *intrepreted* languages defer resolving things until runtime.
Partitularly with languages where classes can be defined 'on the fly'.
At Mon, 24 Feb 2014 13:16:06 -0800 (PST) "kurtk(at)pobox(dot)com" <kurtk(at)pobox(dot)com> wrote:
>
> Evidently, PHP just works differently. Even though Quackologist::update(\Sp=
> lSubject) takes a reference to \SplSubject, this fine provide you pass (at =
> runtime) a class derived from SplSubject that implements whoami(). Only if =
> were to pass an SplSubject instance that does not implement whoami() will y=
> ou get an runtime error.=20
>
> C++, on the other hand, would complain
>
> error: no member named 'whoami' in 'Base'
>
> because Base has no whoami() method.
>
--
Robert Heller -- 978-544-6933 / heller(at)deepsoft(dot)com
Deepwoods Software -- http://www.deepsoft.com/
() ascii ribbon campaign -- against html e-mail
/\ www.asciiribbon.org -- against proprietary attachments
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185051 is a reply to message #185045] |
Tue, 25 February 2014 03:25 |
Thomas 'PointedEars'
Messages: 701 Registered: October 2010
Karma: 0
|
Senior Member |
|
|
Robert Heller wrote:
> Note a compiled language (like C++ or Java) need up-front knowledge about
> things, while *intrepreted* languages defer resolving things until
> runtime. Partitularly with languages where classes can be defined 'on the
> fly'.
Note: PHP source code is compiled to bytecode, as is code in many other
programming languages.
Get rid of the common misconception of “compiled language” vs. “interpreted
language”, and stop spreading this nonsense.
The relevant difference here, if any, is between static and dynamic
type-checking. (Get rid of “loosely typed” vs. “strictly typed” as well.)
PointedEars
--
realism: HTML 4.01 Strict
evangelism: XHTML 1.0 Strict
madness: XHTML 1.1 as application/xhtml+xml
-- Bjoern Hoehrmann
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185057 is a reply to message #185051] |
Tue, 25 February 2014 05:00 |
Jerry Stuckle
Messages: 2598 Registered: September 2010
Karma: 0
|
Senior Member |
|
|
On 2/24/2014 10:25 PM, Thomas 'PointedEars' Lahn wrote:
> Robert Heller wrote:
>
>> Note a compiled language (like C++ or Java) need up-front knowledge about
>> things, while *intrepreted* languages defer resolving things until
>> runtime. Partitularly with languages where classes can be defined 'on the
>> fly'.
>
> Note: PHP source code is compiled to bytecode, as is code in many other
> programming languages.
>
> Get rid of the common misconception of “compiled language” vs. “interpreted
> language”, and stop spreading this nonsense.
>
> The relevant difference here, if any, is between static and dynamic
> type-checking. (Get rid of “loosely typed” vs. “strictly typed” as well.)
>
>
> PointedEars
>
Bytecode is not the same as machine code. And interpreting on the fly
is not the same as compiling once.
But we also know you don't understand the difference - which is why you
claim they are the same.
And we also know you're just being your usual pedantic self.
--
==================
Remove the "x" from my email address
Jerry Stuckle
jstucklex(at)attglobal(dot)net
==================
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185087 is a reply to message #185057] |
Tue, 25 February 2014 23:00 |
Daniel Pitts
Messages: 68 Registered: May 2012
Karma: 0
|
Member |
|
|
On 2/24/14 9:00 PM, Jerry Stuckle wrote:
> On 2/24/2014 10:25 PM, Thomas 'PointedEars' Lahn wrote:
>> Robert Heller wrote:
>>
>>> Note a compiled language (like C++ or Java) need up-front knowledge
>>> about
>>> things, while *intrepreted* languages defer resolving things until
>>> runtime. Partitularly with languages where classes can be defined 'on
>>> the
>>> fly'.
>>
>> Note: PHP source code is compiled to bytecode, as is code in many other
>> programming languages.
>>
>> Get rid of the common misconception of “compiled language” vs.
>> “interpreted
>> language”, and stop spreading this nonsense.
>>
>> The relevant difference here, if any, is between static and dynamic
>> type-checking. (Get rid of “loosely typed” vs. “strictly typed” as
>> well.)
>>
>>
>> PointedEars
>>
>
> Bytecode is not the same as machine code. And interpreting on the fly
> is not the same as compiling once.
>
> But we also know you don't understand the difference - which is why you
> claim they are the same.
>
> And we also know you're just being your usual pedantic self.
This has nothing to do with whether a language is compiled or
interpreted, and everything to do with the type semantics of the language.
The OP example is along the lines of: Duck Typing.
http://en.wikipedia.org/wiki/Type_system#Duck_typing
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185095 is a reply to message #185087] |
Wed, 26 February 2014 02:11 |
Jerry Stuckle
Messages: 2598 Registered: September 2010
Karma: 0
|
Senior Member |
|
|
On 2/25/2014 6:00 PM, Daniel Pitts wrote:
> On 2/24/14 9:00 PM, Jerry Stuckle wrote:
>> On 2/24/2014 10:25 PM, Thomas 'PointedEars' Lahn wrote:
>>> Robert Heller wrote:
>>>
>>>> Note a compiled language (like C++ or Java) need up-front knowledge
>>>> about
>>>> things, while *intrepreted* languages defer resolving things until
>>>> runtime. Partitularly with languages where classes can be defined 'on
>>>> the
>>>> fly'.
>>>
>>> Note: PHP source code is compiled to bytecode, as is code in many other
>>> programming languages.
>>>
>>> Get rid of the common misconception of “compiled language” vs.
>>> “interpreted
>>> language”, and stop spreading this nonsense.
>>>
>>> The relevant difference here, if any, is between static and dynamic
>>> type-checking. (Get rid of “loosely typed” vs. “strictly typed” as
>>> well.)
>>>
>>>
>>> PointedEars
>>>
>>
>> Bytecode is not the same as machine code. And interpreting on the fly
>> is not the same as compiling once.
>>
>> But we also know you don't understand the difference - which is why you
>> claim they are the same.
>>
>> And we also know you're just being your usual pedantic self.
>
> This has nothing to do with whether a language is compiled or
> interpreted, and everything to do with the type semantics of the language.
>
> The OP example is along the lines of: Duck Typing.
>
> http://en.wikipedia.org/wiki/Type_system#Duck_typing
>
>
You missed the entire discussion.
--
==================
Remove the "x" from my email address
Jerry Stuckle
jstucklex(at)attglobal(dot)net
==================
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185102 is a reply to message #185095] |
Wed, 26 February 2014 17:53 |
Daniel Pitts
Messages: 68 Registered: May 2012
Karma: 0
|
Member |
|
|
On 2/25/14 6:11 PM, Jerry Stuckle wrote:
> On 2/25/2014 6:00 PM, Daniel Pitts wrote:
>> On 2/24/14 9:00 PM, Jerry Stuckle wrote:
>>> On 2/24/2014 10:25 PM, Thomas 'PointedEars' Lahn wrote:
>>>> Robert Heller wrote:
>>>>
>>>> > Note a compiled language (like C++ or Java) need up-front knowledge
>>>> > about
>>>> > things, while *intrepreted* languages defer resolving things until
>>>> > runtime. Partitularly with languages where classes can be defined 'on
>>>> > the
>>>> > fly'.
>>>>
>>>> Note: PHP source code is compiled to bytecode, as is code in many other
>>>> programming languages.
>>>>
>>>> Get rid of the common misconception of “compiled language” vs.
>>>> “interpreted
>>>> language”, and stop spreading this nonsense.
>>>>
>>>> The relevant difference here, if any, is between static and dynamic
>>>> type-checking. (Get rid of “loosely typed” vs. “strictly typed” as
>>>> well.)
>>>>
>>>>
>>>> PointedEars
>>>>
>>>
>>> Bytecode is not the same as machine code. And interpreting on the fly
>>> is not the same as compiling once.
>>>
>>> But we also know you don't understand the difference - which is why you
>>> claim they are the same.
>>>
>>> And we also know you're just being your usual pedantic self.
>>
>> This has nothing to do with whether a language is compiled or
>> interpreted, and everything to do with the type semantics of the
>> language.
>>
>> The OP example is along the lines of: Duck Typing.
>>
>> http://en.wikipedia.org/wiki/Type_system#Duck_typing
>>
>>
>
> You missed the entire discussion.
Not all of it, though I will admit that I replied why suffering from a
massive migraine, and I might have been hallucinating a bit ;-).
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185128 is a reply to message #185102] |
Thu, 27 February 2014 20:08 |
kurtk(at)pobox(dot)com
Messages: 10 Registered: May 2012
Karma: 0
|
Junior Member |
|
|
Thanks for all the replies. I could probably have simplified the example, like this
<?php
class Base {}
class DerivedCommonBase extends Base {
public function whoami() { echo "I am DerivedCommonBase\n"; }
}
class Derived1 extends Base {
public function whoami() { echo "I am Derived1\n"; }
}
class Derived2 extends Base {
public function whoami() { echo "I am Derived2\n"; }
}
// Base has not whoami() method...
function test(Base $b) { $b->whoami(); }
$b = new Base();
$d1 = new Derived1();
$d2 = new Derived2();
$a = array();
$a[] = $d1;
$a[] = $d2;
foreach($a as $x) {
echo test($x);
}
test($d1);
test($d2);
test($b); // <--...this is detected at run time
However, I did learn that in PHP you can pass a derived class to a method that takes a base class and then invoke a method that does not exist in the base class. Derived1 is a DerivedCommonBase is a Base. Something with Derived2. But somehow PHP knows the final subtype $b within method test(Base $b)..
|
|
|
Re: Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185132 is a reply to message #185128] |
Thu, 27 February 2014 22:06 |
Thomas 'PointedEars'
Messages: 701 Registered: October 2010
Karma: 0
|
Senior Member |
|
|
kurtk(at)pobox(dot)com wrote:
> Thanks for all the replies. I could probably have simplified the example,
> like this
>
> <?php
> class Base {}
>
> class DerivedCommonBase extends Base {
> public function whoami() { echo "I am DerivedCommonBase\n"; }
> }
>
> class Derived1 extends Base {
> public function whoami() { echo "I am Derived1\n"; }
> }
>
> class Derived2 extends Base {
> public function whoami() { echo "I am Derived2\n"; }
> }
>
> // Base has not whoami() method...
> function test(Base $b) { $b->whoami(); }
>
> $b = new Base();
> $d1 = new Derived1();
> $d2 = new Derived2();
>
> $a = array();
>
> $a[] = $d1;
> $a[] = $d2;
>
> foreach($a as $x) {
> echo test($x);
> }
>
> test($d1);
> test($d2);
> test($b); // <--...this is detected at run time
What do you mean by that?
> However, I did learn that in PHP you can pass a derived class to a method
> that takes a base class and then invoke a method that does not exist in
> the base class. Derived1 is a DerivedCommonBase is a Base. Something with
> Derived2. But somehow PHP knows the final subtype $b within method
> test(Base $b).
Nonsense. As expected, the code that you posted *fails to execute properly*
reproducibly here:
,----
| $ php -r 'class Base {}
| >
| > class DerivedCommonBase extends Base {
| > public function whoami() { echo "I am DerivedCommonBase\n"; }
| > }
| >
| > class Derived1 extends Base {
| > public function whoami() { echo "I am Derived1\n"; }
| > }
| >
| > class Derived2 extends Base {
| > public function whoami() { echo "I am Derived2\n"; }
| > }
| >
| > // Base has not whoami() method...
| > function test(Base $b) { $b->whoami(); }
| >
| > $b = new Base();
| > $d1 = new Derived1();
| > $d2 = new Derived2();
| >
| > $a = array();
| >
| > $a[] = $d1;
| > $a[] = $d2;
| >
| > foreach($a as $x) {
| > echo test($x);
| > }
| >
| > test($d1);
| > test($d2);
| > test($b);'
| I am Derived1
| I am Derived2
| I am Derived1
| I am Derived2
| PHP Fatal error: Call to undefined method Base::whoami() in Command line
code on line 16
| PHP Stack trace:
| PHP 1. {main}() Command line code:0
| PHP 2. test() Command line code:33
|
| $ php -v
| PHP 5.5.9-1 (cli) (built: Feb 8 2014 00:52:52)
| Copyright (c) 1997-2014 The PHP Group
| Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
| with XCache v3.1.0, Copyright (c) 2005-2013, by mOo
| with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend
Technologies
| with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans
| with XCache Optimizer v3.1.0, Copyright (c) 2005-2013, by mOo
| with XCache Cacher v3.1.0, Copyright (c) 2005-2013, by mOo
| with XCache Coverager v3.1.0, Copyright (c) 2005-2013, by mOo
`----
You are not passing a class, and the method does not take a class (nor would
it in C++ where you appear to be coming from). You are passing a reference
to an object (not to be confused with a reference to a variable or array
element), and that is what the function takes.
AFAIK, C++ uses static type-checking: your C++ program does not compile if
the call cannot be matched to a template. PHP uses dynamic type-checking
instead: the type of a symbol is determined at runtime (after compilation,
upon execution of the statement).
The class name in the function/method declaration/definition is a *type
hint*. It means that the object whose reference is passed to the
function/method must have be an instance of this class or its class must
have the specified class as superclass, or it must implement the specified
interface if the identifier is that of an interface:
<http://php.net/manual/en/language.oop5.typehinting.php>
Obviously, this criterion is met here; however, “$b” refers to an instance
of “Base” and those objects do not have a whoami() method; hence the error
message. Whatever you may have observed instead has nothing to do with the
code that you posted.
PointedEars
--
When all you know is jQuery, every problem looks $(olvable).
|
|
|