如何让PHP类构造函数调用其父级的父构造函数

时间:2009-10-13 00:09:05

标签: php class oop inheritance constructor

我需要在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
    }
}

我知道这是一件很奇怪的事情,我试图找到一种味道不好的方法,但是,我很好奇是否有可能。

修改

我想我应该发布所选答案的理由。原因是;它是最优雅的解决方案,希望在保留所有值的同时调用“祖父母”的构造函数。它当然不是最好的方法,也不是OOP友好的,但这不是问题所在。

对于稍后遇到此问题的任何人 - 请找到其他解决方案。我能够找到一种更好的方法,不会对类结构造成严重破坏。你也应该这样做。

14 个答案:

答案 0 :(得分:135)

丑陋的解决方法是将一个布尔参数传递给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);
    }
}

答案 1 :(得分:69)

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

答案 2 :(得分:49)

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

答案 3 :(得分:19)

我最终找到了解决问题的替代解决方案。

  • 我创建了一个扩展爷爷的中级课程。
  • 然后Papa和Kiddo都扩展了那个班级。
  • Kiddo需要Papa的一些中间功能,但不喜欢它的构造函数,因此该类具有额外的功能并且都扩展了它。

我提出了另外两个答案,为一个更丑陋的问题提供了有效但丑陋的解决方案:)

答案 4 :(得分:16)

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

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

答案 5 :(得分:16)

使用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();

答案 6 :(得分:9)

我同意“太多的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();

答案 7 :(得分:8)

对此有一个更简单的解决方案,但它要求您确切知道当前类经历了多少继承。幸运的是,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()的限制,对于您不知道有多少继承发生的情况,这不是一个可行的解决方案,但在受控环境中,它可以按预期工作。

答案 8 :(得分:7)

您可以从您想要的地方调用Grandpa :: __构造,$ this关键字将引用您当前的类实例。 但是要小心这个方法,你不能从这个其他上下文访问受保护的属性和当前实例的方法,只能访问公共元素。 =&gt;所有工作和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();

答案 9 :(得分:4)

关于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();

将抛出严格的错误(非静态函数)。

答案 10 :(得分:2)

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

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

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

然后在Kiddo中使用:

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

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

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

答案 11 :(得分:2)

<?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的构造中做任何事情。运行它将输出:

嘿,我是大霸王 嘿,我还是个孩子

答案 12 :(得分:1)

// 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”。

答案 13 :(得分:-2)

从php 7你可以使用

parent::parent::__construct();