PHP 键入从对象到我的 class 的提示,这不可能吗?

PHP type hinting from object to my class, not possible?

我设置了一个抽象 class,它必须从另一个实例化 class 执行方法。 所以我设置了一个匿名 class 来扩展我的抽象 class 并且在实现的方法中,我想访问那个“其他实例化 class” 的方法,但是对于我的 IDE 为了帮助我找到方法和参数的正确名称,我必须在某处写下类型。

abstract class blah
{
    protected object $reference;
    public function __construct(object $reference)
    {
        $this->reference = $reference;
        $this->hello();
    }
    abstract public function hello();
}

class world
{
    public function __construct()
    {
        new class($this) extends blah {
            public function hello()
            {
                $this->reference->display();
            }
        };
    }

    public function display()
    {
        echo 'hello world';
    }
}

new world();

我无法将类型从“对象”更改为其他类型以允许我的 IDE 显示方法

public function __construct()
    {
        new class($this) extends blah {
            protected world $reference;
            public function hello()
            {
                $this->reference->display();
            }
        };
    }

这抛出 致命错误:blah@anonymous::$reference 的类型必须是对象(如 class blah)

我可以将属性“reference”复制到另一个变量并将类型提示设置为“world”,但这不是最好的

public function __construct()
    {
        new class($this) extends blah {
            protected world $reference;
            public function hello()
            {
                /** @var world $hello */
                $hello = $this->reference;
                $hello->display();
            }
        };
    }

我有什么更好的解决方案?

编辑:这似乎可行,这是最好的方法吗?

new class($this) extends blah {
            /** @var world $reference */
            protected object $reference;
            public function hello()
            {
                $this->reference->display();
            }
        };

如果您只想 IDE 使用它,则使用文档块覆盖类型声明(如果 IDE 支持):

/**
 * @property world $reference
 */
new class($this) extends blah {
    public function hello()
    {
        $this->reference->display();
    }
};

例如PHPStorm 虽然不支持匿名 class,但支持普通 class 描述:

值得理解 为什么 PHP 告诉你不能更改 属性 类型。

由于这是 protected 属性,它可以被 parent 或 child 读取 并写入 class。例如,parent class 可能有这样的方法,这些方法将被 child class:

继承
public function getReference(): object {
    return $this->reference;
}

public function setReference(object $newReference): void {
    $this->reference = $newReference;
}

public function setReferenceToDefault(): void {
    $this->reference = new SomethingBoring;
}

如果您在 child class 中将 world 指定为 $reference 的类型,则 getReference 方法将正常工作 - [ 的任何实例=12=] 是一个 object,所以 return 类型是可以的。但是 setReference()setReferenceToDefault() 方法会失败,因为它们试图分配一些不属于 world.

类型的东西

本质上,通过在基础 class 上声明一个受保护的 属性,您正在为所有 child [=55] 设置一个 contract =]es,PHP 正在为您强制执行。这方面的技术术语是“逆变”:child class 可以 更慷慨 它接受的内容,但它不能拒绝值 parent 会接受。相反的是“协方差”,它适用于 returning 值:child class 可以 更具体 会 return,但不能 return parent class 永远不会。当某物既是输入又是输出时,您会同时受到限制,因此根本无法改变类型。

由于您实际上并没有在基础 class 上使用 属性,因此实际上不需要此限制,因此在这种情况下,一个选择就是 不在基础 class 上定义 属性 或构造函数。在其他情况下,您可能想要使用 traits,它们是“编译器辅助的 copy-and-paste”:它们不会断言两个 [=55= 之间的任何 关系 ] 是的,但它们可以避免您多次编写相同的代码。