Jan 13 2008

Emulating Ruby’s “super” in PHP

If you're new here, you may want to subscribe to my RSS feed. Thanks for visiting!

The code presented here is meant as a proof of concept and therefore does not have any error checking. Also, this is not meant to be used in production — it is merely a neat hack and should only be treated as such.

Following a discussion on FreeNode #php, where someone asked whether it was possible for a method M of class B, a subclass of class A to invoke something along the lines of Ruby’s super and call parent::M() — automagically.

It is, in fact, possible, but requires using an ugly hack — debug_backtrace().

So, here it is:


<?php
error_reporting
(E_ALL);

class A {
public function
super()
{
$bd = debug_backtrace();

$rc = new ReflectionClass($bd[1]['class']);
$pc = $rc->getParentClass()->newInstance();

call_user_func(array($pc, $bd[1]['function']));
}

public function foo() { printf(“omg, foo.\r\n”); }
}

class B extends A
{
public function
foo()
{
printf(“zomg, B-foo.\r\n”);
self::super();
}
}

$b = new B();
$b->foo();
?>

Another version, without modifying classes can be done like this:


<?php
error_reporting
(E_ALL);

function super()
{
$bd = debug_backtrace();

if (isset($bd[1]) && (isset($bd[1]['class']) || isset($bd[1]['object'])))
{
$cls = get_parent_class(isset($bd[1]['object'])?$bd[1]['object']:$bd[1]['class']);
$rm = new ReflectionMethod($cls, $bd[1]['function']);

if ($rm->isStatic())
$rm->invoke(NULL);
else {
$rc = new ReflectionClass($cls);
if (
$rc->isInstantiable())
{
$rm->invoke($rc->newInstance());
}
}
}
}

class A
{
public static function
foo() { printf(“zomg, a-foo.\r\n”); }
public function
bar() { printf(“a-bar!\r\n”); }
}

class B extends A
{
public static function
foo()
{
printf(“zomg, b-foo\r\n”);
super();
}

public function bar() { printf(“b-bar!~\r\n”); super(); }
}

$b = new B();
$b->foo();
$b->bar();
?>