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

Pau*_*ulo 194 php oop inheritance constructor class

我需要在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
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道这是一件很奇怪的事情,我试图找到一种味道并不难闻的手段但是,我很好奇是否有可能.

编辑

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

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

Cor*_*lou 148

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

  • 好的解决方法,但如果父类来自某些希望扩展的外部库,那么这是不可接受的.我喜欢下面的太多php答案. (18认同)

too*_*php 71

你必须使用Grandpa::__construct(),没有其它的捷径.此外,这破坏了Papa类的封装- 在阅读或处理时Papa,应该可以安全地假设该__construct()方法将在构造期间被调用,但是Kiddo类不会这样做.

  • 同意.我一直都在使用它 - 你可以通过它的名字来调用一个类,而不仅仅是通过`parent`或`self`的魔术名称.我知道我会深入三到四节课.事实上,我已经开始通过它的名字来引用我的基类,而不是使用父类,这样我确定我总是得到正确的对象. (11认同)

小智 52

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*kin 21

美丽的解决方案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();
Run Code Online (Sandbox Code Playgroud)


Pau*_*ulo 19

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

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

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

  • 我认为这里更好的想法是打破你试图从构造和受保护方法中使用的功能.然后,您可以有选择地从构造函数中调用该方法 (4认同)

Mit*_*aro 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";
Run Code Online (Sandbox Code Playgroud)


Fis*_*ned 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;
Run Code Online (Sandbox Code Playgroud)

我得到了预期的结果:

老兄

爷爷

这是一个不是bug的功能,请查看以供参考:

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

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

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

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

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;
Run Code Online (Sandbox Code Playgroud)

它也按预期工作:

老兄

爷爷

你好

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

$papa = new Papa;
Run Code Online (Sandbox Code Playgroud)

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

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

if ($this instanceof Kiddo) Kiddo::hello();
Run Code Online (Sandbox Code Playgroud)


mwa*_*way 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!'
Run Code Online (Sandbox Code Playgroud)

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


Xor*_*rax 7

您可以从您想要的位置调用Grandpa :: __构造,$ this关键字将引用您当前的类实例.但是要小心使用此方法,您无法从此其他上下文访问受保护的属性和当前实例的方法,只能访问公共元素.=>所有工作和官方支持.

// 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();
Run Code Online (Sandbox Code Playgroud)


Hau*_*uke 7

关于 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 
Run Code Online (Sandbox Code Playgroud)

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

parent::paFkt(); 
Run Code Online (Sandbox Code Playgroud)

或者

Pa::paFkt();
Run Code Online (Sandbox Code Playgroud)

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

课外定义

$test::paFkt();
Run Code Online (Sandbox Code Playgroud)

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