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

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

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

我有一个错误:

致命错误: MyHelloWorld 的构造函数定义与 trait 相冲突

我如何修复它? 你可以测试我的代码 给你

65998 次浏览

试试:

use SayWorld {
Base::__construct insteadof SayWorld;
}

档号: PHP 文档

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

class MyHelloWorld extends Base {


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


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

编辑2:

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

编辑1:

PHP 5.4.11中有一个 臭虫,它实际上允许作为超类方法的别名。但是 PHP 开发人员认为这是一个禁忌,所以我们仍然坚持使用我在上面介绍的那个繁琐的解决方案。但是这个 bug 引发了一场关于如何处理这个问题的讨论,我希望在未来的版本中能够有针对性地解决这个问题。

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

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

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

我编写的类非常类似于 Tuple(C # 巨蟒用户熟悉的概念) ,并使用它来代替无穷无尽的参数列表:

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

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

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

旧文章,但是,以防这对任何人有帮助:

我也遇到过类似的情况,但是我决定用一种稍微不同的方法。我当时正在写一个 WordPress 插件,想把这个插件的信息(版本、名称、文本域名等等)传递给大家但我不想在重构或扩展其他类时改变每个文件,所以我用构造函数创建了一个 trait,该构造函数只是为类特定的操作调用一个 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
}
}