如何在 PHPDoc 中指示 "include trait" 的参数

How to indicate a parameter that "include trait" in PHPDoc

最近我 运行 在使用 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

AFAIK 你不能以这种方式键入提示特征用法(@param 只接受标量类型或 classes/interfaces + 一些 keywords)。

理想的解决方案 是将 getTraitMsg() 声明放入 I 接口。

如果这不能完成..那么你可以指定只有 AC 的实例可以在这里传递(因为它们利用了那个特征):

/**
 * @param A|C $input
 */
public function doSomethingCool($input)
{
    $msg = $input->getTraitMsg();  // PhpStorm is good now
}

如果事先不知道这些可能的 classes 的名称(例如,它是一个库代码,最终 classes 可以是每个新项目中的任何东西,甚至可以随时添加到当前项目中) .. 然后我建议 使用安全措施 ,无论如何你都应该使用这样的代码(通过 method_exists()):

/**
 * @param I $input
 */
public function doSomethingCool($input)
{
    if (method_exists($input, 'getTraitMsg')) {
        $msg = $input->getTraitMsg();  // PhpStorm is good now
    }
}

为什么要使用保障?因为您可能会传递另一个实现 I 但不使用特征 T 的 class K 的实例。在这种情况下,没有守卫的代码将被破坏。


澄清一下:您可以使用 @param I|T $input 来指定该方法期望实例实现 I 或使用 T .. 但它仅适用于 PhpStorm(不确定其他IDEs) -- AFAIK 它不被实际的 PHPDocumentor 接受并且似乎不适合 PHPDoc proposed standard.

它有点笨拙,但您可以使用 class_uses 它 returns 已用特征列表。并在 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
        }
    }
}

"//Phpstorm 在这里吓坏了" -- 不,不是。它只是试图向您发出信号,表明您的代码不正确。

方法 doSomethingCool() 的契约不要求 $input 公开任何名为 getTraitMsg() 的方法。 docblock 说它应该实现 interface I 但 docblock 不是代码,它只帮助 PhpStorm 帮助您进行验证和建议。

因为您没有对参数 $input 进行类型提示,代码:

$b = new B();
$b->doSomethingCool(1);

是有效的,但它在尝试执行行 $msg = $input -> getTraitMsg();.

时立即崩溃

如果您想在 $input 上致电 getTraitMsg(),您必须:

  • 声明$input的类型;
  • 确保 $input 的声明类型公开了一个名为 getTraitMsg().
  • 的方法

对于第一步,您现有的 class B 代码应为:

class B {
    /**
     * @param I $input 
     */
    public function doSomethingCool(I $input) {
       $msg = $input -> getTraitMsg();
    }
}

请在参数列表中的参数$input前备注类型I

完成下一步的最简单方法是将方法 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 来实现它。