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. 正确的解决方案
如果要修复别人遗留下来的代码,有一些情况是比较容易和可管理的。在下面的例子中,类 B
是类 A
的子类。请注意,按照这些示例并不一定会消除任何LSP违规。
有些情况非常简单。如果子类中缺少默认参数,则只需添加它并继续进行。例如,在这种情况下:
Declaration of B::foo() should be compatible with A::foo($bar = null)
你会做:
- public function foo()
+ public function foo($bar = null)
如果在子类中添加了额外的限制条件,请将它们从定义中删除,并将其移到函数体内部。
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)
{
+
如果您的子类比超类拥有更少的参数,并且您可以使它们在超类中成为可选的,请在子类中添加占位符。给定错误字符串:
Declaration of B::foo($param = '') should be compatible with A::foo($x = 40, $y = '')
您需要做的是:
- public function foo($param = '')
+ public function foo($param = '', $_ = null)
如果你在子类中看到有些参数被要求必须提供,请自行解决这个问题。
- protected function foo($bar)
+ protected function foo($bar = null)
{
+ if (empty($bar['key'])) {
+ throw new Exception("Invalid argument");
+ }
有时更容易修改超类方法,将可选参数完全排除,而回退到func_get_args
魔术方法。不要忘记记录缺失的参数。
- public function getFoo($bar = false)
+ public function getFoo()
{
+ if (func_num_args() && $bar = func_get_arg(0)) {
+
如果你需要移除多个参数,这可能会变得非常繁琐。
如果您存在严重的替换原则违规情况,则情况变得更加有趣。如果没有输入类型的参数,则很容易。只需将所有额外的参数设置为可选,并检查它们是否存在。给定错误:
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()
。
如果你有一整个类层次结构,并且具有不同的接口,那么将其进一步分化可能更容易。在每个类中将具有冲突定义的函数重命名。然后在这些类的单个中间父级中添加代理函数:
function save($arg = null) // conforms to the parent
{
$args = func_get_args();
return $this->saveExtra(...$args); // diverged interface
}
这样LSP仍然会被违反,尽管没有警告,但您可以保留子类中所有类型检查。