Apr 25
The object oriented features of PHP 5 are leaps and bounds more complete than PHP 4. In fact, PHP 5 could almost pass for a true object oriented language in many ways: visibility, overloading, reflection, etc. Yet there are quite a few holes in its implementation that can make it painful to work with compared to similar languages. One well-known issue is that parent constructors are not implicitly called in derived classes; you must explicitly call parent::__construct().
I recently stumbled into a similar hole in PHP 5's OO implementation: class methods and members declared as static do not behave as one might expect with respect to inheritance. Given a base class, Animal, and two derived classes, Dog and Cat, let's assume that all Animals have a color and that it defaults to black.
<?php
class Animal
{
protected static $color = 'Black';
public static function getColor()
{
return self::$color;
}
}
class Cat extends Animal
{
protected static $color = 'Brown';
}
class Dog extends Animal
{
protected static $color = 'Grey';
}
?>
Nice and simple, short and sweet. Animal has a color that defaults to black, and we've extended Animal with a Cat that defaults to brown and a Dog that defaults to grey. All fine and good. That is, until I want to ask the Dog or Cat classes what their default colors are.
<?php echo Animal::getColor(); // Black echo Dog::getColor(); // Black (huh?) echo Cat::getColor(); // Black (what?) ?>
It turns out that static methods are called within the scope of the class where the method is defined. In this case, Animal. So, even though Dog and Cat both inherit the static method getColor(), its scope is bound to Animal. In fact, the PHP Manual attempts to explain this little 'feature':
... static method calls are resolved at compile time. When using an explicit class name the method is already identified completely and no inheritance rules apply. If the call is done by self then self is translated to the current class, that is the class the code belongs to. Here also no inheritance rules apply.
This issue has been a pain point for me lately, and I've resorted to some horrendous and clumsy workarounds to mimic the expected behavior. Until now, there has been very little standing in my way of accomplishing most anything with PHP with relative ease. This is by far one of the most annoying dealings I've had with PHP in over 8 years.
Comments
Re: Workaround?
I worked around it in two ways, neither of which are great. One of the ways is to set a member variable in the constructor. This makes the value available within the object as well as from the outside, though it is not static. Obviously not a great solution.
The second way I've worked around this is to pass an extra class name parameter into the static method calls telling the method which class it should 'act as.' Again, not a great solution.
If you're willing to put up with the lack of purity, these two options work well enough. Hopefully some future version of PHP will address this.
Exactly the same problem
My Solution
static $table;
protected function table() {
$vars = get_class_vars(get_class($this));
return $vars['table'];
}
Re: My solution
Thanks for sharing.
Possibility
this has been irking me for a while.
anybody know if you can catch "method undefined" errors with set_error_handler? no luck yet... i'm not exactly sure what kind of error that is. it may not be "catchable".
dirty, but (hypothetically):
<?php
function myErrorHandler($errno, $errstr){
$info = debug_backtrace();
// if called on a subclass of Record, divert to a call to
// Table::find with the correct $table argument
}
set_error_handler('myErrorHandler');
class Table {
public static function find($table){
// ...
}
}
// dummy
abstract class Record {}
class Person extends Record {
public static $table = 'People';
}
Person::find();
?>
that would keep the classes nice and clean…
Alternative to Tom's method
$className = get_class($this);
return eval("return $className::table;");
If the class is instantiated
propertyDefinitions = self::$propertyDefinitions;
// propertyDefinitions);
However I doubt this is the best solution. There should be the ability to do
$className::whatever without eval or something :/
No title
Re: No title
Web Developer
Re: Web Developer
Catching Undefined Method errors
However, as PHP is magical, there are various "Magic" function in PHP.
Check out the __callStatic magic function. This trick, in combination with class inheritance may provide you the functionality you are looking for.
No title
the singleton pattern
ok just to be clear, by static, i mean a data member that remembers its state across classes.
to be honest i cant remember if it holds up on inhertitance, i think you may have to use interfaces for that, not sure tho.
i am working tho a project at the momment which needs static members, so will update this post (if i remember) when i solve this.
Re: My Solution
But still nice. ^_^
No title
More info on this issue
Parent
Re: Parent
Can you explain your solution a bit more? Swapping declarations as I assume you are suggesting is syntactically impossible. And even if it was possible it overwrites the parent's initial value, which is unacceptable.
class Cat extends Animal {
parent::$color = 'Brown';
}
Further comments on this article have been disabled.




Workaround?
I'm having this exact same problem. In my case I'm subclassing an abstract actsastree for different db tables. Of course I have the table name as a static member, and of course it doesn't work.
I'm interested to know how you work around this.