调用构造函数时静态 属性 未初始化 php

static property not initialised when constructor is called php

我有一个 parent class 像这样:

abstract class UiElement
{
    protected static ?string $template_name = null;

    public function __construct() {
        if(static::$template_name == null) {
            throw new Exception("static $template_name has not been set in child class", 1);
        }
    }
}

现在我有一个 child class 像这样:

class EmptyContent extends UiElement
{
    protected static ?string $template_name = 'empty-content';

    public function __construct() {
        parent::__construct();
    }
}

并这样称呼它:

$empty = new EmptyContent();

我想确保 child class 在这种情况下 EmptyContent 在定义此 class 时设置了一个非 null 的值。所以我在 parent 的 class 构造函数中进行了检查,但这给了我以下错误:

Fatal error: Uncaught Exception: static $template_name has not been set in child class in /Applications/MAMP/htdocs/private_projects/Foodio/App/index.php:108 Stack trace: #0 /Applications/MAMP/htdocs/private_projects/Foodio/App/index.php(126): UiElement->__construct() #1 /Applications/MAMP/htdocs/private_projects/Foodio/App/index.php(134): failure->__construct() #2 {main} thrown in

据我所知,这是因为 属性 $template_name 在 parent 的构造函数被触发时尚未初始化。

我该怎么做?
如果需要更多信息或说明,请告诉我,以便我添加!

按照建议,您可以使用抽象方法强制实现 类 来定义它:

abstract class UiElement
{
    abstract protected function getTemplateName(): string;
}

然后,实现 类 应该定义什么字符串 return:

class EmptyContent extends UiElement
{
    protected function getTemplateName(): string
    {
        return 'empty-content';
    }
}

这意味着你想在任何地方使用这个值,你必须通过调用 $this->getTemplateName() 来检索它。

您可以创建一个抽象方法而不是 属性:

abstract class UiElement
{
    abstract protected static function getTemplateName(): string;
}

class EmptyContent extends UiElement
{
    protected static function getTemplateName(): string
    {
        return 'empty-content';
    }
}

这样你就不能在没有实现的方法(强制定义)的情况下创建 child class。你应该创建一个没有它的 child:

class EmptyContent extends UiElement
{
}

PHP 将在 运行 您的脚本之前抛出致命错误:Class EmptyContent contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (UiElement::getTemplateName).

现在,这有它的缺点。它不会保护您免于实施空方法或 return 类型错误的方法。那种东西只会在你用的时候在你脸上爆炸。您可以通过向 child 添加一个构造函数来防止这种情况(另外检查您没有 return 一个空字符串):

class EmptyContent extends UiElement
{
    public function __construct()
    {
        if (strlen(self::getTemplateName()) === 0) {
            throw new Exception('Template name is not defined');
        }
    }

    ...

为了强制检查实例化。这必须添加到每个 child(乏味和潮湿),因为您无法在 parent 级别处理它(这将尝试调用其抽象方法并在到达运行时之前失败)。

您还可以采用另一种方法,使其成为常规 (non-abstract) 方法,并在 parent:

中抛出
abstract class UiElement
{
    protected function getTemplateName(): string
    {
        throw new Exception('Method not implemented');
    }
}

仅当您尝试在 child 上使用该方法(而不是在实例化期间)时,这也会失败,该方法未实现该方法,但会在 [=40 内部的一个地方处理=].利弊由你权衡

P.S。 不确定如果只在内部使用该方法是否真的需要静态,但您比我更了解您的推理。