【问题标题】:PhpStorm: what is the best way to handle "return value is expected to be ..." warning?PhpStorm:处理“返回值预计为......”警告的最佳方法是什么?
【发布时间】:2022-01-06 23:00:18
【问题描述】:

我的 PHP 代码结构如下(这种结构是不可更改的):

class BaseFooClass {
}

class ChildFooClass1 extends BaseFooClass {
}

class ChildFooClass2 extends BaseFooClass {
}

//... (there are like a hundred child foo classes)

class BaseBarClass {
    protected function getFooBase($input) : BaseFooClass 
    {
        $class = "ChildFooClass" . $input;
        return new $class();
    }
}

class ChildBarClass1 extends BaseBarClass {
    public function getFoo() : ChildFooClass1 
    {
        return $this->getFooBase(1);
    }
}

现在在ChildBarClass1 中的getFoo() 函数中,PhpStorm 将给出警告“返回值预计为 'ChildFooClass1','BaseFooClass' 返回”并且它正确地这样做了。

但是我想要一个干净的代码,没有警告而不改变这个结构。

我通过添加 PHP doc 尝试了以下操作:

    public function getFoo() : ChildFooClass1 
    {
        /** @var ChildFooClass1 $foo **/
        $foo = $this->getFooBase(1);
        return $foo;
    }

但是这次它给出了一个不同的警告“不必要的局部变量”并要求我将其内联。

有没有消除警告的好方法?

编辑:也尝试了以下方法,但仍然出现“预期返回值...”的警告。

    /**
     * @return ChildFooClass1
     */
    public function getFoo() : ChildFooClass1 
    {
        return $this->getFooBase(1);
    }

【问题讨论】:

  • “但是这次它给出了不同的警告“不必要的局部变量”并要求我将其内联。”这样的警告应该不再出现在未来2022.1 版本(甚至可能在即将到来的 2021.3 版本中,但不太可能)youtrack.jetbrains.com/issue/WI-59730
  • 如果在文档块中添加@return 会怎样?相当肯定应该有效
  • @Martijn 我也试过了(实际上是 PhpStorm 自动生成的),但不幸的是警告仍然存在。我想最坏的情况是我可以等到未来的版本并暂时接受不必要的警告,至少它不是永远的。

标签: php inheritance phpstorm phpdoc


【解决方案1】:

假设您的代码 BaseBarClass::getFooBase() 返回 BaseFooClass 而 ChildBarClass1::getFoo() 想要返回 ChildFooClass1。

这违反了 php 的 Covariance/Contravariance 规则,根据该规则,您只能返回更具体的类型而不是更通用的类型,PhpStorm 会正确地警告您。

因此,您可以遵守此规则 (good) 并重构代码或抑制错误 (bad)。

例如:

// corrected type:
class ChildBarClass1 extends BaseBarClass
{
    public function getFoo(): BaseFooClass
    {
        return $this->getFooBase(1);
    }
}

// error suppressed:
class ChildBarClass1 extends BaseBarClass
{
    public function getFoo(): ChildFooClass1
    {
        /** @noinspection PhpIncompatibleReturnTypeInspection */
        return $this->getFooBase(1);
    }
}

【讨论】:

  • 感谢您对规则的回答和解释。我的案件有没有遵守规则的好方法?我的getFooBase 函数做了很多依赖于子类内容的常见事情。
  • Foo 类有一个受保护的数组成员变量,它被硬写在每个 ChildFooClass 文件中,并且每个 ChildFooClass 都不同(如果你知道 Laravel 它是 Model 中的“$fillable”,getFooBase 正在取值从数据库中将其填充到 $fillable 中,然后返回对象)并且 getFooBase 循环遍历数组并使用该数组执行操作。最后的 new() 将循环的结果作为参数。因此,看起来我要么需要在每个单独的子 foo 类中编写重复代码,要么不遵守协方差规则。有什么好办法吗?
  • 也许您可以通过更具体的问题示例提出一个新问题?最好使用您在评论中添加的信息?在此之前,您可以尝试一些事情。 1) 问问自己,输入ChildFooClass1 的返回是否真的很重要? getFoo() 的调用者需要知道它是 ChildFooClass1 还是 BaseFooClass 就足够了? 2)没有看到一个实际的例子,很难确定,但通常父类中的抽象方法(在子类中实现)在这种情况下会有所帮助。
  • 3) 也许您实际上不需要继承,可以使用 trait 代替?
  • @cr001 为什么不从protected function getFooBase($input) 签名中删除` : BaseFooClass` 部分?在您的 simple 示例代码中,它实际上并没有在任何地方使用(getFooBase() 调用的结果——它是立即从函数返回的。需要一个更具体/真实的示例才能更好地为您提供建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 2017-11-15
  • 1970-01-01
  • 2021-10-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多