PHPでTracのようなプラグインの仕組みを作る
Pythonを知らずにTracを触ってたので気付かなかったけど、Tracのソースに書いてあるInterfaceとかimplementsっていう仕組みはTrac独自なんだね。
だから自由にInterfaceの挙動を操作できる。
Interfaceが組み込みでも、リフレクションを使えば似たものが作れる。
任意のクラスでInterfaceを実装すればプラグインとして扱われるという仕組みを考える。
リフレクションを使ってインターフェースを取得するPluginクラス。
<?php // Plugin.php class Plugin { /** @var array array('InterfaceName' => array(plugins)) */ private $plugins = array(); /** * iniファイルを解析してプラグインを登録する。 * * Pluginで利用するコンポーネントからPluginを呼び出せるように * コンストラクタと登録を分離している。 * * @param string $IniFile */ public function parseIni($iniFile) { $iniFile = dirname(__FILE__) . '/' . $iniFile; $plugins = parse_ini_file($iniFile); foreach ($plugins as $className => $value){ if (!$value) continue; $this->add($className, $value); } } /** * クラス名からプラグインを登録する * * @param string $className * @param int $priority = 100 プラグイン同士の優先順位 */ public function add($className, $priority = 100) { $obj = new $className(); $ref = new ReflectionClass($obj); $interfaces = $ref->getInterfaces(); if (!is_array($interfaces)) continue; foreach ($interfaces as $interface) $this->plugins[$interface->name][$priority] = $obj; // 設定ファイルの優先度でソート foreach ($this->plugins as $k => &$v) ksort($v, SORT_NUMERIC); } /** * インターフェース名を受け取り、それを実装しているオブジェクトの配列を返却する。 * * @param string $interface * @return array */ public function gets($interface) { if (isset($this->plugins[$interface])) return $this->plugins[$interface]; else return array(); } }
例えばメニューをプラグインで表示する場合
メニューのインターフェースを作る。
<?php // Menu.php interface Menu { public function getMenuUrl(); public function getMenuText(); }
インターフェースを実装したクラスをいくつか作る。
<?php // HogeMenu.php class HogeMenu implements Menu { public function getMenuUrl(){ return 'hoge'; } public function getMenuText(){ return 'ほげ'; } }
<?php // FugaMenu.php class FugaMenu implements Menu { public function getMenuUrl(){ return 'fuga'; } public function getMenuText(){ return 'ふが'; } }
プラグインを読み込むための設定ファイルを作る。
; Plugin.ini HogeMenu = 2 FugaMenu = 1
最後に呼び出し元のPHPファイル。
<?php // index.php /** * require_once $className.php * * @param string $name */ function autoload($name) { $file = $name . '.php'; if (file_exists($file)) require_once $file; } spl_autoload_register('autoload'); function getMenus(){ $plugin = new Plugin(); $plugin->parseIni('Plugin.ini'); $menus = $plugin->gets('Menu'); $result = array(); foreach ($menus as $menu) $result[$menu->getMenuUrl()] = $menu->getMenuText(); return $result; } ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>View Menu by Plugins</title> </head> <body> <ul> <?php foreach (getMenus() as $url => $text){ echo <<<END <li><a href="$url">$text</a></li> END; } ?> </ul> </body> </html>