如何在 PHP >= 5.4 中的 trait 中重载类构造函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12478124/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 03:38:03  来源:igfitidea点击:

How to overload class constructor within traits in PHP >= 5.4

phptraits

提问by Guy Fawkes

In PHP 5, I can to overload constructors (and any others methods). But if I get some code like this:

在 PHP 5 中,我可以重载构造函数(和任何其他方法)。但是如果我得到一些这样的代码:

class Base {

    public function __construct($a, $b) {
        echo $a+$b;
    }


    public function sayHello() {
        echo 'Hello ';
    }
}


trait SayWorld {

    public function __construct($a, $b, $c = 0) {
        echo (int)$c * ($a+$b);
    }

    public function sayHello($a = null) {
        parent::sayHello();
        echo 'World!'.$a;
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld(2, 3);
$o->sayHello(1);

I have an error:

我有一个错误:

Fatal error: MyHelloWorld has colliding constructor definitions coming from traits

致命错误:MyHelloWorld 具有来自特征的冲突构造函数定义

How can I to fix it? You can test my code here.

我该如何解决?你可以在这里测试我的代码。

回答by Maciej Sz

I think for now the only way to do what you want is:

我认为现在做你想做的唯一方法是:

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b, $c);
    }
}

Edit 2:

编辑2:

My advice, based on over a year of dealing with traits in PHP, is: avoid writing constructors in traits at all, or if you must - at least make them parameterless. Having them in traits goes against the idea of constructors in general, which is: constructors should be specific to a class to which they belong. Other, evolved high-level languages don't even support implicit constructor inheritance. This is because constructors have far more stronger relation to the class then other methods. In fact they have so strong relation, that even the LSPdoes not apply to them. The traits in Scala language (a very mature and SOLID-friendly successor of Java), can't have a constructor with parameters.

基于在 PHP 中处理 trait 一年多的经验,我的建议是:完全避免在 trait 中编写构造函数,或者如果必须的话——至少让它们无参数。将它们放在 trait 中与一般的构造函数的想法背道而驰,即:构造函数应该特定于它们所属的类。其他进化的高级语言甚至不支持隐式构造函数继承。这是因为构造函数与类的关系比其他方法强得多。事实上,它们之间的关系如此紧密,以至于连LSP都不适用于它们。Scala 语言(Java 的一个非常成熟且对SOLID友好的继承者)中的特性不能有带参数的构造函数

Edit 1:

编辑1:

There was a bugin PHP 5.4.11, which actually allowed to alias a superclass method. But this was considered a no-no by the PHP developers, so we are still stuck with that cumbersome solution which I presented above. But that bug raised a discussion about what can be done with this, and I'm hoping it will be targeted in future releases.

PHP 5.4.11 中有一个错误,它实际上允许为超类方法设置别名。但这被 PHP 开发人员认为是禁忌,所以我们仍然坚持我上面介绍的那个繁琐的解决方案。但是那个错误引发了关于可以用它做什么的讨论,我希望它会在未来的版本中成为目标。

Meanwhile I came across the same problem over and over again. My irritation raised exponentially with the number of parameters and lines of docblock which had to be repeated a lot of times in order to use the trait. So I came up with the following pattern in order to stick to the DRY rule as much as I could:

与此同时,我一次又一次地遇到了同样的问题。我的愤怒随着参数和 docblock 行的数量呈指数增长,为了使用该特性必须重复很多次。所以我想出了以下模式,以尽可能坚持 DRY 规则:

Instead of repeating entire set of parameters like this:

而不是像这样重复整个参数集:

trait SayWorld {

    /**
     * This is a valid docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     */
    public function __construct($a, $b) {
        echo (int)$c * ($a+$b);
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * Repeated and unnecessary docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     * @param int $c Doc comment.
     */
    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b);
    }
}

I write a class much like a tuple(concept familiar to C#and Pythonusers), and use it instead of an endless list of parameters:

我编写了一个很像元组的类(C#Python用户熟悉的概念),并使用它而不是无休止的参数列表:

class SayWorldConstructTuple
{
    public $a;

    public $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * New and valid docblock.
     *
     * @param SayWorldConstructTuple $Tuple
     * @param int $c Additional parameter.
     */
    public function __construct(SayWorldConstructTuple $Tuple, $c = 0)
    {
        $this->__swConstruct($Tuple->a, $Tuple->b);
        $this->c = $c;
    }
}

Note: this pattern is of course more useful with a larger amount of tuple's constructor parameters, and more classes using the tuple.

注意:这种模式对于更多元组的构造函数参数和使用元组的更多类当然更有用。

It can be automated further with the use of PHP's dynamic nature.

它可以通过使用 PHP 的动态特性进一步自动化。

回答by Martin

Try:

尝试:

use SayWorld {
  Base::__construct insteadof SayWorld;
}

Ref: PHP Docs

参考:PHP 文档

回答by Chris Schloegel

Old post but, in case this helps anyone:

旧帖子,但如果这对任何人有帮助:

I had a similar situation but decided to use a slightly different approach. I was writing a WordPress plugin and wanted to pass the plugin info around (version, name, text domain etc..) but didn't want to go around changing each file when refactoring or extend another class so I created a trait with a constructor that simply calls an init function for class specific operations.

我有类似的情况,但决定使用稍微不同的方法。我正在编写一个 WordPress 插件,想传递插件信息(版本、名称、文本域等),但不想在重构或扩展另一个类时改变每个文件,所以我用构造函数创建了一个特征它只是为类特定的操作调用一个 init 函数。

trait HasPluginInfoTrait{
    public function __construct() { 

        $this->plugin_name        = PLUGIN_NAME;
        $this->version            = PLUGIN_VERSION;

        if ( method_exists( $this, 'init' ){
            $this->init();
        }
    }
}

class SampleClass {
    use HasPluginInfoTrait;

    private function init(){
        // Code specific to SampleClass
    }
}