PHP Bug: The magic __call method and overriding methods in child classes

This article is about a technical aspect of computer programming and since I know that many of my readers are not computer programmers, and of those that are, many do not program in PHP, you may safely ignore it unless it interests you.

As I’ve been working on a light-weight data access layer for PHP5.1+ that I hope to release as open source in the near future, I have discovered an annoying design flaw in PHP.

Support for a more object-oriented approach to programming has been greatly improved since the introduction of PHP5. PHP also offers some magic methods that can be used to simulate properties and methods without having to actually declare them individually. These are great for implementing on the fly methods and properties.

But these magic “overloading” methods don’t function exactly as expected when it comes to inherited child classes.

Here an example program to demonstrate the problem I have discovered:

<?php
class A
{
// Use the magic __call method to handle
// method calls that aren't actually defined
function __call($strMethod, $arrArgs)
{
switch ($strMethod)
{
case "getEvaluation":
return "PHP " . phpversion();
break;
default: 
$strError = 'Call to undefined method ';
$strError .= get_class($this);
$strError .= '::'.$strMethod.'()';
throw new Exception($strError);
break;
}
}
}
class B extends A
{
// Define a real method in the child class
// that overrides the magic method in
// the parent class to modify what it normally
// would return
function getEvaluation()
{
return parent::getEvaluation() . " Rocks!";
}
}
// Instantiate the parent class and call the
// method which should be handled magically by
// the __call method
$A = new A();
echo $A->getEvaluation() . "<br/>";
// Instantiate the child class and call the method
$B = new B();
echo $B->getEvaluation() . "<br/>";
?>

The output one would expect from this script would be:

PHP 5.2.4
PHP 5.2.4 Rocks!

But instead this is what you really get:

PHP 5.2.4

Fatal error: Call to undefined method A::getevaluation() in /www/test.php on line 34

The magic __call() method is never invoked when an undefined method is called on the parent class from within the child class method. Ouch!

This was apparently reported as a bug to the PHP development team back in PHP 5.1 Release Candidate 1 in October of 2005 (Bug #34739) but it was marked as “Bogus” and closed with a the explanation that ”__call() is not used when calling static methods.”

Of course, the explanation is itself “Bogus.” It doesn’t make any sense at all. In this case, the paamayim-nekudotayim (::) or scope resolution operator is being used in conjunction with the parent keyword to access the overridden method of a parent class within an instance of the child, not a static method on the class.

The code above should work as written.

There is a workaround, but I find it very unsatisfactory. You have to replace the call to parent::getEvaluation() with parent::_call(‘getEvaluation’,null) . This works, but it means that you have to know that your parent class uses the magic _call() method and which methods are magical and which are not in order to override them in a child class.

The child class should be agnostic to whether the parent class methods that it is trying to invoke are real or magical.

So I have submitted the bug to the PHP bug tracker. Let’s hope that the developers agree that this is a serious design flaw that interferes with the ability to program object oriented code in PHP.

Category: technology
Tagged: , , ,
Bookmark: link

Comments are closed.