Pull to refresh

Плагинизация классов

Reading time2 min
Views1.7K
Как-то медитируя на свой новый велосипед подумал, – А почему бы не заложить в него возможность «плагинизации» контроллеров?

Чтобы вот был, например, у нас некий базовый класс Generic.php:
<?php
class Generic
{
  public function Hello() {
    echo 'Hello!';
  }
}
?>


А мы такие взяли и повешали бы на него пару плагинов, которые дополнят/изменят какие-либо его методы не мешая друг-другу (по принципу «кто первый встал – того и тапки»).

Вот они красавцы:
PluginFoo.php:
<?php
class PluginFoo extends Generic_PluginFoo
{
  public function Hello() {
    echo 'Dudes?<br />';
    parent::Hello();
  }
}
?>

и PluginBar.php:
<?php
class PluginBar extends Generic_PluginBar
{
  public function Hello() {
    parent::Hello();
    echo '<br />O.o';
  }
}
?>


А потом мы создали бы объект родительского класса (не обращаясь к последнему потомку), вызвали метод Hello() и тот сказал бы нам:
Dudes?
Hello!
O.o


Ниже лаконичный вариант того как описанного поведения добился ort в своём LiveStreet, а затем уже и я в своём собственном велосипеде.

Вы наверное уже заметили в коде выше, что классы плагинов PluginFoo и PluginBar наследуются от неописанных классов Generic_PluginFoo и Generic_PluginBar соответственно? Так вот их и не будет вообще. Этот финт ушами нам нужен для классалиасинга и выстраивания цепочки наследования.

Да чего резину тянуть.
Вот пример использования:
// создаём объект (Ваш Капитан Очевидность)
$KungFu = new KungFu();
 
// Регистрируем плагин PluginFoo для базового класса Generic
$KungFu->RegisterPlugin('Generic', 'PluginFoo');

 // Регистрируем плагин PluginBar для базового класса Generic
$KungFu->RegisterPlugin('Generic', 'PluginBar');

// Загружаем <i>отплагинизированный</i> объект класса Generic
$Generic = $KungFu->Load('Generic');

$Generic->Hello();

// Получим
// Dudes?
// Hello!
// O.o

Волшебный инструмент:
class KungFu
{
  // Эээ... Конструктор
  public function __construct() {
    spl_autoload_register(array($this, '_AutoLoader'));
  }
  
  // Эээ... Метод автозагрузки
  public function _AutoLoader($sClass)
  {
    if (false == class_exists($sClass)) {
      include($sClass.'.php');
    }
  }
  
  
  // после регистрации плагинов примет вид
  // Array ( [Generic] => Array ( [0] => PluginFoo [1] => PluginBar ) )
  private $_aPlugins = array();
  
  // Метод регистрации плагинов, заполняющий массив, что описан выше
  public function RegisterPlugin($sClass, $sPlugin)
  {
    if (false == isset($this->_aPlugins[$sClass])) {
      $this->_aPlugins[$sClass] = array();
    }
    array_push($this->_aPlugins[$sClass], $sPlugin);
  }
  
  // Метод вернет объект указанного класса. Если для этого класса указаны плагины, то разумеется объект будет по пути плагинизирован.
  public function Load($sClass)
  {
    if (false == isset($this->_aPlugins[$sClass])) {
      return new $sClass();
    }
    else
    {
      $aPlugins = array_reverse($this->_aPlugins[$sClass]);
      
      $sPrev = null;
      
      foreach ($aPlugins as $sPlugin)
      {
        if (null != $sPrev) {
          $aBranch[$sPrev] = $sPlugin;
        }
        $aBranch[$sPlugin] = null;
        $sPrev = $sPlugin;
      }
      
      $aBranch[$sPrev] = $sClass;
      
      foreach (array_reverse($aBranch) as $sPlugin => $sParent) {
        class_alias($sParent, $sClass.'_'.$sPlugin);
      }
      
      if (class_exists($sPlugin)) {
        return new $sPlugin;
      }
    }
  }
}


И вот у меня вопрос, – Как это называется на «языке паттернов»? Как бы вы это назвали?

P.S. Разумеется — этот пост является следствием первоапрельского розыгрыша с ППА :)


– Та-да!
Tags:
Hubs:
+43
Comments28

Articles