在 PHP 7 中静音“声明......应该兼容”警告

新手上路,请多包涵

升级到 PHP 7 后,日志几乎被这种错误所阻塞:

PHP Warning: Declaration of Example::do($a, $b, $c) should be compatible with ParentOfExample::do($c = null) in Example.php on line 22548

如何在 PHP 7 中消除这些错误并且仅消除这些错误?

  • 在 PHP 7 之前,它们是 E_STRICT 可以轻松处理 的警告类型。现在它们只是普通的旧警告。因为我 确实 想了解其他警告,所以我不能完全关闭所有警告。

  • 我没有精神能力重写这些遗留 API,更不用说使用它们的所有软件了。你猜怎么着,没有人会为此付出代价。我一开始就没有开发它们,所以我不应该受到指责。 (单元测试?十年前还不流行。)

  • 我想尽可能避免使用 func_get_args 和类似的 任何 技巧。

  • 我真的不想降级到 PHP 5。

  • 我还想知道其他错误和警告。

有没有一种干净漂亮的方法来完成这个?

原文由 sanmai 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 430
2 个回答

1.解决方法

由于并非总是可以更正 您未编写 的所有代码,尤其是遗留代码…

 if (PHP_MAJOR_VERSION >= 7) {
    set_error_handler(function ($errno, $errstr) {
       return strpos($errstr, 'Declaration of') === 0;
    }, E_WARNING);
}

对于以 Declaration of 开头的警告,此错误处理程序返回 true ,这基本上告诉 PHP 已处理警告。这就是 PHP 不会在别处报告此警告的原因。

另外,此代码只能在 PHP 7 或更高版本中运行。


如果您希望仅针对特定代码库发生这种情况,那么您可以检查有错误的文件是否属于该代码库或感兴趣的库:

 if (PHP_MAJOR_VERSION >= 7) {
    set_error_handler(function ($errno, $errstr, $file) {
        return strpos($file, 'path/to/legacy/library') !== false &&
            strpos($errstr, 'Declaration of') === 0;
    }, E_WARNING);
}

2.妥善解决

至于实际修复其他人的遗留代码,在许多情况下,这可以在简单和可管理之间完成。在下面的示例中,类 BA 的子类。请注意,您不一定会通过遵循这些示例来消除任何 LSP 违规行为。

  1. 有些情况很容易。如果在子类中缺少默认参数,只需添加它并继续。例如在这种情况下:
    Declaration of B::foo() should be compatible with A::foo($bar = null)

你会这样做:

 - public function foo()
+ public function foo($bar = null)
  1. 如果您在子类中添加了额外的约束,请将它们从定义中删除,同时移入函数体内。
    Declaration of B::add(Baz $baz) should be compatible with A::add($n)

您可能希望根据严重性使用断言或抛出异常。

 - public function add(Baz $baz)
+ public function add($baz)
  {
+     assert($baz instanceof Baz);

如果您发现约束仅用于文档目的,请将它们移到它们所属的位置。

 - protected function setValue(Baz $baz)
+ /**
+  * @param Baz $baz
+  */
+ protected function setValue($baz)
  {
+     /** @var $baz Baz */
  1. 如果子类的参数比超类少,并且可以在超类中将它们设为可选,只需在子类中添加占位符即可。给定错误字符串:
    Declaration of B::foo($param = '') should be compatible with A::foo($x = 40, $y = '')

你会这样做:

 - public function foo($param = '')
+ public function foo($param = '', $_ = null)
  1. 如果您看到子类中需要一些参数,请将此事交由您处理。
 - protected function foo($bar)
+ protected function foo($bar = null)
  {
+     if (empty($bar['key'])) {
+         throw new Exception("Invalid argument");
+     }
  1. 有时更改超类方法以完全排除可选参数可能更容易,回到 func_get_args 魔术。不要忘记记录缺少的参数。
      /**
   +  * @param callable $bar
      */
- public function getFoo($bar = false)
+ public function getFoo()
  {
+     if (func_num_args() && $bar = func_get_arg(0)) {
+         // go on with $bar

当然,如果您必须删除多个参数,这会变得非常乏味。

  1. 如果你严重违反替代原则,事情就会变得有趣得多。如果您没有类型参数,那么这很容易。只需将所有额外参数设为可选,然后检查它们是否存在。给定错误:
    Declaration of B::save($key, $value) should be compatible with A::save($foo = NULL)

你会这样做:

 - public function save($key, $value)
+ public function save($key = null, $value = null)
  {
+     if (func_num_args() < 2) {
+         throw new Exception("Required argument missing");
+     }

请注意,我们不能在这里使用 func_get_args() 因为它不考虑默认(未传递)参数。我们只剩下 func_num_args()

  1. 如果你有一个带有发散接口的类的整个层次结构,那么进一步发散它可能会更容易。在每个类中重命名具有冲突定义的函数。然后在这些类的单个中间父级中添加代理函数:
    function save($arg = null) // conforms to the parent
   {
       $args = func_get_args();
       return $this->saveExtra(...$args); // diverged interface
   }

这样 LSP 仍然会被违反,尽管没有警告,但您可以保留子类中的所有类型检查。

原文由 sanmai 发布,翻译遵循 CC BY-SA 4.0 许可协议

对于那些想要实际更正您的代码以使其不再触发警告的人:我发现了解您可以向子类中的重写方法添加其他参数很有用,只要您为它们提供默认值即可。因此,例如,虽然这会触发警告:

 //"Warning: Declaration of B::foo($arg1) should be compatible with A::foo()"
class B extends A {
    function foo($arg1) {}
}

class A {
    function foo() {}
}

这不会:

 class B extends A {
    function foo($arg1 = null) {}
}

class A {
    function foo() {}
}

原文由 Matt Browne 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