Так скажем, вы не хотите, шаблон Observer, потому что она требует, чтобы вы изменили свои методы класса, чтобы справиться с задачей слушания, и хочется чего-то общего. Предположим, вы не хотите использовать наследование extends
, потому что вы уже можете наследовать свой класс из какого-либо другого класса. Было бы здорово иметь общий способ сделать любого класса подключаемого без особых усилий? Вот как:
<?php
////////////////////
// PART 1
////////////////////
class Plugin {
private $_RefObject;
private $_Class = '';
public function __construct(&$RefObject) {
$this->_Class = get_class(&$RefObject);
$this->_RefObject = $RefObject;
}
public function __set($sProperty,$mixed) {
$sPlugin = $this->_Class . '_' . $sProperty . '_setEvent';
if (is_callable($sPlugin)) {
$mixed = call_user_func_array($sPlugin, $mixed);
}
$this->_RefObject->$sProperty = $mixed;
}
public function __get($sProperty) {
$asItems = (array) $this->_RefObject;
$mixed = $asItems[$sProperty];
$sPlugin = $this->_Class . '_' . $sProperty . '_getEvent';
if (is_callable($sPlugin)) {
$mixed = call_user_func_array($sPlugin, $mixed);
}
return $mixed;
}
public function __call($sMethod,$mixed) {
$sPlugin = $this->_Class . '_' . $sMethod . '_beforeEvent';
if (is_callable($sPlugin)) {
$mixed = call_user_func_array($sPlugin, $mixed);
}
if ($mixed != 'BLOCK_EVENT') {
call_user_func_array(array(&$this->_RefObject, $sMethod), $mixed);
$sPlugin = $this->_Class . '_' . $sMethod . '_afterEvent';
if (is_callable($sPlugin)) {
call_user_func_array($sPlugin, $mixed);
}
}
}
} //end class Plugin
class Pluggable extends Plugin {
} //end class Pluggable
////////////////////
// PART 2
////////////////////
class Dog {
public $Name = '';
public function bark(&$sHow) {
echo "$sHow<br />\n";
}
public function sayName() {
echo "<br />\nMy Name is: " . $this->Name . "<br />\n";
}
} //end class Dog
$Dog = new Dog();
////////////////////
// PART 3
////////////////////
$PDog = new Pluggable($Dog);
function Dog_bark_beforeEvent(&$mixed) {
$mixed = 'Woof'; // Override saying 'meow' with 'Woof'
//$mixed = 'BLOCK_EVENT'; // if you want to block the event
return $mixed;
}
function Dog_bark_afterEvent(&$mixed) {
echo $mixed; // show the override
}
function Dog_Name_setEvent(&$mixed) {
$mixed = 'Coco'; // override 'Fido' with 'Coco'
return $mixed;
}
function Dog_Name_getEvent(&$mixed) {
$mixed = 'Different'; // override 'Coco' with 'Different'
return $mixed;
}
////////////////////
// PART 4
////////////////////
$PDog->Name = 'Fido';
$PDog->Bark('meow');
$PDog->SayName();
echo 'My New Name is: ' . $PDog->Name;
В части 1, это то, что вы можете включить с require_once()
вызова в верхней части вашего PHP скрипта. Он загружает классы, чтобы сделать что-то подключаемое.
В части 2 мы загружаем класс. Примечание. Мне не нужно было делать ничего особенного для класса, что существенно отличается от шаблона Observer.
В части 3 мы превращаем наш класс в «подключаемый» (т. Е. Поддерживаем плагины, которые позволяют нам переопределять методы и свойства класса). Так, например, если у вас есть веб-приложение, у вас может быть реестр плагинов, и вы можете активировать плагины здесь. Также обратите внимание на функцию Dog_bark_beforeEvent()
. Если я установил $mixed = 'BLOCK_EVENT'
перед оператором return, он заблокирует собаку от лая и также заблокирует Dog_bark_afterEvent, потому что не будет никакого события.
В части 4 это нормальный код операции, но обратите внимание, что то, что вы, возможно, думаете, запускаете, вообще не работает. Например, собака не объявляет, что это имя «Фидо», но «Коко». Собака не говорит «мяу», но «Уоф». И когда вы захотите взглянуть на имя собаки после этого, вы обнаружите, что это «Разное», а не «Коко». Все эти переопределения были представлены в части 3.
Как это работает? Ну, давайте исключаем eval()
(все говорят, что это «зло») и исключаем, что это не шаблон наблюдателя. Таким образом, способ, которым это работает, - это скрытый пустой класс под названием Pluggable, который не содержит методов и свойств, используемых классом Dog. Таким образом, поскольку это происходит, магические методы будут задействованы для нас. Вот почему в частях 3 и 4 мы связываемся с объектом, полученным из класса Pluggable, а не с самим классом Dog.Вместо этого мы позволяем классу Plugin «касаться» объекта Dog для нас. (Если это какой-то дизайн, о котором я не знаю - сообщите мне.)
Обратите внимание, что для PHP> = 5.0 вы можете реализовать это, используя интерфейсы Observer/Subject, определенные в SPL: http://www.php.net/manual/en/class.splobserver.php – 2010-02-25 14:47:31
Педантичное примечание: это не пример шаблона Observer. Это пример [`Mediator Pattern`] (http://sourcemaking.com/design_patterns/mediator). Истинные наблюдатели - это чистое уведомление, отсутствует передача сообщений или условное уведомление (а также нет центрального администратора для контроля уведомлений). Это не делает ответ * неправильным *, но следует отметить, что люди, вызывающие вещи, не могут ошибаться ... – ircmaxell 2011-02-09 23:35:07
Обратите внимание, что при использовании нескольких крючков/слушателей вы должны возвращать либо строки, либо массивы, а не обе. Я реализовал нечто подобное для Hound CMS - https://getbutterfly.com/hound/. – Ciprian 2017-12-19 11:21:08