在PHP v5.3中使用后期静态绑定,可以在接口中有用地声明static
方法;对于PHP v5.4中的特征,方法可以是static
或abstract
,但不能同时是两者。这似乎是不合逻辑和不一致的。
特别是,假设一个接口的特性提供所有实现,静态方法除外;除非在特征中声明了该方法,否则静态分析器会对特征内的任何引用进行跟踪。但是,在特征中提供具体实现不再强制实现/使用类来提供自己的实现 - 这是危险的; abstract static
是理想的,但不允许。
这个矛盾的解释是什么?您如何建议解决此问题?
interface MyInterface
{
public static function getSetting();
public function doSomethingWithSetting();
}
trait MyTrait
{
public abstract static function getSetting(); // I want this...
public function doSomethingWithSetting() {
$setting = static::getSetting(); // ...so that I can do this
/* ... */
}
}
class MyClass implements MyInterface
{
use MyTrait;
public static function getSetting() { return /* ... */ }
}
答案 0 :(得分:8)
TL; DR:自PHP 7起,you can。在此之前,您可能在abstract static
上定义trait
,但内部人员认为这是不好的做法。
严格地说,abstract
表示子类必须实现,而static
仅表示此特定类的代码。总之,abstract static
意味着“子类必须仅为此特定类实现代码”。完全正交的概念。
但是......由于LSB,PHP 5.3+支持静态继承。所以我们实际上稍微打开了这个定义:self
采用静态的前一个定义,而static
变成“这个特定类或其任何子类的代码”。 abstract static
的新定义是“子类必须为此特定类或其任何子类实现代码”。这可能会导致一些人严格意义上的static
混淆。请参阅示例bug #53081。
是什么让trait
如此特别以引发此警告?好吧,看看实现通知的engine code:
if (ptr->flags & ZEND_ACC_STATIC && (!scope || !(scope->ce_flags & ZEND_ACC_INTERFACE))) {
zend_error(error_type, "Static function %s%s%s() cannot be abstract", scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
}
该代码表示只允许 的地方允许abstract static
在interface
范围内。它并不是特征所独有的,它对abstract static
的定义是独一无二的。为什么?好吧,请注意我们的定义中有一个小角落:
子类必须为此特定类或其任何子类实现代码
使用此代码:
abstract class Foo {
abstract public static function get();
}
该定义意味着我应能够呼叫Foo::get
。毕竟Foo
是一个类(在那里看到关键字“class”)并且在严格的定义中,get
意味着要在该类Foo
中实现。但显然这没有任何意义,因为我们回到了严格静态的正交性。
如果您在PHP中尝试,则可以获得唯一可能的理由:
无法调用抽象方法Foo :: get()
因为PHP添加了静态继承,所以它必须处理这些极端情况。这就是特征的本质。其他一些语言(C#,Java等)没有这个问题,因为它们采用严格的定义而不允许abstract static
。为了摆脱这种极端情况,并简化引擎,我们可能在将来强制执行“仅在接口中的抽象静态”规则。因此,E_STRICT
。
我会使用服务委托来解决问题:
我想在几个课程中使用常用方法。这种常用方法依赖于必须在公共代码外部定义的静态方法。
trait MyTrait
{
public function doSomethingWithSetting() {
$service = new MyService($this);
return $service->doSomethingWithSetting();
}
}
class MyService
{
public function __construct(MyInterface $object) {
$this->object = $object;
}
public function doSomethingWithSetting() {
$setting = $this->object->getSetting();
return $setting;
}
}
感觉有点Rube Goldberg。可能会考虑静力学的动机,并考虑重构它们。