PHP - есть ли способ различить, когда волшебный метод вызывается через конструкцию PHP или напрямую?

в PHP есть ли способ различить, когда волшебный метод вызывается через конструкцию PHP или напрямую?

Например: public function __clone() {} вызывается каждый раз, когда объект клонируется с помощью ключевого слова clone:

$a = new ObjectA();
$clone = clone $a; // Calls __clone()

Однако кто-то может даже (не знаю, зачем кому-то это делать, но все равно) напрямую вызвать __clone():

$a = new ObjectA();
$clone = $a->__clone(); // Call __clone() directly

То же самое и с другими магическими методами PHP (__call(), __debugInfo и т. д.).

Можем ли мы как-то провести различие между этими двумя случаями?

Спасибо за внимание!

РЕДАКТИРОВАТЬ: Предположим следующий сценарий:

У меня есть динамический прокси-класс, который расширяет базовый класс и внутренне создает экземпляр базового класса только тогда, когда объект эффективно используется:

class A {
   ...
   public function __clone() {
      echo "clone A!!!";
   }
   ...
}

class DynamicProxy extends A {

   protected $a = NULL;

   public function __clone() {
      $this->createA();
      if ( /* __clone() is triggered by `clone $obj`, I need to clone $a: */ )  {
        clone $this->a;
      }
      else {
        // But if someone, for some reason (I do not now why, but it could be, and I would like to be consistent), have called `$obj->__clone();`, I do not have to call `clone $obj`, but proxy the call:
        return $this->a->__clone();
      }

   }

   protected function createA() {
      if (is_null($this->a)) {
        $this->a = new A();
      }
   }
}

$obj = new DynamicProxy();

Почему ты? Метод не должен вести себя по-разному в зависимости от того, кто его вызвал.

deceze 11.04.2018 08:53

Я создаю динамический прокси-сервер, который расширяет базовый класс, переопределяет все его методы, а затем передает вызов внутреннему экземпляру базового класса. Если на прокси-сервере вызывается __clone() или другой волшебный метод, мне нужно знать, был ли он вызван напрямую (по некоторым причинам, которых я не знаю). Если бы он был вызван напрямую, мне пришлось бы вызвать его прямо в моем прокси-сервере __clone() без клонирования внутреннего экземпляра через clone, если нет, мне нужно clone внутренний экземпляр с ключевым словом clone. Это должно соответствовать коду вызывающего абонента.

tonix 11.04.2018 08:58

Я думаю, что более простым и последовательным решением было бы определить поведение один для __clone и просто запретить другое поведение. Например. clone $proxy всегда создает клон обернутого объекта, заключенного в новый прокси.

deceze 11.04.2018 09:00

Вы правы, однако я не знаю, почему кто-то может напрямую вызвать в __clone(). Тем не менее, есть возможность, и я, конечно, если бы это было возможно, согласился бы с кодом вызывающего абонента. Я опубликовал правку, лучше объясняющую, чего я пытаюсь достичь.

tonix 11.04.2018 09:07

Если у вас нет конкретного варианта использования (вы не знаете, зачем кому-то это делать), не пытайтесь удовлетворить этот неопределенный вариант использования.

deceze 11.04.2018 09:07

Спасибо! В любом случае, если вы знаете, как это сделать, напишите, пожалуйста, ответ! Я думал об использовании debug_backtrace(), но похоже, что и clone $obj, и $obj->__clone() имеют один и тот же стек вызовов.

tonix 11.04.2018 09:09
clone - это ключевое слово, а не функция. Вот почему он не отображается в выводе debug_backtrace().
axiac 11.04.2018 09:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
7
96
1

Ответы 1

Многие из ваших предположений неверны, нельзя напрямую вызвать __clone() и получить клон возвращаемого объекта, потому что документы говорит:

void __clone ( void )

а также

An object's __clone() method cannot be called directly.

Это немного вводит в заблуждение, потому что на самом деле его можно вызвать, но результат аналогичен вызову метода любого другого объекта и не создает клон исходного объекта.

Рассмотрим этот фрагмент:

<?php
class Foo {
    function __construct($x) {
        $this->x = $x;
    }

    function __clone() {
        echo "I'm a clone";
    }

    function getX() {
        echo $this->x;
    }
}

$a = new Foo('foo');
$b = $a->__clone(); # > I'm a clone
var_dump($b);       # > NULL
$b->getX();         # Exception: Call to a member function getX() on null

?> 

Метод __clone после клонирования называется на вновь созданном клонированном объекте. Это больше похоже на обратный вызов, и он не создает клон.

Может быть, было бы лучше, если бы PHP выдавал исключение, когда кто-то пытался вызвать __clone напрямую, но есть много вещей, которые PHP мог бы сделать лучше.

Что вы можете сделать, так это тщательно проверить, действительно ли возможно то, что, по вашему мнению, возможно.

Другие вопросы по теме