PHP: вызов метода в контексте каждого объекта цепочки наследования - PullRequest
2 голосов
/ 23 апреля 2009

Посмотрите на следующий фрагмент кода:

<?php

class A {
        function fn() {
                print 'Context: Class:' . get_class($this) 
                        . ' Parent:' . get_parent_class($this) . "\n";
                if(get_parent_class($this)) {
                        parent::fn();
                }
        }
}

class B extends A { }
class C extends B { }

$a = new A();
$c = new C();

$a->fn();

print "\n";

$c->fn();

?>

Запустив его, вы получите следующий вывод:

Context: Class:A Parent:

Context: Class:C Parent:B

Fatal error: Cannot access parent:: when current class scope has no parent in /home/andrei/test/test.php on line 10

Я считаю, что это должно быть примерно так:

Context: Class:A Parent:

Context: Class:C Parent:B
Context: Class:B Parent:A
Context: Class:A Parent:

Что ты думаешь? Если get_parent_class($this) возвращает не ложное значение, мы должны безопасно предположить, что parent :: определен? В каком контексте класса называется fn()?

Ответы [ 5 ]

3 голосов
/ 23 апреля 2009

Вызовы, использующие parent :: или self ::, считаются статическими. Предполагается, что они будут оцениваться в контексте определяющего класса, а не в области вызывающего объекта. PHP 5.3 добавляет новое значение к слову static , которое будет доступно для статических вызовов , таких как parent и self, но будет оцениваться в контексте вызывающего класса . Это называется Позднее статическое связывание . Больше информации на этой странице:

http://www.php.net/lsb

Edit:

После некоторых размышлений я считаю, что это поведение вполне приемлемо. Во-первых, давайте посмотрим, почему A хочет, чтобы его метод foo () вызывал метод foo () своего родителя, учитывая, что у A нет родителя. Потому что он хочет, чтобы метод всегда выполнялся независимо от реализации дочерних элементов. Если так, то есть и другие решения, хотя и не такие приятные:

class A
{
    final public function __construct()
    {
        echo __METHOD__;

        $this->foo();

        $init = array($this, 'init');
        $args = func_get_args();

        call_user_func_array($init, $args);
    }

    // init would be used as a pseudo-constructor in children
    public function init()
    {}

    final private function foo()
    {
        echo __METHOD__;
    }
}

class B extends A
{}

class C extends B
{}

$c = new C;

Если вы пытались выполнить каждый метод foo () в цепочке, то это было ожидаемое поведение. Если бы существовала функция B :: foo (), то она была бы выполнена, и если бы она содержала вызов parent :: foo (), то A :: foo () также была бы выполнена.

Так что, вероятно, parent :: foo () как-то сбивает с толку. Это должно быть прочитано как (извините, но я не мог найти лучший пример):

the_first_parent_in_the_inheritance_chain_which_has_a_foo_method :: Foo ()

Это то, что вас на самом деле интересует. Почему вы хотите вызывать foo () в контексте B? Единственная причина, по которой я могу придумать, - получить доступ к частному члену из B. Но тогда как A узнает, что есть у частных членов B? Не может Вы не можете использовать члены A :: foo (), которые A не объявил. Если A не является абстрактным и определяет некоторые абстрактные методы. Конечно, A может объявить это свойство как личное (B не может дать переопределенному члену более низкую видимость, чем в родительском, и наша цель состоит в том, чтобы B имел приватное). Затем B переопределяет это свойство, делая его приватным, как мы хотим. Теперь, если бы ваш код работал, A или C имели бы доступ к приватному члену B, хотя это не должно было быть. Это нарушает правило.

Есть ли ситуации, в которых вам нужно поведение, о котором вы спрашивали?

1 голос
/ 26 августа 2009

Я думаю, что вы пытаетесь сделать неправильно. Если в вашем классе C определена функция fn (), то он никогда не вызовет функцию fn () в классе A, поэтому нет смысла говорить, что parent :: fn bcoz ваш родительский объект не имеет функции с именем fn, поэтому причина его отказа от fn () определено в A.

Но если вы хотите вызвать какую-то другую функцию в текущем контексте объекта , используйте $ this вместо parent . Код Herez для этого.

<?php

class A {
        function fn() {
                print 'Context: Function:'.__FUNCTION__.' Class:' . get_class($this)
                        . ' Parent:' . get_parent_class($this) . "\n";
                if(get_parent_class($this)) {
                        $this->fB();
                }
        }
}

class B extends A { }
class C extends B {
        function fB(){
                print 'Context: Function:'.__FUNCTION__.' Class:' . get_class($this)
                        . ' Parent:' . get_parent_class($this) . "\n";
        }
}

$a = new A();
$c = new C();

$a->fn();

print "\n";

$c->fn();

?>

**out put**
Context: Function:fn Class:A Parent:

Context: Function:fn Class:C Parent:B
Context: Function:fB Class:C Parent:B
1 голос
/ 23 апреля 2009

Из моих собственных экспериментов, вызванных вашим вопросом, кажется, что parent :: разрешен как родительский класс класса, в котором находится оператор. Измените ваш тестовый пример следующим образом, чтобы увидеть это поведение:

<?php
class Z {
        function fn() {
                print "This is Z\n";
        }
}

class A extends Z {
        function fn() {
                print 'Context: Class:' . get_class($this)
                        . ' Parent:' . get_parent_class($this) . "\n";
                if(get_parent_class($this)) {
                        parent::fn();
                }
        }
}
class B extends A { }
class C extends B { }
$a = new A();
$c = new C();
$a->fn();
print "\n";
$c->fn();
?>

Я согласен, что это не интуитивно понятно и может быть ошибкой.

UPDATE: Интересно, что происходит, когда вы используете это для вызова родителя вместо этого:

eval(get_parent_class($this). "::fn();");
1 голос
/ 23 апреля 2009

Я не волшебник php, но думаю, что проблема в том, что родительский статический

0 голосов
/ 23 апреля 2009

Не можете ли вы попытаться вызвать parent-> fn () Я никогда не пробовал подобные вещи раньше, но это было бы моим первым предположением ..

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...