Написал вот такой небольшой пример быдлокода. Предполагается, что необходимо 1) реализовать паттерн как таковой 2) реализовать динамическое изменение цепочки, как изнутри, так и снаружи, если процесс выполнения не может возвращать эту цепь по каким-либо причинам (проблемы в архитектуре, как частный случай).
Создадим абстрактный класс некого контроллера:
abstract class Controller {
protected $_data = array();
protected $_chain = null;
public function __construct(array $data = array()) {
$this->_data = $data;
}
public function setChain(Handler $chain) {
$this->_chain = $chain;
}
public function getChain() {
return $this->_chain;
}
abstract public function runStrategy();
}
Теперь добавим набор унаследованных от него классов, реализующих разную интересную логику:
class D extends Controller {
public function runStrategy() {
echo 'I am just D!<br />';
printf('My data is: %s', print_r($this->_data, true));
}
}
class CA extends Controller {
public function runStrategy() {
echo '<table border="1">';
foreach ($this->_data as $item) {
echo "<tr><td>{$item}</td></tr>";
}
echo '</table>';
}
}
class CB extends Controller {
public function runStrategy() {
foreach ($this->_data as $item) {
echo "{$item}<br />";
}
}
}
class CC extends Controller {
public function runStrategy() {
foreach ($this->_data as $item) {
echo "----{$item}<br />";
}
}
}
Напишем непосредственно реализацию цепочки:
class Handler
{
protected $_next = null;
protected $_controller;
public function getLimit()
{
return $this->limit;
}
public function __construct(Handler $handler = null)
{
$this->setNext($handler);
}
public function setNext(Handler $handler = null) {
$this->_next = $handler;
}
public function getNext() {
return $this->_next;
}
public function getController() {
return $this->_controller;
}
public function setController(Controller $controller) {
$this->_controller = $controller;
}
public function handleRequest($state = 0)
{
if ($state === 0)
{echo 'START FK<BR />';}
$this->_controller->runStrategy();
$state++;
if ($this->_next) {
$this->_next->handleRequest($state);
}
if ($state === 1) {echo 'STOP FK<BR />';}
}
}
В коде метода handleRequest показано, как можно реализовать выполнение каких-либо действие (у меня схематичное отключение и включение внешних ключей) до и после вызова всех методов.
Теперь создадим класс-контроллер, который внутри себя изменит цепочку:
class C extends Controller {
public function runStrategy() {
$cs = array();
for ($i = 0; $i < 2; $i++) {
$c1 = new CA(array('word1'.$i, 'word2'.$i, 'word3'.$i, 'word4'.$i));
$c2 = new CB(array('Russia'.$i, 'USA'.$i, 'China'.$i, 'France'.$i));
$c3 = new CC(array('hi'.$i, 'bye'.$i, '9000'.$i, '!-^-!'.$i));
$cs['s'][] = $c1;
$cs['m'][] = $c2;
$cs['f'][] = $c3;
}
foreach ($cs as $k => $vs) {
foreach ($vs as $v) {
$cs['r'][] = $v;
}
}
$cs = $cs['r'];
$start = $this->getChain()->getNext();;
foreach ($cs as $c) {
$start = new Handler($start);
$start->setController($c);
}
$this->getChain()->setNext($start);
print_r($this->getChain());
}
}
Конечно, print_r нужно убрать. Я оставил его, чтобы вы могли увидеть, какая цепочка получится.
Осталось добавить использование цепочки:
$cMain = new C();
$cAdditional = new D();
$cAdditional2 = new D(array('Second D'));
$start = new Handler();
$start->setController($cAdditional2);
$start = new Handler($start);
$start->setController($cAdditional);
$start = new Handler($start);
$start->setController($cMain);
$start->getController()->setChain($start);
echo '<br />';
$start->handleRequest();
Полный код (по сути все то же, что тут) можно взять здесь: gist.github.com/1688458