简体   繁体   English

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

[英]Why can't PHP traits have static abstract methods?

With late static binding in PHP v5.3, one can usefully declare static methods in interfaces; 使用PHP v5.3中的后期静态绑定,可以在接口中有用地声明static方法; with traits in PHP v5.4, methods can be either static or abstract but not both. 对于PHP v5.4中的特征,方法可以是static也可以是abstract但不能同时是两者。 This appears to be illogical and inconsistent. 这似乎是不合逻辑和不一致的。

In particular, suppose one has an interface for which a trait provides all implementation, except for a static method; 特别地,假设一个具有特征提供所有实现的接口,静态方法除外; unless that method is declared in the trait, static analysers balk at any references thereto from within the trait. 除非在特征中声明了该方法,否则静态分析器会对特征内的任何引用进行跟踪。 But providing a concrete implementation within the trait no longer forces implementing/using classes to provide their own implementation—which is dangerous; 但是,在特征中提供具体实现不再强制实现/使用类来提供自己的实现 - 这是危险的; abstract static would be ideal, but is not allowed. abstract static是理想的,但是不允许。

What is the explanation for this contradiction? 这个矛盾的解释是什么? How would you recommend resolving this problem? 你会如何建议解决这个问题?

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 /* ... */ }
}

TL;DR: As of PHP 7, you can . TL; DR:从PHP 7开始, 你可以 Before then, you could define abstract static on trait , but internals deemed it bad practice. 在此之前,您可以trait上定义abstract static ,但内部认为这是不好的做法。


Strictly, abstract means sub-class must implement, and static means code for this specific class only. 严格地说, abstract意味着子类必须实现, static意味着只为这个特定类的代码。 Taken together, abstract static means "sub-class must implement code for this specific class only". 总而言之, abstract static意味着“子类必须仅为此特定类实现代码”。 Totally orthogonal concepts. 完全正交的概念。

But... PHP 5.3+ supports static inheritance thanks to LSB. 但是......由于LSB,PHP 5.3+支持静态继承。 So we actually open that definition up a bit: self takes the former definition of static, while static becomes "code for this specific class or any of its sub-classes". 所以我们实际上打开了一下这个定义: self取了static的前一个定义,而static变成了“这个特定类或其任何子类的代码”。 The new definition of abstract static is "sub-class must implement code for this specific class or any of its sub-classes". abstract static的新定义是“子类必须为此特定类或其任何子类实现代码”。 This can lead some folks, who think of static in the strict sense, to confusion. 这可能导致一些人认为严格意义上的static混乱。 See for example bug #53081 . 请参阅bug#53081

What makes trait so special to elicit this warning? 是什么让trait如此特别以引发这种警告? Well, take a look at the engine code that implements the notice: 那么,看看实现通知的引擎代码

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

That code says the only place an abstract static is allowed is within an interface . 该代码表示​​允许abstract static唯一位置是在interface It's not unique to traits, it's unique to the definition of abstract static . 它并不是特征所独有的,它对abstract static的定义是独一无二的。 Why? 为什么? Well, notice there's a slight corner case in our definition: 好吧,注意我们的定义中有一个小角落的情况:

sub-class must implement code for this specific class or any of its sub-classes 子类必须为此特定类或其任何子实现代码

With this code: 使用此代码:

abstract class Foo {
    abstract public static function get();
}

That definition means I should be able to call Foo::get . 这个定义意味着我应该能够调用Foo::get After all Foo is a class (see that keyword "class" there) and in the strict definition, get is meant to be implemented in that class Foo . 毕竟Foo是一个类(在那里看到关键字“class”)并且在严格定义中, get意味着在该类Foo But clearly that makes no sense, because well, we're back to the orthogonality of strict static. 但显然这没有任何意义,因为我们又回到了严格静态的正交性。

If you try it in PHP, you get the only rationale response possible: 如果您在PHP中尝试它,您可以获得唯一的理由响应:

Cannot call abstract method Foo::get() 无法调用抽象方法Foo :: get()

So because PHP added static inheritance, it has to deal with these corner cases. 因为PHP添加了静态继承,它必须处理这些极端情况。 Such is the nature of features. 这就是特征的本质。 Some other languages ( C# , Java , etc.) don't have this problem, because they adopt the strict definition and simply don't allow abstract static . 其他一些语言( C#Java等)没有这个问题,因为它们采用严格的定义,并且根本不允许abstract static To get rid of this corner case, and simplify the engine, we may enforce this "abstract static only in interface" rule in the future. 为了摆脱这种极端情况,并简化引擎,我们可能在将来强制执行“仅在接口中的抽象静态”规则。 Hence, E_STRICT . 因此, E_STRICT


I would use a service delegate to solve the problem: 我会使用服务委托来解决问题:

I have common method I want to use in several classes. 我有一些常用的方法,我想在几个类中使用。 This common method relies on a static method that must be defined externally to the common code. 这种常用方法依赖于必须在公共代码外部定义的静态方法。

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

Feels a bit Rube Goldberg though. 感觉有点Rube Goldberg。 Probably would look at the motivation for statics and consider refactoring them out. 可能会看静力学的动机,并考虑重构它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM