In PHP, I'm trying to reference a method defined in an object's parent class, from a method inherited from the object's parent class. Here's the code:
class base_class {
function do_something() {
print "base_class::do_something()\n";
}
function inherit_this() {
parent::do_something();
}
}
class middle_class extends base_class {
function do_something() {
print "middle_class::do_something()\n";
}
}
class top_class extends middle_class {
function do_something() {
print "top_class::do_something()\n";
$this->inherit_this();
}
}
$obj = new top_class;
$obj->do_something();
The problem is that parent::do_something() in inherit_this() tries to find the parent class of base_class, not the parent of the object's actual class, and the example above throws an error. Is there something I can write instead of parent::do_something() that would call middle_class::do_something(), and that would still work even in classes that extend (say) top_class?
I'll start by saying "Thank you" to grrbrr404. He gave me some ideas and got me started in the right direction.
The solution I finally settled on was the following:
function inherit_this() {
$bt = debug_backtrace();
call_user_func(array($this, get_parent_class($bt[1]['class']) . '::do_something'));
}
It's not pretty (I particularly hate calling debug_backtrace()
for this), but it keeps the object context set to $this, and handles the case where the method is called from a method somewhere in the middle of the object hierarchy.
For those who found my example confusing and/or wanted to know "Why would you want to do this?" I apologize, and provide the following additional example, which is hopefully more illustrative and closer to the original problem. It is considerably longer, but hopefully shows why I care about keeping $this set properly, and also shows why I can't hard-code any particular class name or use $this->method(). Avoiding infinite loops is always a priority with me. :-)
class class1_required_type { }
class class2_required_type { }
class class3_required_type { }
class class4_required_type { }
class class1 {
protected $data = array();
protected function checkType($name, $value, $requiredType) {
print "In class1::checkType()\n";
if (get_class($value) === $requiredType) {
$backtrace = debug_backtrace();
call_user_func(array($this, get_parent_class($backtrace[1]['class']) . "::mySet"), $name, $value);
} else {
throw new Exception(get_class($this) . "::mySet('" . $name . "') requires an object of type '" . $requiredType . "', but got '" . get_class($value) . "'");
}
}
function mySet($name, $value) {
print "In class1::mySet()\n";
if ($name === 'class1_field') {
$this->checkType($name, $value, 'class1_required_type');
} else {
$this->data[$name] = $value;
}
}
function dump() {
foreach ($this->data as $key => $value) {
print "$key: " . get_class($value) . "\n";
}
}
}
class class2 extends class1 {
function mySet($name, $value) {
print "In class2::mySet()\n";
if ($name === 'class2_field') {
$this->checkType($name, $value, 'class2_required_type');
} else {
parent::mySet($name, $value);
}
}
}
class class3 extends class2 {
function mySet($name, $value) {
print "In class3::mySet()\n";
if ($name === 'class3_field') {
$this->checkType($name, $value, 'class3_required_type');
} else {
parent::mySet($name, $value);
}
}
}
class class4 extends class3 {
function mySet($name, $value) {
print "In class4::mySet()\n";
if ($name === 'class4_field') {
$this->checkType($name, $value, 'class4_required_type');
} else {
parent::mySet($name, $value);
}
}
}
$obj = new class4;
$obj->mySet('class3_field', new class3_required_type);
$obj->dump();
I'm trying to avoid duplication of the "checkType()" function, yet still provide correct behavior even when the hierarchy gets arbitrarily large.
More elegant solutions are, of course, most welcome.