我目前正在开发一个 PHP 项目,我在我的代码中实现了依赖注入。代码运行正常,但我遇到了 PhpStorm(版本 2023.1)错误解释代码的问题,这导致类型提示中断。我正在向社区寻求帮助以解决此问题并帮助 PhpStorm 正确解释我的代码以恢复无缝类型提示功能。
为了提供一些上下文,这里有一个简化的代码示例:
class B {
public static function SayHello(){
echo 'Hello World';
}
}
class A {
/**
* @param B::class $B
*/
public function __construct(
private readonly string $B
){}
public function start(){
$this->B::SayHello(); // <---- I Would like that SayHallo() was available via typehinting
}
}
class AFactory extends A {
public function __construct(){
parent::__construct(B::class);
}
}
$class = new AFactory();
$class->start();
这种行为令人费解,因为我已经在 PHPDoc 中明确指出
$B
应该是 B::class
类型,指的是类而不仅仅是一个通用字符串。
在尝试解决这个问题时,我多次尝试通知 PhpStorm
$this->B
应该被识别为 B
类的一个实例,而不仅仅是一个字符串。我尝试的一种方法是在 PHPDoc 中指定 $B
应被视为 B
类的实例。然而,这种方法引入了其他问题。
PhpStorm 将 PHPDoc 声明解释为表明
$B
已经被初始化,但事实并非如此。结果,它引入了进一步的混乱并且没有按预期解决问题。
我将非常感谢社区的任何帮助或建议,以解决此问题或找到有效实施依赖注入的替代方法。
如果你的目标是实现DI,那么你不想传递一个包含类名的字符串,你想传递一个包含类实例的对象。
这种行为令人费解,因为我已经在 PHPDoc 中明确指出 $B 应该是 B::class 类型,指的是类而不仅仅是一个通用字符串。
没有。
B::class
是一个字符串,其中包含类的名称,即"B"
。你想要一个B
的instance,所以只需使用
B
:
// this means that $obj must be an instance of B
public function __construct(private readonly B $obj)
{
}
public function start()
{
$this->obj::<start typing> // now your IDE will autocomplete B's methods
}
所以,那么你会做:
$b = new B();
$a = new A($b); // This is where the DI happens.
在您的示例中,您是说构造函数的参数只能是确切的字符串
"B"
——这没有任何意义,就好像它永远不会改变一样,您为什么还要费心将它作为参数。
另请注意,如果您是显式类型提示,那么您通常不需要文档块,因为您的 IDE 将能够仅从类型提示中推断出类型,并且同时提供这两种提示可能会使它们发生冲突。除非您要为参数添加说明,否则我会将它们排除在外。否则,确保它们匹配:
/**
* @param B $obj An instance of B.
*/
public function __construct(private readonly B $obj)
{
}
不幸的是,您无法在不污染代码的情况下执行此操作,因为您无法输入提示
$this->B
。解决方法是将它重新分配给另一个变量并键入提示该变量,如下所示:
public function start(){
/** @var B $cls */
$cls = $this->B;
$cls::SayHello();
}
感谢 Marcin Olowsky 的帖子,我找到了解决该问题的方法。正如他所建议的,我不得不稍微污染一下我的代码。通过编写
start()
方法如下:
public function start(){
/**
* @var class-string<B> $c
*/
$c = $this->B;
$c::SayHello();
}
通过这种方式,PHPStorm 知道
$c
的可能性并为我提供正确的类型提示。