Поздние статические привязки PHP (новая статика): как обеспечить совместимость конструктора подкласса и обработать расходящиеся конструкторы? - PullRequest
0 голосов
/ 13 ноября 2018

Для более сложной иерархии классов я немного поиграл с минимальным примером этой проблемы.

Этот класс задан - метод "createOrUpdate ()" может быть изменен:

class A {

    protected $a;
    protected $b;
    protected $c;

    function __construct($a,$b,$c) {
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
    }

    public static function createOrUpdate($a,$b,$c) {
        if(self::exists($b)) {
            someWhateverUpdate();
        } else {
            new static($a,$b,$c);
        }
    }
}

Теперь давайте посмотрим, что произойдет, если мы расширим его:

class B extends A {
    function __construct($a,$b,$c) {
        parent::__construct($a,$b,$c);
    }
}
B::createOrUpdate("rock","this","now");

Работает отлично!

class C extends B {
    function __construct($a,$b) {
        parent::__construct($a,$b,"exactly");
    }
}
C::createOrUpdate("rock","this","now");

Работает также нормально, однако, если кто-то делает параметр createOrUpdate () $ c молча теряется!

class D extends A {
    protected $d;
    function __construct($a,$b,$c,$d) {
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
        $this->d = $d;
    }
}
D::createOrUpdate("rock","this","now");

Ошибка: выдает ArgumentCountError

class E extends A {
        function __construct($b,$c) {
        $this->a = "Lorem";
        $this->b = $b;
        $this->c = $c;
    }
}

D::createOrUpdate("rock","this","now");

Ошибка: работает, но будет вести себя совершенно неожиданно.

Теперь мой вопрос: могу ли я использовать некоторое отражение в createOrUpdate(), чтобы проверить, правильно ли вызван текущий подкласс, реализующий конструктор? Как бы вы справились с этим, если бы кто-то еще мог реализовать дополнительные подклассы в иерархии?

Ответы [ 3 ]

0 голосов
/ 13 ноября 2018

После экспериментов с вводом @nforced я нашел еще одно хорошее решение - сделать конструктор защищенным и окончательным. Теперь подклассы вынуждены реализовывать метод создания (например, create()), который использует нужный конструктор.

class A {

    protected $a;
    protected $b;
    protected $c;

    protected final function __construct($a,$b,$c) {
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
    }

    public static function createOrUpdate($a,$b,$c) {
        if(self::exists($b)) {
            updateMe();
        } else {
            new static($a,$b,$c);
        }
    }
}
0 голосов
/ 21 мая 2019

Вы пытались отправить объект / интерфейс в функцию upsert()?Вот так:

class Parameters {
    private $a;
    private $b;
    private $c;
    ...
    setter&getter for attributes();
}
$paramObj = new Parameters();
$paramObj->setA('a');
...

D::upsert($paramObj);

class D extends A {
    public static function upsert(Parameters $param) {
        if ($param->getA()) {
            doSth();
        } else {
            $instance = new static();
            $instance->a = $param->getB();
        }
    }
}

Это всего лишь мое маленькое мнение, и если я что-то упущу, скажите мне, спасибо.

0 голосов
/ 13 ноября 2018

Как насчет того, чтобы реализовать интерфейс для вашего класса A?

interface interfaceA {
    public function __construct($a, $b, $c);
}

class A implements interfaceA
{
 ...
}

Это заставит каждый расширяющий класс либо не иметь конструктора, либо реализовать тот, который соответствует классу A, или будет выдан PHP Fatal error: Declaration of B::__construct($a, $b) must be compatible with interfaceA::__construct($a, $b, $c).

Вы также можете добавить public static function createOrUpdate($a, $b, $c); к интерфейсу, чтобы заставить все расширяющиеся классы реализовать такой метод.

...