如何在 PHP >= 5.4 的特征中重载类构造函数
Posted
技术标签:
【中文标题】如何在 PHP >= 5.4 的特征中重载类构造函数【英文标题】:How to overload class constructor within traits in PHP >= 5.4 【发布时间】:2012-09-10 18:47:20 【问题描述】:在 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);
我有一个错误:
致命错误:MyHelloWorld 有冲突的构造函数定义来自特征
我该如何解决?你可以测试我的代码here。
【问题讨论】:
只是一个警告。自 5.4.7 起,特性别名将导致 PHP 崩溃,尤其是自动加载器。已在 repo 中添加了一个修复程序,因此希望它会出现在下一个版本中。 【参考方案1】:旧帖子,但万一这对任何人都有帮助:
我有类似的情况,但决定使用稍微不同的方法。我正在编写一个 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
【讨论】:
好主意,但是当向一个类添加多个特征时,这不再有意义,这并不少见。 在 trait 中有一个方法,比如PluginInfoInit()
,然后在类构造函数中添加一个调用,比如:if ( method_exists( $this, 'PlubinInfoInit' ) ) $this->PluginInfoInit();
这将适用于多个特征,并且可以只添加 trait 时卡在构造函数中。【参考方案2】:
我认为目前做你想做的唯一方法是:
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 中违背了一般构造函数的想法,即:构造函数应该特定于它们所属的类。其他进化的高级语言甚至不支持隐式构造函数继承。这是因为构造函数与类的关系比其他方法强得多。事实上,它们之间的关系如此密切,以至于即使LSP 也不适用于它们。 Scala 语言中的特征(Java 非常成熟且对SOLID 友好的继承者)can't have a constructor with parameters。
编辑 1:
在 PHP 5.4.11 中有一个 bug,它实际上允许为超类方法设置别名。但是 PHP 开发人员认为这是一个禁忌,所以我们仍然坚持我上面介绍的那个繁琐的解决方案。但是这个错误引发了关于可以用它做什么的讨论,我希望它会在未来的版本中成为目标。
与此同时,我一遍又一遍地遇到同样的问题。我的愤怒随着 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# 和 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;
注意:这种模式当然更适用于更多元组的构造函数参数,以及更多使用元组的类。
可以利用 PHP 的动态特性进一步自动化。
【讨论】:
+1 为avoid writing constructors in traits at all
解释【参考方案3】:
试试:
use SayWorld
Base::__construct insteadof SayWorld;
参考:PHP Docs
【讨论】:
我试过了。 您是否尝试过运行您自己的代码?它返回 MyHelloWorld 有冲突的构造函数定义,无论如何都来自特征致命错误。 抱歉,我的错误是我确信我的方法是正确的 ;)... 已修复! 修复后,您的答案将逻辑返回到未使用任何特征时的状态。 这个表现如何?我改用旧版构造函数,因为它似乎没有引发冲突。pastebin.com/zHktB5C0 好的,如果您要使用参数 3、7 和 10 创建 MyHelloWorld 实例,代码的输出是什么?以上是关于如何在 PHP >= 5.4 的特征中重载类构造函数的主要内容,如果未能解决你的问题,请参考以下文章
给定一个基类作为参数,如果传递了派生类,我如何使用 op<< 重载来打印派生类的特征?