Допустим, вам не нужен шаблон 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;
В первой части вы можете включить вызов require_once()
в верхней части вашего PHP-скрипта. Он загружает классы, чтобы сделать что-то подключаемым.
Во второй части мы загружаем класс. Обратите внимание, что мне не нужно было делать ничего особенного для класса, который значительно отличается от шаблона Observer.
В третьей части мы переключаем наш класс на «подключаемый» (то есть поддерживаемые плагины, которые позволяют переопределять методы и свойства класса). Так, например, если у вас есть веб-приложение, у вас может быть реестр плагинов, и вы можете активировать плагины здесь. Обратите внимание также на функцию Dog_bark_beforeEvent()
. Если я установлю $mixed = 'BLOCK_EVENT'
перед оператором возврата, он заблокирует собаку от лая и также заблокирует Dog_bark_afterEvent, потому что не будет никакого события.
В части 4 это нормальный код операции, но обратите внимание, что то, что, как вы думаете, будет работать, вообще не работает. Например, собака не объявляет свое имя как «Фидо», но «Коко». Собака говорит не «мяу», а «Гав». И когда вы хотите посмотреть на имя собаки потом, вы обнаружите, что это «Другой» вместо «Коко». Все эти переопределения были предоставлены в части 3.
Так как это работает? Что ж, давайте исключим eval()
(который все называют "злым") и исключим, что это не шаблон наблюдателя. Таким образом, он работает как скрытый пустой класс с именем Pluggable, который не содержит методов и свойств, используемых классом Dog. Таким образом, поскольку это происходит, магические методы будут задействованы для нас. Вот почему в частях 3 и 4 мы связываемся с объектом, производным от класса Pluggable, а не с самим классом Dog. Вместо этого мы позволяем классу Plugin «касаться» объекта Dog за нас. (Если это какой-то шаблон дизайна, о котором я не знаю - пожалуйста, дайте мне знать.)