为什么PHP允许在构造函数而不是方法上重载类型提示?


Why does PHP allow you to overload typehints on constructors but not on methods?

假设你有:

<?php
interface Type
{}
class SomeType implements Type
{}
class SomeOtherType implements Type
{}

现在,如果你有一个抽象类,它有Type接口作为构造函数依赖,像这样:

<?php
abstract class TypeUser
{
        public function __construct(Type $type)
        {}
}

为什么PHP允许像这样在具体类上重载构造函数:

<?php
class SomeTypeUser extends TypeUser
{
        public function __construct(SomeType $type)
        {}
}
class SomeOtherTypeUser extends TypeUser
{
        public function __construct(SomeOtherType $type)
        {}
}

但是它不允许你对方法做同样的事情,只有构造函数-如果你试图对非构造函数方法这样做,PHP会抛出一个Strict standards错误?

请阅读:用子接口作为新参数覆盖方法参数

TL;DR:如果用窄的类型重载方法参数类型,方法签名将被更改,并使与父方法不兼容。你不能用任意的子类代替TypeUser的实例,因为重载的子类的方法签名与父类的方法签名不兼容,因此没有稳定的,已知的接口来调用这样的实例。

为什么在构造函数中有效?因为要构造一个实例,您(通常)硬编码它的类名:

$user = new SomeTypeUser($parameter);

这里构造函数期望的参数没有歧义,您确切地知道要实例化的是哪个类。

然而,这里的情况并非如此:

function (TypeUser $user) {
    $user->foo($something);
}

那么,$user是什么类的实例呢?SomeTypeUser吗?SomeOtherTypeUser吗?完全不同的东西?$user->foo()的签名是foo(SomeType $type)还是foo(SomeOtherType $type) ?上面的代码是否会在运行时爆炸?谁知道…

PHP允许在继承方法中重载参数,只要它们不是定义为abstract或属于该类扩展/实现的interface

interface Foo {
    public function baz(ArrayObject $array);
}
class Bar implements Foo {
   public function baz(DateTime $dateTime) {
   }
}

abstract class Foo {
    public abstract function baz(ArrayObject $array);
}
class Bar extends Foo {
   public function baz(DateTime $dateTime) {
   }
}

将导致众所周知的错误:

致命错误:Bar::baz()的声明必须与/…/test.php中的Foo::baz(ArrayObject $array)兼容

,

abstract class Foo {
    public function baz(ArrayObject $array) {
    }
}
class Bar extends Foo {
   public function baz(DateTime $dateTime) {
   }
}

很好,因为baz方法不是接口定义方法的实现,也不是父类中的抽象方法。