8

在重构之后,我们的一个类中有这样的东西:

class FooBar
{
    // $foo was $bla before
    private $foo;

    public function setBlubbOnArrayOnlyOnce($value)
    {
        // $this->bla was forgotten during refactoring. Must be $this->foo
        if(!isset($this->bla['blubb'])) {
             $this->foo['blubb'] = $value;
        }
    }
}

所以最后 $this->foo['blubb'] 总是被设置,而不仅仅是一次。发生这种情况是因为 PHP 的神奇方法。我们不希望动态访问字段,所以我想我只是添加一个代码嗅探器规则。但是我没有找到,问我为什么。

PHPStorm 在那里显示了一个动态声明的字段,但我希望它在我们的部署周期中自动失败并使用代码嗅探器(或类似的东西)。

有人对此有想法吗?有什么好的规定吗?我应该自己写吗?怎么写?还是禁用它是不好的做法?

免责声明:我们使用测试,但有时你会遗漏一些东西......首先防止这种情况会很好。另外,请不要想出覆盖魔术方法。我不想在每个班级都有一个特质/抽象。

4

3 回答 3

2

重构之后

首先防止这种情况会很好。

您只能通过在每个重构步骤后运行测试来捕获此类重构错误。这个错误也会冒泡,因为foo['blubb']它被设置为一个特定的值,这应该会在另一个测试中造成不必要的影响——不仅仅是在设置器逻辑的测试中。

我们使用测试,但有时你会错过一些东西......

是的,覆盖率不够高是很常见的。这就是为什么拥有良好的测试覆盖率是所有重构的起点。

这两行在您的报道报告中不是“绿色”的:

   if(!isset($this->bla['blubb'])) {
       $this->foo['blubb'] = $value;

另外,请不要想出覆盖魔术方法。我不想在每个班级都有一个特质/抽象。

您已将其排除在外,但这是捕获属性的一种方法:通过使用魔术函数__set()(用于不可访问的变量)或property_exists()使用Reflection*类来查找。


现在,为时已晚,您需要另一个工具来捕获错误,好的:

该工具需要解析 PHP 文件及其父文件(因为变量范围)并在$this->bla没有先前public|private|protected变量(类属性)声明的情况下查找。这不会指示错误的确切类型,只是在没有声明的情况下访问了“bla”。

可以将其实现为 CodeSniffer 规则。

你也可以试试http://phpmd.org/https://scrutinizer-ci.com/ 。而且,如果您使用的是 PHP7:https ://github.com/etsy/phan

tl;tr

在不运行、评估和分析底层代码的情况下确定确切的错误及其上下文是很复杂的。想想“动态变量名”,你就知道原因了:通过查看源代码,你甚至不知道属性的名称,因为它是在程序流程中动态构建的。静态分析器无法捕捉到这一点。

动态分析器必须跟踪所有事物,此处$this->访问并考虑上下文:!isset(x)。上下文评估可以发现很多常见的编码错误。最后你可以建立一个报告:说 $this->bla 只被访问了 1 次,这表明要么

  • 引入了动态声明的属性,但从未重复使用,建议您可以删除它或将其声明为类属性
  • 或添加上下文评估:当从 isset() 内部访问此变量时 - 访问了未声明属性的不存在键,而没有先前的 set() 等。
于 2016-01-13T11:37:00.180 回答
2

这不是代码嗅探器或 phpstorm 问题。你不能想用代码嗅探器或 IDE 来解决这个问题。IDE、codesniffer、phpdocumentor 等——这是“静态”分析。对于动态分析,您可以使用例如 phpunit。

如果你想检查属性的存在,你必须使用 property_exists() 函数。

class X
{
    public function __get($name)
    {
        $this->{$name} = null;
        return $this->{$name};
    }
}

$x = new X();
var_dump(property_exists($x, 'foo')); // false
var_dump($x->foo); // NULL
var_dump(property_exists($x, 'foo')); // true

或者您可以使用反射属性http://php.net/manual/en/class.reflectionproperty.php

如果你想检查“isset”,你必须知道:

var_dump(isset($x), $x); // false + NULL with notice
$x = null;
var_dump(isset($x), $x); // false + NULL
unset($x);
var_dump(isset($x), $x); // false + NULL without notice

当您进行这种检查时,您可以使用 isset()

但是您应该始终首先检查财产的存在。否则,您的代码可能会有未定义的行为。

于 2016-01-10T15:36:26.343 回答
1

现在在 2017 年,您正在寻找工具 PHPStan。我链接了我为第一次使用的用户写的简短介绍。

它完全满足您的需求!

于 2017-05-21T22:44:33.663 回答