OSDN Git Service

- followed ini file name changes.
[ethna/ethna.git] / class / Plugin.php
1 <?php
2 // vim: foldmethod=marker
3 /**
4  *  Ethna_Plugin.php
5  *
6  *  @author     ICHII Takashi <ichii386@schweetheart.jp>
7  *  @author     Kazuhiro Hosoi <hosoi@gree.co.jp>
8  *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
9  *  @package    Ethna
10  *  @version    $Id$
11  */
12
13 // {{{ Ethna_Plugin
14 /**
15  *  プラグインクラス
16  *  
17  *  @author     ICHII Takashi <ichii386@schweetheart.jp>
18  *  @author     Kazuhiro Hosoi <hosoi@gree.co.jp>
19  *  @access     public
20  *  @package    Ethna
21  */
22 class Ethna_Plugin
23 {
24     /**#@+
25      *  @access private
26      */
27
28     /** @var    object  Ethna_Controller    コントローラオブジェクト */
29     var $controller;
30
31     /** @var    object  Ethna_Controller    コントローラオブジェクト($controllerの省略形) */
32     var $ctl;
33
34     /** @var    object  Ethna_Logger        ログオブジェクト */
35     var $logger;
36
37     /** @var    array   プラグインのオブジェクト(インスタンス)を保存する配列 */
38     var $obj_registry = array();
39
40     /** @var    array   プラグインのクラス名、ソースファイル名を保存する配列 */
41     var $src_registry = array();
42
43     /** @var    array   検索対象ディレクトリを,プラグインの優先順に保存する配列 */
44     var $_dirlist = array();
45
46     /**#@-*/
47
48     // {{{ コンストラクタ
49     /**
50      *  Ethna_Pluginのコンストラクタ
51      *
52      *  @access public
53      *  @param  object  Ethna_Controller    $controller コントローラオブジェクト
54      */
55     function Ethna_Plugin(&$controller)
56     {
57         $this->controller =& $controller;
58         $this->ctl =& $this->controller;
59         $this->logger = null;
60
61         // load dir_registry
62         $this->_loadPluginDirList();
63
64     }
65
66     /**
67      *  loggerをsetする。
68      *
69      *  LogWriterはpluginなので、pluginインスタンス作成時点では
70      *  loggerに依存しないようにする。
71      *
72      *  @access public
73      *  @param  object  Ethna_Logger    $logger ログオブジェクト
74      */
75     function setLogger(&$logger)
76     {
77         if ($this->logger === null && is_object($logger)) {
78             $this->logger =& $logger;
79         }
80     }
81     // }}}
82
83     // {{{ プラグイン呼び出しインタフェース
84     /**
85      *  プラグインのインスタンスを取得
86      *
87      *  @access public
88      *  @param  string  $type   プラグインの種類
89      *  @param  string  $name   プラグインの名前
90      *  @return object  プラグインのインスタンス
91      */
92     function &getPlugin($type, $name)
93     {
94         return $this->_getPlugin($type, $name);
95     }
96
97     /**
98      *  ある種類 ($type) のプラグイン ($name) の全リストを取得
99      *
100      *  @access public
101      *  @param  string  $type   プラグインの種類
102      *  @return array   プラグインオブジェクトの配列
103      */
104     function getPluginList($type)
105     {
106         $plugin_list = array();
107
108         $this->searchAllPluginSrc($type);
109         if (isset($this->src_registry[$type]) == false) {
110             return $plugin_list;
111         }
112         foreach ($this->src_registry[$type] as $name => $value) {
113             if (is_null($value)) {
114                 continue;
115             }
116             $plugin_list[$name] =& $this->getPlugin($type, $name);
117         }
118         return $plugin_list;
119     }
120     // }}}
121
122     // {{{ obj_registry のアクセサ
123     /**
124      *  プラグインのインスタンスをレジストリから取得
125      *
126      *  @access private
127      *  @param  string  $type   プラグインの種類
128      *  @param  string  $name   プラグインの名前
129      *  @return object  プラグインのインスタンス
130      */
131     function &_getPlugin($type, $name)
132     {
133         if (isset($this->obj_registry[$type]) == false) {
134             $this->obj_registry[$type] = array();
135
136             // プラグインの親クラスを(存在すれば)読み込み
137             list($class, $file) = $this->getPluginNaming($type, null);
138             $dir = $this->_searchPluginSrcDir($type, null);
139             if (!Ethna::isError($dir)) {
140                 $this->_includePluginSrc($class, $dir, $file, true);
141             }
142         }
143
144         // key がないときはプラグインをロードする
145         if (array_key_exists($name, $this->obj_registry[$type]) == false) {
146             $this->_loadPlugin($type, $name);
147         }
148
149         // null のときはロードに失敗している
150         if (is_null($this->obj_registry[$type][$name])) {
151             return Ethna::raiseWarning('plugin [type=%s, name=%s] is not found',
152                 E_PLUGIN_NOTFOUND, $type, $name);
153         }
154
155         // プラグインのインスタンスを返す
156         return $this->obj_registry[$type][$name];
157     }
158
159     /**
160      *  get plugin obejct and set to property
161      *
162      *  @access public
163      *  @param  string  $plugin_alias_name  property name to set
164      *  @param  array   $plugin             array(type, name)
165      */
166     function setPlugin($plugin_alias_name, $plugin)
167     {
168         if (isset($this->{$plugin_alias_name})) {
169             return Ethna::raiseWarning('preload plugin alias name is conflicted [alias=%s], It doesn\'t loaded.',
170                 E_PLUGIN_GENERAL, $plugin_alias_name);
171         }
172
173         $this->{$plugin_alias_name} = $this->getPlugin($plugin[0], $plugin[1]);
174     }
175
176     /**
177      *  プラグインをincludeしてnewし,レジストリに登録
178      *
179      *  @access private
180      *  @param  string  $type   プラグインの種類
181      *  @param  string  $name   プラグインの名前
182      */
183     function _loadPlugin($type, $name)
184     {
185         // プラグインのファイル名を取得
186         $plugin_src_registry = $this->_getPluginSrc($type, $name);
187         if (is_null($plugin_src_registry)) {
188             $this->obj_registry[$type][$name] = null;
189             return;
190         }
191         list($plugin_class, $plugin_dir, $plugin_file) = $plugin_src_registry;
192
193         // プラグインのファイルを読み込み
194         $r =& $this->_includePluginSrc($plugin_class, $plugin_dir, $plugin_file);
195         if (Ethna::isError($r)) {
196             $this->obj_registry[$type][$name] = null;
197             return;
198         }
199
200         // プラグイン作成
201         $instance =& new $plugin_class($this->controller, $type, $name);
202         if (is_object($instance) == false
203             || strcasecmp(get_class($instance), $plugin_class) != 0) {
204
205             if ($this->logger !== null) {
206                 $this->logger->log(LOG_WARNING, 'plugin [%s::%s] instantiation failed', $type, $name);
207             }
208
209             $this->obj_registry[$type][$name] = null;
210             return;
211         }
212         $this->obj_registry[$type][$name] =& $instance;
213     }
214
215     /**
216      *  プラグインのインスタンスをレジストリから消す
217      *
218      *  @access private
219      *  @param  string  $type   プラグインの種類
220      *  @param  string  $name   プラグインの名前
221      */
222     function _unloadPlugin($type, $name)
223     {
224         unset($this->obj_registry[$type][$name]);
225     }
226     // }}}
227
228     /**
229      *  プラグインのインスタンスをレジストリから消す
230      *
231      *  @access private
232      *  @param  string  $type   プラグインの種類
233      *  @param  string  $name   プラグインの名前
234      */
235     function _loadPluginDirList()
236     {
237         $this->_dirlist[] = $this->controller->getDirectory('plugin');
238
239         // include_path から検索
240         $include_path_list = explode(PATH_SEPARATOR, get_include_path());
241
242         // Communiy based libraries
243         $extlib_dir = implode(DIRECTORY_SEPARATOR, array('Ethna', 'extlib', 'Plugin'));
244         // Ethna bandle
245         $class_dir = implode(DIRECTORY_SEPARATOR, array('Ethna', 'class', 'Plugin'));
246         foreach ($include_path_list as $include_path) {
247             if (is_dir($include_path . DIRECTORY_SEPARATOR . $extlib_dir)) {
248                 $this->_dirlist[] = $include_path . DIRECTORY_SEPARATOR . $extlib_dir;
249             }
250             if (is_dir($include_path . DIRECTORY_SEPARATOR . $class_dir)) {
251                 $this->_dirlist[] = $include_path . DIRECTORY_SEPARATOR . $class_dir;
252             }
253         }
254     }
255
256     // {{{ src_registry のアクセサ
257     /**
258      *  プラグインのソースファイル名とクラス名をレジストリから取得
259      *
260      *  @access private
261      *  @param  string  $type   プラグインの種類
262      *  @param  string  $name   プラグインの名前
263      *  @return array   ソースファイル名とクラス名からなる配列
264      */
265     function _getPluginSrc($type, $name)
266     {
267         if (isset($this->src_registry[$type]) == false) {
268             $this->src_registry[$type] = array();
269         }
270
271         // key がないときはプラグインの検索をする
272         if (array_key_exists($name, $this->src_registry[$type]) == false) {
273             $this->_searchPluginSrc($type, $name);
274         }
275
276         // プラグインのソースを返す
277         return $this->src_registry[$type][$name];
278     }
279     // }}}
280
281     // {{{ プラグインファイル検索部分
282     /**
283      *  プラグインのクラス名、ディレクトリ、ファイル名を決定
284      *
285      *  @access public
286      *  @param  string  $type   プラグインの種類
287      *  @param  string  $name   プラグインの名前 (nullのときは親クラス)
288      *  @param  string  $appid  アプリケーションID (廃止予定)
289      *  @return array   プラグインのクラス名、ファイル名の配列
290      */
291     function getPluginNaming($type, $name = null, $appid = 'Ethna')
292     {
293         $ext = $this->ctl->getExt('php');
294
295         $plugin_class_name = array(
296             $appid,
297             'Plugin',
298             $type,
299         );
300
301         if ($name !== null) {
302             $plugin_class_name[] = $name;
303         }
304         else {
305             $name = $type;
306         }
307
308         $class = implode('_', $plugin_class_name);
309         $file  = "{$name}.{$ext}";
310
311         return array($class, $file);
312     }
313
314     /**
315      *  プラグインのソースを include する
316      *
317      *  @access private
318      *  @param  string  $class  クラス名
319      *  @param  string  $dir    ディレクトリ名
320      *  @param  string  $file   ファイル名
321      *  @param  bool    $parent 親クラスかどうかのフラグ
322      *  @return true|Ethna_Error
323      */
324     function &_includePluginSrc($class, $dir, $file, $parent = false)
325     {
326         $true = true;
327         if (class_exists($class)) {
328             return $true;
329         }
330
331         $file = $dir . '/' . $file;
332         if (file_exists_ex($file) === false) {
333             if ($parent === false) {
334                 return Ethna::raiseWarning('plugin file is not found: [%s]',
335                                            E_PLUGIN_NOTFOUND, $file);
336             } else {
337                 return $true;
338             }
339         }
340
341         include_once $file;
342
343         if (class_exists($class) === false) {
344             if ($parent === false) {
345                 return Ethna::raiseWarning('plugin class [%s] is not defined',
346                     E_PLUGIN_NOTFOUND, $class);
347             } else {
348                 return $true;
349             }
350         }
351
352         if ($parent === false) {
353             if ($this->logger !== null) {
354                 $this->logger->log(LOG_DEBUG, 'plugin class [%s] is defined', $class);
355             }
356         }
357         return $true;
358     }
359
360     /**
361      *  プラグインのソースディレクトリを決定する
362      *
363      *  @param  string  $type   プラグインの種類
364      *  @param  string  $name   プラグインの名前 (nullのときは親クラス)
365      *  @retur  string  directory
366      */
367     function _searchPluginSrcDir($type, $name = null)
368     {
369         list(, $file) = $this->getPluginNaming($type, $name);
370
371         $dir_prefix = "";
372         if ($name !== null) {
373             $dir_prefix = DIRECTORY_SEPARATOR . $type;
374         }
375
376         // dirlist にしたがって検索
377         foreach ($this->_dirlist as $dir) {
378             $dir .= $dir_prefix;
379
380             if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) {
381                 return $dir;
382             }
383         }
384
385         return Ethna::raiseWarning('plugin file is not found in search directories: [%s]',
386                                    E_PLUGIN_NOTFOUND, $file);
387     }
388
389     /**
390      *  アプリケーション, extlib, Ethna の順でプラグインのソースを検索する
391      *
392      *  @access private
393      *  @param  string  $type   プラグインの種類
394      *  @param  string  $name   プラグインの名前
395      *  @return array   class, dir, file
396      */
397     function _searchPluginSrc($type, $name)
398     {
399         list($class, $file) = $this->getPluginNaming($type, $name);
400
401         // 古いバージョンのプラグインの命名規則にしたがったファイルは無視
402         if (strpos($name, "_") !== false) {
403             return;
404         }
405
406         if (class_exists($class)) {
407             // すでにクラスが存在する場合は特別にスキップ
408             if (isset($this->src_registry[$type]) == false) {
409                 $this->src_registry[$type] = array();
410             }
411         }
412
413         $dir = $this->_searchPluginSrcDir($type, $name);
414
415         if (Ethna::isError($dir)) {
416             $this->src_registry[$type][$name] = null;
417             return ;
418         }
419
420         if (file_exists("{$dir}/{$file}")) {
421             $this->logger->log(LOG_DEBUG, 'plugin file is found in search: [%s/%s]',
422                                $dir, $file);
423             if (isset($this->src_registry[$type]) == false) {
424                 $this->src_registry[$type] = array();
425             }
426             $this->src_registry[$type][$name] = array($class, $dir, $file);
427             return;
428         }
429
430         // 見つからなかった場合 (nullで記憶しておく)
431         $this->logger->log(LOG_WARNING, 'plugin file for [type=%s, name=%s] is not found in search', $type, $name);
432         $this->src_registry[$type][$name] = null;
433     }
434
435     /**
436      *  プラグインの種類 ($type) をすべて検索する
437      *
438      *  @access public
439      *  @return array
440      */
441     function searchAllPluginType()
442     {
443         $type_list = array();
444         foreach($this->_dirlist as $dir) {
445             $type_dir= glob($dir . DIRECTORY_SEPARATOR . "*", GLOB_ONLYDIR);
446             if (!$type_dir) {
447                 continue;
448             }
449             foreach ($type_dir as $dir) {
450                 if ($type_dir{0} != '.') {
451                     $type_list[basename($dir)] = 0;
452                 }
453             }
454         }
455         return array_keys($type_list);
456     }
457
458     /**
459      *  指定された $type のプラグイン (のソース) をすべて検索する
460      *
461      *  @access public
462      *  @param  string  $type   プラグインの種類
463      */
464     function searchAllPluginSrc($type)
465     {
466         // 後で見付かったもので上書きするので $this->appid_list の逆順とする
467         $name_list = array();
468         $ext = $this->ctl->getExt('php');
469
470         foreach($this->_dirlist as $dir) {
471             $files = glob($dir . DIRECTORY_SEPARATOR . $type . DIRECTORY_SEPARATOR . "/*." . $ext);
472             if (!$files) {
473                 $this->logger->log(LOG_DEBUG, 'cannot open plugin directory: [%s/%s]', $dir, $type);
474                 continue;
475             }
476             $this->logger->log(LOG_DEBUG, 'plugin directory opened: [%s]', $dir);
477             foreach ($files as $plugin_file) {
478                 $plugin_name = substr(basename($plugin_file), 0, - strlen($ext) - 1);
479                 $name_list[$plugin_name] = 0;
480             }
481         }
482
483         foreach (array_keys($name_list) as $name) {
484             // 冗長だがもう一度探しなおす
485             $this->_searchPluginSrc($type, $name);
486         }
487     }
488     // }}}
489
490     // {{{ static な include メソッド
491     /**
492      *  Ethna 本体付属のプラグインのソースを include する
493      *  (B.C.) Ethna 2.5.0 perview 5 以降,このメソッドには意味がありません.Ethna_Plugin::import を使ってください
494      *
495      *  @access public
496      *  @param  string  $type   プラグインの種類
497      *  @param  string  $name   プラグインの名前
498      *  @static
499      */
500     function includeEthnaPlugin($type, $name)
501     {
502         Ethna_Plugin::import($type, $name);
503     }
504
505     /**
506      *  プラグインのソースを include する
507      *
508      *  @access public
509      *  @param  string  $type   プラグインの種類
510      *  @param  string  $name   プラグインの名前
511      */
512     function includePlugin($type, $name = null)
513     {
514         if ($name !== null) {
515             list($class, $file) = $this->getPluginNaming($type);
516             $dir = $this->_searchPluginSrcDir($type);
517             $this->_includePluginSrc($class, $dir, $file);
518         }
519
520         list($class, $file) = $this->getPluginNaming($type, $name);
521         $dir = $this->_searchPluginSrcDir($type, $name);
522         $this->_includePluginSrc($class, $dir, $file);
523     }
524     // }}}
525
526     /**
527      *  プラグインのソースを include する
528      *
529      *  @access public
530      *  @param  string  $type   プラグインの種類
531      *  @param  string  $name   プラグインの名前
532      *  @static
533      */
534     // static function import($type, $name = null)
535     function import($type, $name = null)
536     {
537         $ctl =& Ethna_Controller::getInstance();
538         $plugin =& $ctl->getPlugin();
539
540         $plugin->includePlugin($type, $name);
541     }
542 }
543 // }}}