PHP:在继承链的每个对象的上下文中调用方法

时间:2009-04-23 14:33:53

标签: php

请查看以下代码段:

<?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 个答案:

答案 0 :(得分:3)

使用parent ::或self ::的调用被认为是静态的。这些应该在定义类的上下文中进行评估,而不是在调用对象的范围内进行评估。 PHP 5.3为单词 static 添加了一个新的含义,它可用于静态调用,如 parent和self,但将在调用类的上下文中进行评估即可。这称为延迟静态绑定。本页更多信息:

http://www.php.net/lsb

修改

经过一番思考后,我相信这种行为是完全可以接受的。首先,让我们看看为什么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()也会被执行。

所以,可能是父:: foo()在某种程度上令人困惑。它应该像(对不起,但我找不到更好的例子):

the_first_parent_in_the_inheritance_chain_which_has_a_foo_method :: foo的()

这就是你真正感兴趣的内容。为什么你想在B的上下文中调用foo()?我能想到的唯一原因是从B访问私人会员。但是,A怎么会知道私人会员B有什么?它不能。您不能在A尚未声明的A :: foo()成员中使用。除非A是抽象的并且定义了一些抽象方法。当然,A可以宣称该财产是私有的(B不能给被覆盖的成员提供比父母更低的可见度,我们的目标是B应该有私有财产)。 然后B覆盖该属性,使其成为我们想要的私有。现在,如果您的代码有效,A或C可以访问B的私有成员,尽管它不应该。这违反了规则。

是否有任何情况需要您提出的行为?

答案 1 :(得分:1)

我不是php向导,但我猜想问题是父母是静态的

答案 2 :(得分:1)

根据您的问题提示我自己的实验,似乎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();
?>

我同意这不直观,可能是一个错误。

更新: 有趣的是当你使用它来调用父代时会发生什么:

eval(get_parent_class($this). "::fn();");

答案 3 :(得分:1)

我认为你要做的事情是错的。如果你的类C定义了fn()函数,那么它永远不会在类A中调用fn(),所以这并不意味着说parent :: fn bcoz ur parent没有函数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

答案 4 :(得分:0)

你不能试着打电话给parent-&gt; fn() 我以前从未尝试过这样的东西,但那是我的第一次猜测......