в 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();
Я создаю динамический прокси-сервер, который расширяет базовый класс, переопределяет все его методы, а затем передает вызов внутреннему экземпляру базового класса. Если на прокси-сервере вызывается __clone() или другой волшебный метод, мне нужно знать, был ли он вызван напрямую (по некоторым причинам, которых я не знаю). Если бы он был вызван напрямую, мне пришлось бы вызвать его прямо в моем прокси-сервере __clone() без клонирования внутреннего экземпляра через clone, если нет, мне нужно clone внутренний экземпляр с ключевым словом clone. Это должно соответствовать коду вызывающего абонента.
Я думаю, что более простым и последовательным решением было бы определить поведение один для __clone и просто запретить другое поведение. Например. clone $proxy всегда создает клон обернутого объекта, заключенного в новый прокси.
Вы правы, однако я не знаю, почему кто-то может напрямую вызвать в __clone(). Тем не менее, есть возможность, и я, конечно, если бы это было возможно, согласился бы с кодом вызывающего абонента. Я опубликовал правку, лучше объясняющую, чего я пытаюсь достичь.
Если у вас нет конкретного варианта использования (вы не знаете, зачем кому-то это делать), не пытайтесь удовлетворить этот неопределенный вариант использования.
Спасибо! В любом случае, если вы знаете, как это сделать, напишите, пожалуйста, ответ! Я думал об использовании debug_backtrace(), но похоже, что и clone $obj, и $obj->__clone() имеют один и тот же стек вызовов.
clone - это ключевое слово, а не функция. Вот почему он не отображается в выводе debug_backtrace().






Многие из ваших предположений неверны, нельзя напрямую вызвать __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 мог бы сделать лучше.
Что вы можете сделать, так это тщательно проверить, действительно ли возможно то, что, по вашему мнению, возможно.
Почему ты? Метод не должен вести себя по-разному в зависимости от того, кто его вызвал.