在PHP中使用子类型参数覆盖方法时的行为不一致

时间:2016-12-08 00:45:23

标签: php oop solid-principles liskov-substitution-principle

我在使用子类型作为方法的参数时,在子类中重写方法时发现了PHP中的不一致。如果我在代码中解释,这是最简单的:

interface IngredientInterface {}
interface TomatoInterface extends IngredientInterface {}

class Tomato implements TomatoInterface {}

class Soup {
    public function __construct(IngredientInterface $ingredient) {}
    public function foo(IngredientInterface $ingredient) {}
}

class TomatoSoup extends Soup {
    public function __construct(TomatoInterface $tomato) { parent::__construct($tomato); }
    public function foo(TomatoInterface $ingredient) { parent::foo($ingredient); }
}

可以预期,覆盖__construct()和foo()方法之间的错误报告行为是相同的,但它们不是。

__construct()方法在PHP 5.5.38,5.6.19和7.0.4中不会生成错误

foo()方法在5.5.38和5.6.19中生成以下错误:

Strict Standards: Declaration of TomatoSoup::foo() should be compatible with Soup::foo(IngredientInterface $ingredient) in H:\webroot\test.php on line 16

和7.0.4:

Warning: Declaration of TomatoSoup::foo(TomatoInterface $ingredient) should be compatible with Soup::foo(IngredientInterface $ingredient) in H:\webroot\test.php on line 16

现在我并不担心错误类型已从E_STRICT更改为E_WARNING,我更担心构造函数解析不正确,但foo()方法不是。

这是PHP中的错误吗?我应该把它报告给bugs.php.net吗?

1 个答案:

答案 0 :(得分:0)

如果你要扩展一个类,为什么你想拥有一个子类型,如果你有一个超类型IngredientInterface你可以将每个对象是IngredienteInterface的子类型传递给构造函数和方法foo(记住好的做法Liskov替换原则),¿为什么要扩展接口? (记住界面隔离原则

如果您在子类中使用超类型,结果是一样的,但我认为您必须再查看一下您的代码,SOLID原则是一个很好的起点

<?php

interface IngredientInterface {}
interface TomatoInterface extends IngredientInterface {}

class Ingredient implements IngredientInterface {}
class Tomato implements TomatoInterface {}

class Soup {
    public function __construct(IngredientInterface $ingredient) {}
    public function foo(IngredientInterface $ingredient) {}
}

class TomatoSoup extends Soup {
    public function __construct(IngredientInterface $tomato) { parent::__construct($tomato); }
    public function foo(IngredientInterface $ingredient) { parent::foo($ingredient); }
}

$tom = new TomatoSoup(new Tomato());
$tom->foo(new Tomato());

$tom2 = new TomatoSoup(new Ingredient());
$tom2->foo(new Ingredient());