Ограничение того, что может создать класс PHP

У меня два класса: «А» и «В». В логике приложения никому не разрешено создавать объект класса «В», кроме класса «А». Но, поскольку я не хочу иметь два класса в одном файле, я не могу ограничить его свойством «private».

Можно ли создать такое ограничение? Если кто-то другой, кроме "А", попытается создать объект класса "Б", вы говорите, отвали!?


person Emil    schedule 15.10.2010    source источник
comment
Мне интересно знать, почему вы хотели бы это сделать?   -  person rojoca    schedule 15.10.2010


Ответы (5)


Это настолько хакерски, насколько это возможно, и вы не должны его использовать. Я публикую это только потому, что мне нравятся хакерские вещи;) Кроме того, это вызовет ошибку, если включено E_STRICT сообщение об ошибке:

class B
{
    private function __construct() {}

    public function getInstance() {
        if (!isset($this) || !$this instanceof A) {
            throw new LogicException('Construction of B from a class other than A is not permitted.');
        }

        return new self;
    }
}

class A
{
    function someMethod() {
        $b = B::getInstance(); // note that I'm calling the non-static method statically!
    }
}

Причина, по которой это работает, — это «функция», которую можно увидеть во втором пример этой страницы руководства.

person NikiC    schedule 15.10.2010
comment
Имейте в виду, что, хотя это и работает, PHP выдает E_STRICT. - person BoltClock; 16.10.2010
comment
если кто-то действительно хочет использовать этот хак, ему лучше включить error_reporting(-1) и использовать @$b = B::getInstance(); , но я не уверен, что @ подавит все предупреждения/ошибки, сгенерированные в методе getInstance - person Sudhi; 21.10.2011

Вы можете проверить обратную трассировку:

class B
{
    public function __construct()
    {
        $chain = debug_backtrace();
        $caller = $chain[1]['class'];

        if ('A' != $caller) {
            throw new Exception('Illegal instantiation');
        }
    }
}
person Nev Stokes    schedule 15.10.2010
comment
Если вы пойдете по этому пути, вы должны профилировать, как он работает. Функция называется debug _backtrace не просто так. - person Gordon; 15.10.2010
comment
О, более чем согласился Гордон, но это решение! - person Nev Stokes; 15.10.2010

В конструкторе B потребуйте, чтобы A был передан. Когда вы затем хотите получить B из A, просто создайте B и передайте A. Когда вызывается новый B, он потребует передачи A.

class A
{
    private $b;

    private function getB()
    {
        if (null === $this->b)
        {
            $this->b    = new B($this);
        }

        return $this->b;
    }
}

class B
{
    public function __construct(A $a)
    {

    }
}
person MANCHUCK    schedule 15.10.2010
comment
На самом деле думать об этом правильно. Позвольте мне проверить, что произойдет, если A будет следовать одноэлементному шаблону. - person MANCHUCK; 15.10.2010
comment
Вы также можете клонировать существующий экземпляр B. И поскольку экземпляр класса все равно будет создан теми, кто пишет код, в ограничении доступа таким образом очень мало смысла. Класс A и B также будет сложно тестировать таким образом, поскольку A имеет жестко закодированную зависимость, а B всегда требует A Mock. Превращение B в синглтона полностью отправит вас в ад. IMO ценность, которую вы получаете за ограничение доступа, не стоит штрафа. - person Gordon; 15.10.2010
comment
Возможно, лучшим решением было бы использовать PHPcs. Создание собственного сниффера для определения погоды B было вызвано из A. Каждый раз, когда вы хотите развернуть код, просто запускайте сниффер и смотрите, ловит ли он его - person MANCHUCK; 15.10.2010

Возможно, вы захотите использовать что-то вроде этого:

class A
{
        protected function __construct ()
        {
        }
}

class B extends A
{
        public function __construct ()
        {
                $a = new A();
        }
}

$b = new B();
person matthiasz    schedule 15.10.2010

Используйте get_called_class, чтобы узнать, какой класс пытается создать экземпляр объекта:

class B
{
        public function __construct ()
        {
                if(get_called_class() != 'A') {
                    //booboo
                }
        }
}
person rthrwht    schedule 16.10.2010
comment
Это будет работать только в том случае, если A будет расширять B и B, а не A. - person NikiC; 16.10.2010