17

有时我需要执行祖父方法(即绕过父方法),我知道这是代码异味,但有时我无法更改其他类(框架、库等)。

在 PHP 中,我们可以这样做:

call_user_func(array(get_parent_class(get_parent_class($childObject)), 'grandParentMethod'));

问题是,如果您启用了 E_STRICT 错误,您将收到如下错误:

严格标准:非静态方法 GrandParent::grandParentMethod() 不应在 ...

我只找到了一个解决方案(不删除 E_STRICT),它只是添加@来抑制错误。

但这真的很难看,有人知道更好的解决方案吗?

谢谢 !PS:我无法实例化一个新对象,例如:

$grandparent = get_parent_class(get_parent_class($son));
$gp= new $grandparent;
$gp->grandParentMethod

因为我需要在 $son 的上下文中调用我的祖父母方法。

4

4 回答 4

69

您可以直接按名称调用祖父母(不需要反射,也不需要call_user_func)。

class Base {
    protected function getFoo() {
        return 'Base';
    }
}

class Child extends Base {
    protected function getFoo() {
        return parent::getFoo() . ' Child';
    }
}

class Grandchild extends Child {
    protected function getFoo() {
        return Base::getFoo() . ' Grandchild';
    }
}

Base::getFoo调用可能看起来像一个静态调用(由于冒号::语法),但事实并非如此。就像parent::不是静态的一样。

从类中的继承链调用方法将正确绑定$this值,将其作为常规方法调用,遵守可见性规则(例如受保护),并且不违反任何形式!

起初这可能看起来有点奇怪,但是,这是在 PHP 中执行此操作的方式。

于 2015-07-01T00:26:52.857 回答
2

您可以使用ReflectionMethod->invoke()

例子:

<?php
class Grandpa {
    protected $age = 'very old';
    public function sayMyAge() {
        return 'sayMyAge() in Grandpa should be very old. ' . 
                  'My age is: ' . $this->age;
    }
}

class Pa extends Grandpa {
    protected $age = 'less old';
    public function sayMyAge() {
        return 'sayMyAge() in Pa should be less old. ' .
                  'My age is: ' . $this->age;
    }
}

class Son extends Pa {
    protected $age = 'younger';
    public function sayMyAge() {
        return 'sayMyAge() in Son should be younger. ' .
                  'My age is: ' . $this->age;
    }
}

$son = new Son();
$reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($son)), 
                                         'sayMyAge');
echo $reflectionMethod->invoke($son);
// returns:
// sayMyAge() in Grandpa should be very old. My age is: younger

注意:调用的方法必须是公共的。

于 2012-10-12T02:43:06.367 回答
2

蒂莫的回答有效,但我认为它的作用是偶然的,而不是设计的。如果您查看 $this->doX 与 parent::doX 与 Grandparent::doX 的操作码,您会看到 Grandparent::doX 作为静态方法调用,但 PHP 将使用范围内的 $this

$this->doX
  17     1        EXT_STMT                                                 
         2        INIT_METHOD_CALL                               'doX'
         3        EXT_FCALL_BEGIN                                          
         4        DO_FCALL                                      0          
         5        EXT_FCALL_END                                            

parent::doX
  18     6        EXT_STMT                                                 
         7        FETCH_CLASS                                 514  :1      
         8        INIT_STATIC_METHOD_CALL                     $1, 'doX'
         9        EXT_FCALL_BEGIN                                          
        10        DO_FCALL                                      0          
        11        EXT_FCALL_END                                            

Grandparent::doX
  19    12        EXT_STMT                                                 
        13        INIT_STATIC_METHOD_CALL                  'C1', 'doX'
        14        EXT_FCALL_BEGIN                                          
        15        DO_FCALL                                      0          
        16        EXT_FCALL_END                                            

由于这个原因,$this 参数在祖父母的静态调用中可用(来自https://www.php.net/manual/en/language.oop5.basic.php):

当从对象上下文中调用方法时,伪变量 $this 可用。$this 是对调用对象的引用(通常是方法所属的对象,但也可能是另一个对象,如果该方法是从辅助对象的上下文中静态调用的)。从 PHP 7.0.0 开始,从不兼容的上下文中静态调用非静态方法会导致 $this 在方法内部未定义。自 PHP 5.6.0 起,从不兼容的上下文中静态调用非静态方法已被弃用。从 PHP 7.0.0 开始,静态调用非静态方法通常已被弃用(即使从兼容的上下文中调用)。在 PHP 5.6.0 之前,此类调用已经触发了严格的通知。

于 2019-07-03T23:34:01.313 回答
1

您可以使用单独的内部方法(例如_doStuff补充doStuff)并通过父级直接从孙子级调用它。

// Base class that everything inherits
class Grandpa  {
    protected function _doStuff() {
        // grandpa's logic
        echo 'grandpa ';
    }

    public function doStuff() {
        $this->_doStuff();
    }

}

class Papa extends Grandpa {
    public function doStuff() {
        parent::doStuff();
        echo 'papa ';
    }
}

class Kiddo extends Papa {
    public function doStuff() {
        // Calls _doStuff instead of doStuff
        parent::_doStuff();
        echo 'kiddo';
    }
}

$person = new Grandpa();
$person->doStuff();
echo "\n";
$person = new Papa();
$person->doStuff();
echo "\n";
$person = new Kiddo();
$person->doStuff();

节目

grandpa
grandpa papa
grandpa kiddo
于 2013-05-10T09:22:03.940 回答