Я начну с того, что скажу "Спасибо" grrbrr404. Он дал мне некоторые идеи и заставил меня начать в правильном направлении.
Решение, на котором я наконец остановился, было следующее:
function inherit_this() {
$bt = debug_backtrace();
call_user_func(array($this, get_parent_class($bt[1]['class']) . '::do_something'));
}
Это не красиво (я особенно ненавижу вызывать debug_backtrace()
для этого), но оно сохраняет контекст объекта установленным в $ this и обрабатывает случай, когда метод вызывается из метода где-то в середине иерархии объектов.
Для тех, кто посчитал мой пример непонятным и / или хотел бы знать: «Зачем вам это делать?» Я прошу прощения и приведу следующий дополнительный пример, который, надеюсь, будет более иллюстративным и ближе к исходной проблеме. Это значительно дольше, но, надеюсь, показывает, почему я забочусь о правильном хранении $ this set, а также показывает, почему я не могу жестко закодировать какое-либо конкретное имя класса или использовать $ this-> method (). Избегание бесконечных циклов всегда приоритетно для меня. : -)
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();
Я пытаюсь избежать дублирования функции "checkType ()", но все же обеспечиваю правильное поведение, даже когда иерархия становится произвольно большой.
Более элегантные решения, конечно, приветствуются.