使用不兼容的函数实现接口和错误

Ved*_*eda 6 php oop

我有这个测试脚本:

<?php
interface A
{
  function myfunction();
}

class B implements A
{
  function myfunction($var = "default")
  {
    echo $var;
  }
}

class C extends B
{
  function myfunction()
  {
    echo "myfunction";
  }
}

$c = new C();
$c->myfunction();

$b = new B();
$b->myfunction();
Run Code Online (Sandbox Code Playgroud)

这运行良好和输出myfunctiondefault.

现在,当我删除接口A并让B不再实现A时,如下所示:

<?php
class B
{   
  function myfunction($var = "default")
  {   
    echo $var;
  }   
}   

class C extends B
{   
  function myfunction()
  {   
    echo "myfunction";
  }   
}   

$c = new C();
$c->myfunction();

$b = new B();
$b->myfunction();
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

PHP Strict standards:  Declaration of C::myfunction() should be compatible with that of B::myfunction() in /var/www/test/index.php on line 16

Strict standards: Declaration of C::myfunction() should be compatible with that of B::myfunction() in /var/www/test/index.php on line 16

myfunctiondefault
Run Code Online (Sandbox Code Playgroud)

为什么这首次在第一次工作?我希望在这两种情况下出现错误......

dec*_*eze 3

使用接口时,BC针对接口声明进行独立验证。没关系C extends BC间接地implements A,并且C::myfunction与 兼容A::myfunction,所以没有问题。B::myfunction也兼容A::myfunction,所以也没有问题。

如果没有接口,B::myfunction则该方法的规范声明,并且由于它接受参数但重写C::myfunction不接受,因此STRICT会引发警告。

基本上,您要确保此代码有效:

if ($obj instanceof <classWhoseInterfaceIExpect>) {
    $obj-><interfaceIExpect>();
}
Run Code Online (Sandbox Code Playgroud)

更具体地说:

if ($obj instanceof A) {
    $obj->myfunction();
}
Run Code Online (Sandbox Code Playgroud)

由于A::myfunction规范地声明为不接受任何参数,因此上面的代码将始终有效。但是,如果没有接口:

if ($obj instanceof B) {
    $obj->myfunction('foo');
}
Run Code Online (Sandbox Code Playgroud)

如果B::myfunction是接受参数的规范声明,但C提供的实现不接受参数,那么您就产生了冲突。因此发出警告。这是为了回答为什么它会这样工作的问题,它解释了 PHP 的思想。


警告:是的,即使您使用该接口,这仍然会产生相同的冲突:

interface A { ... }
class B implements A { ... }
class C extends B { ... }  // conflicting implementation to B

$obj = new C;

if ($obj instanceof B) {
    $obj->myfunction('foo');
}
Run Code Online (Sandbox Code Playgroud)

C既是instanceof A又是B,但它不能myfunction同时与 的两种实现兼容。这是创建两个冲突的实现的问题。PHP 是否也应该在这里发出警告是有争议的。PHP 不能鱼与熊掌兼得。它的类型系统可以帮助您尽早发现某些错误;它当然并不完美,并且无法保护您免于从所有可能的角度射中自己的脚。

如果您想避免此类问题,您基本上不应该更改方法签名。或者让变化向下级联;这意味着每个扩展类与其直接父类的方法签名兼容。因此C::myfunction应该接受至少一个可选参数。也许 PHP 应该捕获这种情况,也许它可以被视为一个错误,即您在所有情况下都没有收到警告。同样,这是有争议的,您应该从一开始就避免陷入这种情况。