这个问题已经被问了一遍又一遍,但回复都有点老了,我有点绝望地希望能有所改变,因为"无法做到";回答。
背景:
class AbstractBuildObject {}
class Hammer extends AbstractBuildObject{}
class Nail extends AbstractBuildObject{}
class AbstractFactory{
/**
* return $type
*/
public function build1(string $type): AbstractBuiltObject {
return new $type();
}
/**
* return any(AbstractBuiltObject)
*/
public function build2(string $someArg): AbstractBuiltObject {
$type = $this->decideTypeBasedOnArgsAndState($someArg);
return new $type();
}
}
我试图用上面的注释来表示我需要的东西。
return $type
(或者理想情况下,return $type of AbstractBuiltObject
应该提示在输入参数中指定返回类型。
在第二种情况下,any(AbstractBuiltObject)
表示可以返回抽象类的任何派生具体。
所以我需要某种注释来实现我描述的效果。这些注释显然不起作用,我只是用它们来说明这个概念。
我知道有人可能会尝试使用像return Hammer|Nail
这样的管道类型连接,但在我的情况下,工厂类应该在每次将新的具体实现添加到项目时进行修改,在build1
的情况下也不够具体,我确切地知道返回类型应该是什么。
(new AbstractFactory())->build1(Hammer::class)-> // I should have Hammer autocomplete here
(new AbstractFactory())->build2('foo')-> // I should have autocomplete of any concretion of the abstract here
关于在SOLID中打破D的哲学对话,如果你想要这样的东西来自动完成可用的方法Hammer
:
(new AbstractFactory())->build1(Hammer::class)->
那么已经承诺专门为Hammer
类编写这段代码。如果你要那样做,那么你最好这样做:
$hammer = (new AbstractFactory())->build1(Hammer::class);
如果你这样做了,那么你不妨这样做:
/**
* @var Hammer
*/
$hammer = (new AbstractFactory())->build1(Hammer::class);
然后你的自动补全在$hammer->
应该工作。
我们采用的解决方案是:
<?php
class AbstractBuiltClass {
/**
* @return static
*/
public static function type(self $instance)
// change return type to :static when #PHP72 support is dropped and remove explicit typecheck
:self
{
if(!($instance instanceof static)){
throw new Exception();
}
return $instance;
}
}
class Hammer extends AbstractBuiltClass{
function hammerMethod(){}
}
Hammer::type($factory->get(Hammer::class))->hammerMethod();
可行解决方案的竞争者:
- 诗篇模板注释:https://psalm.dev/docs/annotating_code/templated_annotations/非常有前途,但尚未得到广泛支持
- 变量文档块(见Alex Howansky的回答)