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

How do I get a PHP class constructor to call its parent's parent's constructor

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 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
    }
}

我知道这是一件很奇怪的事情,我正试图找到一种不臭的方法,但尽管如此,我很好奇这是否可行。

编辑

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

对于以后遇到此问题的任何人,请找到另一个解决方案。我找到了一种更好的方法,它不会破坏课堂结构。你也应该这样。


丑陋的解决方法是将一个布尔参数传递给papa,表示您不希望解析它的构造函数中包含的代码。即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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);
    }
}


你必须使用Grandpa::__construct(),没有其他捷径。此外,这会破坏Papa类的封装—在读取或处理Papa时,可以安全地假定在构建期间调用__construct()方法,但Kiddo类不会这样做。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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();
    }
}


最后我想出了一个解决这个问题的替代方案。

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

我对其他两个为更丑陋的问题提供了有效但又丑陋的解决方案的答案投了反对票:)


使用Reflection的漂亮解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
class Grandpa
{
    public function __construct()
    {
        echo"Grandpa's constructor called
"
;
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        echo"Papa's constructor called
"
;

        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        echo"Kiddo's constructor called
"
;

        $reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($this)), '__construct');
        $reflectionMethod->invoke($this);
    }
}

$kiddo = new Kiddo();
$papa = new Papa();

另一个不使用标志并且可能在您的情况下工作的选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?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}
{$kid->prop2}
"
;


我同意"PHP太多",请尝试以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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;

我得到了预期的结果:

Kiddo

Grandpa

< /块引用>

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

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

It is just the way it works. If it sees it is coming from the right context this call version does not enforce a static call.

Instead it will simply keep $this and be happy with it.

< /块引用>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;

它也按预期工作:

Kiddo

Grandpa

Hello

< /块引用>

但是,如果您尝试初始化新的爸爸,您将得到一个e_严格错误:

1
$papa = new Papa;

Strict standards: Non-static method Kiddo::hello() should not be called statically, assuming $this from incompatible context

< /块引用>

可以使用InstanceOf确定是否可以在父方法中调用children::method():

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


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

请记住,这也内在地依赖于静态地调用类的_uuConstruct()方法,尽管在继承对象的实例范围内,这种特殊情况下的差异是可以忽略的(ah,php)。

考虑以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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()的限制,这不是一个可行的解决方案,但是在受控制的情况下,它可以按预期工作。


您可以从所需的位置调用grando::uu构造,$this关键字将引用当前的类实例。但是要小心使用此方法,您不能从此其他上下文访问当前实例的受保护属性和方法,只能访问公共元素。>All work and officialy supported.

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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();

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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");
    }
}

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

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

1
parent::paFkt();

1
Pa::paFkt();

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

外部类定义

1
$test::paFkt();

将特洛伊严格错误(非静态函数)。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

class grand_pa
{
    public function __construct()
    {
        echo"Hey I am Grand Pa";
    }
}

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

new kiddo();
?>

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

嘿,我是大爸爸嘿,我是个孩子


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

在Papa中创建一个函数:

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

那么在基多你用:

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

我想它能起作用…我还没有测试,所以我不确定对象创建正确。

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    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();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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();
    }
}

注意,"构装"不是什么神奇的名字,你可以称它为"dograndpastuff"。


从php 7 u可以使用

parent::parent::__construct();