How to call grandparent method without getting E_STRICT error?

后端 未结 4 1129
有刺的猬
有刺的猬 2021-01-04 02:57

Sometimes I need to execute grandparent method (that is, bypass the parent method), I know this is code smell, but sometimes I can\'t change the other classes (frameworks, l

相关标签:
4条回答
  • 2021-01-04 03:04

    You may use ReflectionMethod->invoke()

    Example:

    <?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
    

    Note: The invoked method must be public.

    0 讨论(0)
  • 2021-01-04 03:15

    Timo's answer works but I think it works by accident more than by design. If you look at the opcodes for a $this->doX vs a parent::doX vs a Grandparent::doX you can see that Grandparent::doX is invoked as a static method but PHP will use the $this that's in scope

    $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                                            
    

    The $this parameter is available in the Grandparent's static invocation because of this (from https://www.php.net/manual/en/language.oop5.basic.php):

    The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object). As of PHP 7.0.0 calling a non-static method statically from an incompatible context results in $this being undefined inside the method. Calling a non-static method statically from an incompatible context has been deprecated as of PHP 5.6.0. As of PHP 7.0.0 calling a non-static method statically has been generally deprecated (even if called from a compatible context). Before PHP 5.6.0 such calls already triggered a strict notice.

    0 讨论(0)
  • 2021-01-04 03:17

    You can use a separate internal method (e.g. _doStuff to complement doStuff) and call that directly from the grandchild, through the parent.

    // 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();
    

    shows

    grandpa
    grandpa papa
    grandpa kiddo
    
    0 讨论(0)
  • 2021-01-04 03:24

    You can call the grandparent directly by name (does not need Reflection, nor 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';
        }
    }
    

    The Base::getFoo call may look like a static call (due to the colon :: syntax), however it is not. Just like parent:: isn't static, either.

    Calling a method from the inheritance chain within a class will correctly bind the $this value, invoke it as a regular method, honour the visibility rules (e.g. protected), and is not a violation of any kind!

    This may look a bit strange at first, but, this is the way to do it in PHP.

    0 讨论(0)
提交回复
热议问题