【问题标题】:How do I get a PHP class constructor to call its parent's parent's constructor?如何让 PHP 类构造函数调用其父级的父级构造函数?
【发布时间】:2010-12-06 04:03:29
【问题描述】:

我需要在 PHP 中有一个类构造函数调用其父级的 父级(祖父级?)构造函数而不调用父级构造函数。

// main class that everything inherits
class Grandpa 
{
    public function __construct()
    {

    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        // THIS IS WHERE I NEED TO CALL GRANDPA'S
        // CONSTRUCTOR AND NOT PAPA'S
    }
}

我知道这是一件很奇怪的事情,我正在尝试寻找一种不难闻的方法,但我很好奇这是否可能。

【问题讨论】:

  • 你有什么理由不能在 Kiddo 的构造中实例化爷爷?
  • Grampa 构造函数为自己设置由其子代继承的属性。 Papa 在它的构造函数中做了一些会弄乱 Kiddo 的东西。所以我需要在构造过程中调用 Grandpa 构造函数来为 Kiddo 设置属性。
  • 那么 Kiddo 不应该是从爷爷而不是爸爸延伸出来的。
  • @MitMaro。我同意,实际上我通过创建一个扩展 Grandpa 的中间类解决了我的实际问题。然后爸爸和孩子都延长了这门课。 Kiddo 需要 Papa 的一些中间功能,但不喜欢它的构造函数,因此该类具有该附加功能并且都对其进行了扩展。
  • 完美的解决方案。您应该将其发布为答案。

标签: php class oop inheritance constructor


【解决方案1】:

从php 7你可以使用

parent::parent::__construct();

【讨论】:

  • @Wouter0100 lala977 确实说“来自 PHP 7”,您的链接正在使用 5.6
  • @TravisWeston 请重新检查我的链接。它针对所有版本测试代码的平稳性。 5.6 到 7.2 重现相同的错误,而其他运行时返回一些不同的错误。
【解决方案2】:

关于 php 的有趣细节:扩展类可以在静态问题中使用父类的非静态函数。在外面你会得到一个严格的错误。

error_reporting(E_ALL);

class GrandPa
{
    public function __construct()
    {
        print("construct grandpa<br/>");
        $this->grandPaFkt();
    }

    protected function grandPaFkt(){
        print(">>do Grandpa<br/>");
    }
}

class Pa extends GrandPa
{
    public function __construct()
    {   parent::__construct();
        print("construct Pa <br/>");
    }

    public function paFkt(){
        print(">>do Pa <br>");
    }
}

class Child extends Pa
{
    public function __construct()
    {
        GrandPa::__construct();
        Pa::paFkt();//allright
        //parent::__construct();//whatever you want
        print("construct Child<br/>");
    }

}

$test=new Child();
$test::paFkt();//strict error 

所以在扩展类(Child)中你可以使用

parent::paFkt(); 

Pa::paFkt();

访问父母(或爷爷的)(非私人)功能。

类外定义

$test::paFkt();

将引发严格错误(非静态函数)。

