It’s not a joke if you can choke on the thought of it
I was trying to explain to a friend some of the gripes that I had with PHP’s gung-ho approach to object-orientation and launched into an extended description of my biggest bugbear: class inheritance. At the time I was slightly less than sober (as is so often the case with important discussions and me) so in the aftermath I began questioning what I had said, especially as it was a while ago I last tried to push past the “bug”.
First, an explanation. If you have an abstract class you can define abstract methods which must be beefed out in any non-abstract subclasses but you can also define some standard methods (with bodies) which any subclasses will happily take with them. The easiest way to explain this is to just dump the code and go from there:
Edit: This has essentially been “solved” now and boiled down to me being a retard and not reading the documentation. The behaviour explained below is a bug with get_class when it’s called from an object’s method, to get the “real” class name of an instance, simply pass $this to the get_class method.
abstract class AbstractClass
{
public function methodOne()
{
echo get_class()."\n";
}
}
class ConcreteClass extends AbstractClass
{
public function methodTwo()
{
echo get_class()."\n";
}
}
$conc = new ConcreteClass();
$conc->methodOne();
$conc->methodTwo();
This is as simple as it gets, one abstract class, one “Concrete” class (because Concrete is cool kids) and two method invocations. Now those methods should output “ConcreteClass” twice, however, methodOne() outputs “AbstractClass” and methodTwo() outputs “ConcreteClass”. Looking at the definition for “get_class” reveals that what it returns is technically true ($conc is indeed an instance of AbstractClass) but this functionality is the opposite side of useful.
When I tried this back at the beginning of 2006, I cursed and chalked it up to PHP being PHP and got on with things. This simple case isn’t earth-shattering and can be worked around by putting a static function in AbstractClass which accepts an object and gets the class from that but that sort of roundabout route mangles an otherwise elegant solution.
It’s not hard to imagine a situation for this: an abstract “Model” class with methods for automagically building up SQL functions (pulling the name of the instantiating class as the name of the database table). No longer can you call $myModel->create() as the get_class() function would only return “Model” as the class rather than “MyModel”. The “solution” to this is, as mentioned above, to use static methods in Model, so $myModel->create() becomes Model::create($myModel) which is syntactically more convoluted and the static-charge of the method could easily cause problems further down the line.
When explaining this, I began to wonder whether I had gone space-crazy and perhaps my original implementation of this suffered from being too complex. Or perhaps it was indeed a bug and had been fixed. Neither were true. But then I wondered whether I was assuming something of PHP which perhaps didn’t hold true in other languages, perhaps I was imagining the functionality I wanted…
Ruby:
class AbstractClass
def method_one
puts self.class.to_s
end
end
class ConcreteClass > AbstractClass
def method_two
puts self.class.to_s
end
end
concrete = ConcreteClass.new
concrete.method_one
concrete.method_two
Java:
public abstract class AbstractClass
{
public void methodOne()
{
System.out.println(this.getClass().getName());
}
}
public class ConcreteClass extends AbstractClass
{
public void methodTwo()
{
System.out.println(this.getClass().getName());
}
}
public class Bimble
{
public static void main(String[] args)
{
ConcreteClass conc = new ConcreteClass();
conc.methodOne();
conc.methodTwo();
}
}
Both of these dirt-simple examples print out “ConcreteClass” twice and prove I wasn’t going crazy (at least not degenerating any further than is normal). I don’t really have much to say about the situation, PHP has always been a bastard child of OO and scripting but it’s foibles like these which build-up from irksome to tiresome.
13th April 2007
Stevie D:>
Good clarification, and I think that’s what you said at the time. However:
[steven@why cmjlib]$ php -v
PHP 4.3.9 (cgi) (built: Nov 24 2005 15:34:08)
Copyright (c) 1997-2004 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies
[steven@why cmjlib]$ cat omg.php
methodOne();
$conc->methodTwo();
[steven@why cmjlib]$ php -f omg.php
concreteclass
concreteclass
[root@lvps212-241-196-172 root]# php -v
PHP 5.1.4 (cgi-fcgi) (built: Jul 12 2006 11:24:16)
Copyright (c) 1997-2006 The PHP Group
And the same on my PHP5 box:
[root@lvps212-241-196-172 root]# php -f omg.php
ConcreteClass
ConcreteClass
:(( WFM.
15th April 2007
ChaosTangentOkay, from what you said earlier, passing $this to get_class() does actually return the correct class in both cases. So this does actually boil down to a bug/feature as according to the manual: (http://uk.php.net/manual/en/function.get-class.php) “The object parameter is optional if called from the object’s method” (since 5.0.0). You can make the argument that the get_class method is doing it’s job and returns the class you’re currently “in”, but really, what use is that?
So when you edited it for PHP4 (which needs to be called via $this) then ran it for PHP5, the “bug” disappeared. My bad for not fully exploring the options really; still, if it solves a problem though I’m more than happy to be wrong.