PHP:为什么“严格标准:声明x应该与y兼容”适用于静态方法?

4

PHP警告“Strict standards: Declaration of x should be compatible with y”是指当您编写以下类似代码时会发出警告:

class A {
  function foo($x) {
    ..
  }
}
class B extends A {
  function foo() {
    ..
  }
}

这是有道理的,因为像 "$a" 这样的对象引用,你认为它是一个 A,但在运行时可能会变成一个 B,所以像 $a->foo(3) 这样的动态分派方法调用,可能会以错误数量的参数调用 B::foo()
我的问题是:为什么这个相同的警告也适用于静态方法,它们不是动态分派的?
class X {
  static function bar($x) {
    ..
  }
}
class Y extends X {
  static function bar() {
    ..
  }
}

在这个例子中,函数Y::bar()没有覆盖X::bar(),所以没有理由触发警告,但是PHP仍然会发出警告。为什么?

你需要添加 class B extends Aclass Y extends X 才能使你的示例合法。 - nickb
3个回答

4

就像在Java中一样,您也可以在实例上调用静态方法。因此仍然存在混淆的空间。


当然:你也可以动态地调用静态方法:$a::foo()。多么棒的语言... - Rich
(这与Java不同,静态调用在Java中是静态分派的(根据名称似乎很明智),基于您调用它的对象引用的声明类型。) - Rich

2
与Java不同的是,您可以在实例上调用静态方法,并且它们由该对象的运行时类型分派。因此,Y :: bar()实际上覆盖了X :: bar(),并且应该是兼容的。
<?php

class X {
  static function bar($x) {
    echo "X::bar() with x = $x\n";
  }
}
class Y extends X {
  static function bar() {
    echo "Y::bar()\n";
  }
}

echo "Static dispatch:\n";
X::bar(1);
Y::bar();

echo "Dynamic dispatch of a static method (surprise!):\n";
$arr = array(new X(), new Y());
foreach ($arr as $a) {
  $a::bar(1);
}

将输出(在这里运行):
Warning: Declaration of Y::bar() should be compatible with X::bar($x) in /in/phAWB on line 12
Static dispatch:
X::bar() with x = 1
Y::bar()
Dynamic dispatch of a static method (surprise!):
X::bar() with x = 1
Y::bar()

等效代码在Java中无法编译。)

-2

这个警告与现实世界无关。忘记学术例子,看一个真实的例子。有成千上万的锁制造商生产带组合触摸板和钥匙备用的门锁。他们会使用两个死锁 - 一个用于组合锁,一个用于钥匙锁吗?不会。死锁被抽象为钥匙锁和组合锁。这就是现实世界。PHP(对于这个问题很快拒绝任何错误报告)想在这个问题上把头埋进沙子里。

class abstractLock
{
    private $locked = true;
    public function unlock()
    {
       $this->locked=false;
    }
    public function lockStatus()
    {
        if($this->locked) return "Locked\n";
        else return "Unlocked\n";
    }
}

class combinationLock extends abstractLock
{
    private $combination = '32-10-21'; // combination
    public function unlock($combination)        // unlock WITH combination
    {
        if($this->combination == $combination) parent::unlock();
    }
}

class paddleLock extends abstractLock
{
    private $key = '100,70,80,30,50,90,60,40,100'; // ridge heights
    public function unlock($key)                            // unlock WITH key
    {
        if($this->key == $key) parent::unlock();
    }
} 

$lock1 = new paddleLock();
echo "paddleLock is " . $lock1->lockStatus();
$lock1->unlock('100,70,80,30,50,90,60,40,100');
echo "paddleLock is " . $lock1->lockStatus();

$lock2 = new combinationLock();
echo "combinationLock is " . $lock2->lockStatus();
$lock2->unlock('32-10-21');
echo "combinationLock is " . $lock2->lockStatus();

PHP不愿意讨论这个问题。因此,我的所有代码都将包含以下内容,以消除这个PHP bug

// FIX PHP's bogus warning of: PHP Warning: Declaration of * should be compatible with
if (PHP_MAJOR_VERSION >= 7) {
    set_error_handler(function ($errno, $errstr) {
       return strpos($errstr, 'Declaration of') === 0;
    }, E_WARNING);
}

1
“现实世界”与PHP方法继承规则有什么关系?您的示例违反了这些规则,因此PHP的警告在您的情况下是正确的。方法接口是一种契约;如果我有一个从“abstractlock”继承的对象,我应该保证它将具有“unlock()”方法,因为那是您声明的接口。 “combinationlock”类不遵守该接口,因此出现了PHP的错误。请参见https://en.wikipedia.org/wiki/Liskov_substitution_principle 。类层次结构是接口合同和代码重用机制,而不是分类法。 - Rich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接