FUDforum
Fast Uncompromising Discussions. FUDforum will get your users talking.

Home » Imported messages » comp.lang.php » Why is polymorphism in PHP not like other languages? Is there a bug in PHP?
Show: Today's Messages :: Unread Messages :: Show Polls :: Message Navigator
| Subscribe to topic | Bookmark topic 
Switch to threaded view of this topic Create a new topic Submit Reply
Why is polymorphism in PHP not like other languages? Is there a bug in PHP? [message #185026] Mon, 24 February 2014 14:49 Go to next message
kurtk(at)pobox(dot)com is currently offline  kurtk(at)pobox(dot)com
Messages: 10
Registered: May 2012
Karma: 0
Junior Member
add to buddy list
ignore all messages by this user
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 16:16 Go to previous messageGo to next message
kurtk(at)pobox(dot)com is currently offline  kurtk(at)pobox(dot)com
Messages: 10
Registered: May 2012
Karma: 0
Junior Member
add to buddy list
ignore all messages by this user
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] Mon, 24 February 2014 21:43 Go to previous messageGo to next message
Robert Heller is currently offline  Robert Heller
Messages: 60
Registered: December 2010
Karma: 0
Member
add to buddy list
ignore all messages by this user
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] Mon, 24 February 2014 22:25 Go to previous messageGo to next message
Thomas 'PointedEars'  is currently offline  Thomas 'PointedEars'
Messages: 701
Registered: October 2010
Karma: 0
Senior Member
add to buddy list
ignore all messages by this user
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 00:00 Go to previous messageGo to next message
Jerry Stuckle is currently offline  Jerry Stuckle
Messages: 2598
Registered: September 2010
Karma: 0
Senior Member
add to buddy list
ignore all messages by this user
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 18:00 Go to previous messageGo to next message
Daniel Pitts is currently offline  Daniel Pitts
Messages: 68
Registered: May 2012
Karma: 0
Member
add to buddy list
ignore all messages by this user
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] Tue, 25 February 2014 21:11 Go to previous messageGo to next message
Jerry Stuckle is currently offline  Jerry Stuckle
Messages: 2598
Registered: September 2010
Karma: 0
Senior Member
add to buddy list
ignore all messages by this user
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 12:53 Go to previous messageGo to next message
Daniel Pitts is currently offline  Daniel Pitts
Messages: 68
Registered: May 2012
Karma: 0
Member
add to buddy list
ignore all messages by this user
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 15:08 Go to previous messageGo to next message
kurtk(at)pobox(dot)com is currently offline  kurtk(at)pobox(dot)com
Messages: 10
Registered: May 2012
Karma: 0
Junior Member
add to buddy list
ignore all messages by this user
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 17:06 Go to previous message
Thomas 'PointedEars'  is currently offline  Thomas 'PointedEars'
Messages: 701
Registered: October 2010
Karma: 0
Senior Member
add to buddy list
ignore all messages by this user
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).
Quick Reply
Formatting Tools:   
  Switch to threaded view of this topic Create a new topic
Previous Topic: Completely stumped
Next Topic: Correlating curl resources to some other object.
Goto Forum:
  

-=] Back to Top [=-
[ Syndicate this forum (XML) ] [ RSS ]

Current Time: Tue Dec 12 18:50:00 EST 2017

Total time taken to generate the page: 0.00738 seconds