为什么PHP特征不能有静态抽象方法?

时间:2015-09-24 12:58:43

标签: php oop static abstract traits

在PHP v5.3中使用后期静态绑定,可以在接口中有用地声明static方法;对于PHP v5.4中的特征,方法可以是staticabstract,但不能同时是两者。这似乎是不合逻辑和不一致的。

特别是,假设一个接口的特性提供所有实现,静态方法除外;除非在特征中声明了该方法,否则静态分析器会对特征内的任何引用进行跟踪。但是,在特征中提供具体实现不再强制实现/使用类来提供自己的实现 - 这是危险的; 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 /* ... */ }
}

1 个答案:

答案 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 staticinterface范围内。它并不是特征所独有的,它对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。可能会考虑静力学的动机,并考虑重构它们。