最近,当使用PhpStorm实现PHP应用程序时,我遇到了一个有趣的情况。以下代码段说明了此问题。
interface I{
function foo();
}
trait T{
/**
* @return string
*/
public function getTraitMsg()
{
return "I am a trait";
}
}
class A implements I{
use T;
function foo(){}
}
class C implements I{
use T;
function foo(){}
}
class B {
/**
* @param I $input <===Is there anyway to specify that $input use T?
*/
public function doSomethingCool($input){ //An instance of "A" or "C"
$msg = $input -> getTraitMsg(); //Phpstorm freaks out here
}
}
我的问题在评论中。如何指示$input
参数实现I
并使用T
?
答案 0 :(得分:4)
它有点乱,但你可以使用class_uses
它返回使用过的特征列表。并在PHPDoc中将T
添加为@param类型以用于自动完成
class B {
/**
* @param I|T $input <===Is there anyway to specify that $input use T?
*/
public function doSomethingCool($input){ //An instance of "A" or "C"
$uses = class_uses(get_class($input));
if (!empty($uses['T'])) {
echo $input->getTraitMsg(); //Phpstorm freaks out here
}
}
}
答案 1 :(得分:2)
AFAIK你不能以这种方式输入暗示特征用法(@param
只接受标量类型或类/接口+一些keywords)。
您的理想解决方案会将getTraitMsg()
声明放入I
界面。
如果无法做到这一点..那么您可以指定只能传递A
或C
的实例(因为它们使用该特征):
/**
* @param A|C $input
*/
public function doSomethingCool($input)
{
$msg = $input->getTraitMsg(); // PhpStorm is good now
}
如果事先知道这些可能类别的名称(例如,它的库代码和最终类可以是每个新项目中的任何内容,甚至可以随时添加到当前项目中)..那么我建议使用安全措施,无论如何都应该使用此类代码(通过method_exists()
):
/**
* @param I $input
*/
public function doSomethingCool($input)
{
if (method_exists($input, 'getTraitMsg')) {
$msg = $input->getTraitMsg(); // PhpStorm is good now
}
}
为什么要使用保障措施?因为您可以传递实现K
但不使用特征I
的另一个类T
的实例。在这种情况下,没有后卫的代码就会中断。
只是为了澄清:您可以使用@param I|T $input
指定该方法需要实现I
的实例或使用T
..但它只适用于PhpStorm(不确定)关于其他IDE) - AFAIK它不被实际的PHPDocumentor接受,似乎不适合PHPDoc proposed standard。
答案 2 :(得分:0)
&#34; // Phpstorm在这里吓坏了#34; - 不,它不是。它只是试图告诉你你的代码不正确。
方法doSomethingCool()
的合同并不要求$input
公开任何名为getTraitMsg()
的方法。 docblock表示它应该实现interface I
,但docblock不是代码,它只会帮助PhpStorm帮助您进行验证和建议。
因为您没有键入 - 提示参数$input
,所以代码:
$b = new B();
$b->doSomethingCool(1);
有效但尝试执行第$msg = $input -> getTraitMsg();
行时会崩溃。
如果您想在getTraitMsg()
上致电$input
,则必须:
$input
; $input
类型公开了一个名为getTraitMsg()
的方法。第一步,您现有的班级B
代码应为:
class B {
/**
* @param I $input
*/
public function doSomethingCool(I $input) {
$msg = $input -> getTraitMsg();
}
}
请在参数列表中注明参数I
前面的$input
类型。
完成下一步的最简单方法是将方法getTraitMsg()
声明为interface I
:
interface I {
function foo();
function getTraitMsg();
}
现在,代码:
$b = new B();
$b->doSomethingCool(1);
在到达行$b->doSomethingCool(1);
时(即在进入函数之前)抛出异常。这是PHP的方法告诉你没有使用正确的参数调用该方法。您必须传递一个实现interface I
的对象,无论它是A
还是C
。它可以是实现interface I
的任何其他类型,如果它使用trait T
来实现它,则无人问津。