【讨论】:

    【解决方案3】:
        class Grandpa 
    {
        public function __construct()
        {
            echo"Hello Kiddo";
        }    
    }
    
    class Papa extends Grandpa
    {
        public function __construct()
        {            
        }
        public function CallGranddad()
        {
            parent::__construct();
        }
    }
    
    class Kiddo extends Papa
    {
        public function __construct()
        {
    
        }
        public function needSomethingFromGrandDad
        {
           parent::CallGranddad();
        }
    }
    

    【讨论】:

      【解决方案4】:

      使用Reflection 的漂亮解决方案。

      <?php
      class Grandpa 
      {
          public function __construct()
          {
              echo "Grandpa's constructor called\n";
          }
      
      }
      
      class Papa extends Grandpa
      {
          public function __construct()
          {
              echo "Papa's constructor called\n";
      
              // call Grandpa's constructor
              parent::__construct();
          }
      }
      
      class Kiddo extends Papa
      {
          public function __construct()
          {
              echo "Kiddo's constructor called\n";
      
              $reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($this)), '__construct');
              $reflectionMethod->invoke($this);
          }
      }
      
      $kiddo = new Kiddo();
      $papa = new Papa();
      

      【讨论】:

        【解决方案5】:
        <?php
        
        class grand_pa
        {
            public function __construct()
            {
                echo "Hey I am Grand Pa <br>";
            }
        }
        
        class pa_pa extends grand_pa
        {
            // no need for construct here unless you want to do something specifically within this class as init stuff
            // the construct for this class will be inherited from the parent.
        }
        
        class kiddo extends pa_pa
        {
            public function __construct()
            {
                parent::__construct();
                echo "Hey I am a child <br>";
            }
        }
        
        new kiddo();
        ?>
        

        当然,这期望您不需要在 pa_pa 的构造中做任何事情。运行它会输出:

        嘿,我是爷爷 嘿,我是个孩子

        【讨论】:

          【解决方案6】:

          好吧,又一个丑陋的解决方案:

          在 Papa 中创建一个函数,如:

          protected function call2Granpa() {
               return parent::__construct();
          }
          

          然后你在 Kiddo 中使用:

          parent::call2Granpa(); //而不是在 Papa 中调用构造函数。

          我认为它可以工作......我没有测试它,所以我不确定是否 对象被正确创建。

          我使用了这种方法,但使用了非构造函数。

          【讨论】:

            【解决方案7】:

            我同意“php太多”,试试这个:

            class Grandpa 
            {
                public function __construct()
                {
                    echo 'Grandpa<br/>';
                }
            
            }
            
            class Papa extends Grandpa
            {
                public function __construct()
                {
                    echo 'Papa<br/>';
                    parent::__construct();
                }
            }
            
            class Kiddo extends Papa
            {
                public function __construct()
                {
                    // THIS IS WHERE I NEED TO CALL GRANDPA'S
                    // CONSTRUCTOR AND NOT PAPA'S
                    echo 'Kiddo<br/>';
                    Grandpa::__construct();
                }
            }
            
            $instance = new Kiddo;
            

            我得到了预期的结果:

            童鞋

            爷爷

            这是一个功能而不是错误,请检查此以供参考:

            https://bugs.php.net/bug.php?id=42016

            这就是它的工作方式。如果它发现它来自正确的上下文,则此调用版本不会强制执行静态调用。

            相反,它只会保留 $this 并对此感到满意。

            parent::method() 以相同的方式工作,您不必将方法定义为静态,但可以在相同的上下文中调用它。试试这个更有趣:

            class Grandpa 
            {
                public function __construct()
                {
                    echo 'Grandpa<br/>';
                    Kiddo::hello();
                }
            
            }
            
            class Papa extends Grandpa
            {
                public function __construct()
                {
                    echo 'Papa<br/>';
                    parent::__construct();
                }
            }
            
            class Kiddo extends Papa
            {
                public function __construct()
                {
                    // THIS IS WHERE I NEED TO CALL GRANDPA'S
                    // CONSTRUCTOR AND NOT PAPA'S
                    echo 'Kiddo<br/>';
                    Grandpa::__construct();
                }
            
                public function hello()
                {
                    echo 'Hello<br/>';
                }
            }
            
            $instance = new Kiddo;
            

            它也可以按预期工作:

            童鞋

            爷爷

            你好

            但是如果你尝试初始化一个新的 Papa,你会得到一个 E_STRICT 错误:

            $papa = new Papa;
            

            严格标准:不应静态调用非静态方法 Kiddo::hello(),假设 $this 来自不兼容的上下文

            您可以使用 instanceof 来确定是否可以在父方法中调用 Children::method():

            if ($this instanceof Kiddo) Kiddo::hello();
            

            【讨论】:

            • 请记住,父级只是实现该方法的任何第一个父级的快捷方式……因此,从后代调用 AscendantName::method 可以工作,从后代调用 static::method 将始终调用已实现的最新一代方法。如果您希望父母调用孩子方法,您可以检查不要使用 (get_class($this)==__CLASS__?'Healthy people dont\'t call themself…':'Calling latest') 调用自己(看起来很奇怪,但可能与私有函数和 __call 魔术函数结合使用)......
            【解决方案8】:
            // main class that everything inherits
            class Grandpa 
            {
                public function __construct()
                {
                    $this->___construct();
                }
            
                protected function ___construct()
                {
                    // grandpa's logic
                }
            
            }
            
            class Papa extends Grandpa
            {
                public function __construct()
                {
                    // call Grandpa's constructor
                    parent::__construct();
                }
            }
            
            class Kiddo extends Papa
            {
                public function __construct()
                {
                    parent::___construct();
                }
            }
            

            注意“___construct”不是什么神奇的名字,你可以叫它“doGrandpaStuff”。

            【讨论】:

              【解决方案9】:

              我最终想出了解决问题的替代解决方案。

              • 我创建了一个扩展 Grandpa 的中间类。
              • 然后爸爸和孩子都扩展了这门课。
              • Kiddo 需要 Papa 的一些中间功能,但不喜欢它的构造函数,因此该类具有该附加功能并且都对其进行了扩展。

              我赞成其他两个答案,它们为更丑陋的问题提供了有效但丑陋的解决方案:)

              【讨论】:

              • 我认为这里更好的想法是将您尝试使用的功能从构造方法中分离出来并转换为受保护的方法。然后你可以有选择地从构造函数中调用该方法
              • 这并不能回答您所说的确切问题。如果现实世界混淆了应该清晰和受限的东西,就会发生这种情况。可惜了这个问题。
              【解决方案10】:

              你可以从你想要的地方调用 Grandpa::__construct 并且 $this 关键字将引用你当前的类实例。 但是要小心这个方法,你不能从这个其他上下文访问当前实例的受保护属性和方法,只能访问公共元素。 =>所有工作和officialy supported

              例子

              // main class that everything inherits
              class Grandpa 
              {
                  public function __construct()
                  {
                      echo $this->one; // will print 1
                      echo $this->two; // error cannot access protected property
                  }
              
              }
              
              class Papa extends Grandpa
              {
                  public function __construct()
                  {
                      // call Grandpa's constructor
                      parent::__construct();
                  }
              }
              
              class Kiddo extends Papa
              {
                  public $one = 1;
                  protected $two = 2;
                  public function __construct()
                  {
                      Grandpa::__construct();
                  }
              }
              
              new Kiddo();
              

              【讨论】:

                【解决方案11】:
                class Grandpa 
                {
                    public function __construct()
                    {}
                }
                
                class Papa extends Grandpa
                {
                    public function __construct()
                    {
                        //call Grandpa's constructor
                        parent::__construct();
                    }
                }
                
                class Kiddo extends Papa
                {
                    public function __construct()
                    {
                        //this is not a bug, it works that way in php
                        Grandpa::__construct();
                    }
                }
                

                【讨论】:

                • 我个人不会选择这样做,因为这意味着爸爸的承包商根本不会被叫到。我会采用类似 cballou 的方法(即将参数传递给 Papa 的构造函数并让它调用 it's 父构造函数或不基于此)。
                • 我们这里的情况是我们需要跳过父类的逻辑,并且在大多数情况下我们无法更改祖父类或父类。我相信这是最好的方法,因为只有孩子才能做出改变。唯一的问题是它可能会抛出一个 E_STRICT 通知 link,但当我测试它时它并不适合我。
                • 这是一个有趣的解决方案,但是,如果你真的不需要父构造函数的逻辑,你确定你真的在做父的子类吗? ?
                • 这是正确答案。虽然继承 Papa 似乎很愚蠢,但您想在没有 Papa 的情况下调用 GrandPa 构造函数,但我发现这样做很有用。我想保持层次结构,但我需要做一个干净的 Kiddo 构造函数,它与 Papa 没有任何关系,但仍然希望使用 GrandPa 构造函数中发生的事情的好处。也许 Papa 在构造函数中做了一堆 Kiddo 不需要或不需要的垃圾,但它仍然有有用的组件。
                • 刚叫爷爷就像我是老板一样
                【解决方案12】:

                对此有一个更简单的解决方案,但它要求您确切知道当前类经历了多少继承。幸运的是,get_parent_class() 的参数允许您的类数组成员作为类名作为字符串以及实例本身。

                请记住,这本质上也依赖于静态调用类的 __construct() 方法,尽管在继承对象的实例化范围内,这种特殊情况下的差异可以忽略不计(啊,PHP)。

                考虑以下几点:

                class Foo {
                    var $f = 'bad (Foo)';
                
                    function __construct() {
                        $this->f = 'Good!';
                    }
                }
                
                class Bar extends Foo {
                    var $f = 'bad (Bar)';
                }
                
                class FooBar extends Bar {
                    var $f = 'bad (FooBar)';
                
                    function __construct() {
                        # FooBar constructor logic here
                        call_user_func(array(get_parent_class(get_parent_class($this)), '__construct'));
                    }
                }
                
                $foo = new FooBar();
                echo $foo->f; #=> 'Good!'
                

                同样,由于 debug_backtrace() 的限制,对于您不知道发生了多少继承的情况,这不是一个可行的解决方案,但在受控情况下,它可以按预期工作。

                【讨论】:

                  【解决方案13】:

                  另一个不使用标志并且可能适用于您的情况的选项:

                  <?php
                  // main class that everything inherits
                  class Grandpa 
                  {
                      public function __construct(){
                          $this->GrandpaSetup();
                      }
                  
                      public function GrandpaSetup(){
                          $this->prop1 = 'foo';
                          $this->prop2 = 'bar';
                      }
                  }
                  
                  class Papa extends Grandpa
                  {
                      public function __construct()
                      {
                          // call Grandpa's constructor
                          parent::__construct();
                          $this->prop1 = 'foobar';
                      }
                  
                  }
                  class Kiddo extends Papa
                  {
                      public function __construct()
                      {
                          $this->GrandpaSetup();
                      }
                  }
                  
                  $kid = new Kiddo();
                  echo "{$kid->prop1}\n{$kid->prop2}\n";
                  

                  【讨论】:

                    【解决方案14】:

                    丑陋的解决方法是向 Papa 传递一个布尔参数,表明您不希望解析其构造函数中包含的代码。即:

                    // main class that everything inherits
                    class Grandpa 
                    {
                        public function __construct()
                        {
                    
                        }
                    
                    }
                    
                    class Papa extends Grandpa
                    {
                        public function __construct($bypass = false)
                        {
                            // only perform actions inside if not bypassing
                            if (!$bypass) {
                    
                            }
                            // call Grandpa's constructor
                            parent::__construct();
                        }
                    }
                    
                    class Kiddo extends Papa
                    {
                        public function __construct()
                        {
                            $bypassPapa = true;
                            parent::__construct($bypassPapa);
                        }
                    }
                    

                    【讨论】:

                    • 很好的解决方法,但是如果父类来自一些希望你扩展的外部库,这是不可接受的。我喜欢下面太多的 php 答案。
                    • 这其实很聪明。在我的实现中,我做if($bypass) return; & 可以定位它,以便在绕过之前完成一些重要的事情。
                    【解决方案15】:

                    您必须使用Grandpa::__construct(),没有其他快捷方式。此外,这破坏了Papa 类的封装——在阅读或处理Papa 时,可以安全地假设__construct() 方法将在构造期间调用,但Kiddo 类不这样做.

                    【讨论】:

                    • 无法理解。在 PHP5.3 下,将 __construct 声明为静态会导致我出现以下错误“致命错误:构造函数 Grandpa::__construct() 不能是静态的”
                    • 当我尝试它时,我没有将它声明为静态。我通常创建了类结构,但没有调用 parent::__construct(),而是调用了 Grandpa::__construct() 并且它起作用了。我似乎也不对,但整个问题有点奇怪。
                    • 同意。我一直都在使用它——你可以通过它的名字来调用一个类,而不仅仅是通过parentself 的神奇名称。众所周知,我上了三四门课。事实上,我已经开始通过它的名称而不是使用父类来引用我的基类,这样我就可以确定我总是得到正确的对象。
                    • EvilChookie 的解决方案是最好的。应该是认可的答案。比你 Chookie!
                    • @SparK 如果您使用的是 PHP 5.3.0 或更高版本,Late Static Bindings 很可能会解决您的用例。
                    猜你喜欢
                    • 2011-05-14
                    • 2017-09-28
                    • 1970-01-01
                    • 2017-08-29
                    • 2018-02-28
                    • 1970-01-01
                    • 2023-03-15
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多