PHP7 .3: Как я могу унаследовать защищенное свойство stati c с максимально возможной областью действия без повторного выделения его в дочернем классе? - PullRequest
3 голосов
/ 05 февраля 2020

В приложении, над которым я работаю, часть Model стека MVC предназначена для работы через одиночные игры; каждая модель имеет __getInstanceMethod, равный

protected static $singleton;
public static function __getInstance(): self {
    if(self::$singleton === null) {
        self::$singleton = __CLASS__;
        self::$singleton = new self::$singleton;
    }
    return self::$singleton;
}

Конечным результатом является то, что если __getInstance () вызывается дважды для одного и того же класса модели, он возвращает один и тот же точный объект оба раза.

Я пытался чтобы уменьшить дублирование кода путем перемещения метода __getInstance () в родительский класс модели BaseModel, отредактировав его следующим образом.

class BaseModel {
    protected static $singleton;
    public static function __getInstance(): self {
        if (static::$singleton === null) {
            static::$singleton = static::class;
            static::$singleton = new static::$singleton();
        }
        return static::$singleton;
    }
}
class AModel extends BaseModel {
    protected static $singleton;
    /** ... */
}
class BModel extends BaseModel {
    protected static $singleton;
    /** ... */
}

AModel::__getInstance(); // AModel
BModel::__getInstance(); // BModel

Проблема в том, что мне нужно вручную добавить свойство $ singleton для каждого элемента Класс Model, в противном случае мне всегда будет возвращен экземпляр первого класса Model, для которого я вызвал метод.

class BaseModel {
    protected static $singleton;
    public static function __getInstance(): self {
        if (static::$singleton === null) {
            static::$singleton = static::$class;
            static::$singleton = new static::$singleton();
        }
        return static::$singleton;
    }
}
class AModel extends BaseModel {}
class BModel extends BaseModel {}

AModel::__getInstance(); // AModel
BModel::__getInstance(); // Still AModel

Есть ли способ избежать этого?

1 Ответ

3 голосов
/ 05 февраля 2020

Вы можете переключиться на "карту экземпляра" , например:

<?php
declare(strict_types=1);

error_reporting(-1);
ini_set('display_errors', 'On');

class BaseModel
{
    protected static $instances = [];

    public static function __getInstance(): self
    {
        if (!isset(static::$instances[static::class])) {
            static::$instances[static::class] = new static();
        }

        return static::$instances[static::class];
    }
}

class AModel extends BaseModel
{
}

class BModel extends BaseModel
{
}

echo get_class(AModel::__getInstance()), "\n";
echo get_class(BModel::__getInstance());

https://3v4l.org/qG0qJ


и с 7.4+ это можно упростить до:

<?php
declare(strict_types=1);

error_reporting(-1);
ini_set('display_errors', 'On');

class BaseModel
{
    private static array $instances = [];

    public static function __getInstance(): self
    {
        return static::$instances[static::class] ??= new static();
    }
}
...