OSDN Git Service

- fixed bug Ethna_Session#isAnonymous returns invalid result (thanks:longkey1)
[ethna/ethna.git] / class / Ethna_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      *  loggerをsetする。
67      *
68      *  LogWriterはpluginなので、pluginインスタンス作成時点では
69      *  loggerに依存しないようにする。
70      *
71      *  @access public
72      *  @param  object  Ethna_Logger    $logger ログオブジェクト
73      */
74     function setLogger(&$logger)
75     {
76         if ($this->logger === null && is_object($logger)) {
77             $this->logger =& $logger;
78         }
79     }
80     // }}}
81
82     // {{{ プラグイン呼び出しインタフェース
83     /**
84      *  プラグインのインスタンスを取得
85      *
86      *  @access public
87      *  @param  string  $type   プラグインの種類
88      *  @param  string  $name   プラグインの名前
89      *  @return object  プラグインのインスタンス
90      */
91     function &getPlugin($type, $name)
92     {
93         return $this->_getPlugin($type, $name);
94     }
95
96     /**
97      *  ある種類 ($type) のプラグイン ($name) の全リストを取得
98      *
99      *  @access public
100      *  @param  string  $type   プラグインの種類
101      *  @return array   プラグインオブジェクトの配列
102      */
103     function getPluginList($type)
104     {
105         $plugin_list = array();
106
107         $this->searchAllPluginSrc($type);
108         if (isset($this->src_registry[$type]) == false) {
109             return $plugin_list;
110         }
111         foreach ($this->src_registry[$type] as $name => $value) {
112             if (is_null($value)) {
113                 continue;
114             }
115             $plugin_list[$name] =& $this->getPlugin($type, $name);
116         }
117         return $plugin_list;
118     }
119     // }}}
120
121     // {{{ obj_registry のアクセサ
122     /**
123      *  プラグインのインスタンスをレジストリから取得
124      *
125      *  @access private
126      *  @param  string  $type   プラグインの種類
127      *  @param  string  $name   プラグインの名前
128      *  @return object  プラグインのインスタンス
129      */
130     function &_getPlugin($type, $name)
131     {
132         if (isset($this->obj_registry[$type]) == false) {
133             $this->obj_registry[$type] = array();
134
135             // プラグインの親クラスを(存在すれば)読み込み
136             list($class, $file) = $this->getPluginNaming($type, null);
137             $dir = $this->_searchPluginSrcDir($type, null);
138             if (!Ethna::isError($dir)) {
139                 $this->_includePluginSrc($class, $dir, $file, true);
140             }
141         }
142
143         // key がないときはプラグインをロードする
144         if (array_key_exists($name, $this->obj_registry[$type]) == false) {
145             $this->_loadPlugin($type, $name);
146         }
147
148         // null のときはロードに失敗している
149         if (is_null($this->obj_registry[$type][$name])) {
150             return Ethna::raiseWarning('plugin [type=%s, name=%s] is not found',
151                 E_PLUGIN_NOTFOUND, $type, $name);
152         }
153
154         // プラグインのインスタンスを返す
155         return $this->obj_registry[$type][$name];
156     }
157
158     /**
159      *  プラグインをincludeしてnewし,レジストリに登録
160      *
161      *  @access private
162      *  @param  string  $type   プラグインの種類
163      *  @param  string  $name   プラグインの名前
164      */
165     function _loadPlugin($type, $name)
166     {
167         // プラグインのファイル名を取得
168         $plugin_src_registry = $this->_getPluginSrc($type, $name);
169         if (is_null($plugin_src_registry)) {
170             $this->obj_registry[$type][$name] = null;
171             return;
172         }
173         list($plugin_class, $plugin_dir, $plugin_file) = $plugin_src_registry;
174
175         // プラグインのファイルを読み込み
176         $r =& $this->_includePluginSrc($plugin_class, $plugin_dir, $plugin_file);
177         if (Ethna::isError($r)) {
178             $this->obj_registry[$type][$name] = null;
179             return;
180         }
181
182         // プラグイン作成
183         $instance =& new $plugin_class($this->controller, $type, $name);
184         if (is_object($instance) == false
185             || strcasecmp(get_class($instance), $plugin_class) != 0) {
186             $this->logger->log(LOG_WARNING, 'plugin [%s::%s] instantiation failed', $type, $name);
187             $this->obj_registry[$type][$name] = null;
188             return;
189         }
190         $this->obj_registry[$type][$name] =& $instance;
191     }
192
193     /**
194      *  プラグインのインスタンスをレジストリから消す
195      *
196      *  @access private
197      *  @param  string  $type   プラグインの種類
198      *  @param  string  $name   プラグインの名前
199      */
200     function _unloadPlugin($type, $name)
201     {
202         unset($this->obj_registry[$type][$name]);
203     }
204     // }}}
205
206     /**
207      *  プラグインのインスタンスをレジストリから消す
208      *
209      *  @access private
210      *  @param  string  $type   プラグインの種類
211      *  @param  string  $name   プラグインの名前
212      */
213     function _loadPluginDirList()
214     {
215         $this->_dirlist[] = $this->controller->getDirectory('plugin');
216
217         // include_path から検索
218         $include_path_list = explode(PATH_SEPARATOR, get_include_path());
219
220         // Communiy based libraries
221         $extlib_dir = implode(DIRECTORY_SEPARATOR, array('Ethna', 'extlib', 'Plugin'));
222         // Ethna bandle
223         $class_dir = implode(DIRECTORY_SEPARATOR, array('Ethna', 'class', 'Plugin'));
224         foreach ($include_path_list as $include_path) {
225             if (is_dir($include_path . DIRECTORY_SEPARATOR . $extlib_dir)) {
226                 $this->_dirlist[] = $include_path . DIRECTORY_SEPARATOR . $extlib_dir;
227             }
228             if (is_dir($include_path . DIRECTORY_SEPARATOR . $class_dir)) {
229                 $this->_dirlist[] = $include_path . DIRECTORY_SEPARATOR . $class_dir;
230             }
231         }
232     }
233
234     // {{{ src_registry のアクセサ
235     /**
236      *  プラグインのソースファイル名とクラス名をレジストリから取得
237      *
238      *  @access private
239      *  @param  string  $type   プラグインの種類
240      *  @param  string  $name   プラグインの名前
241      *  @return array   ソースファイル名とクラス名からなる配列
242      */
243     function _getPluginSrc($type, $name)
244     {
245         if (isset($this->src_registry[$type]) == false) {
246             $this->src_registry[$type] = array();
247         }
248
249         // key がないときはプラグインの検索をする
250         if (array_key_exists($name, $this->src_registry[$type]) == false) {
251             $this->_searchPluginSrc($type, $name);
252         }
253
254         // プラグインのソースを返す
255         return $this->src_registry[$type][$name];
256     }
257     // }}}
258
259     // {{{ プラグインファイル検索部分
260     /**
261      *  プラグインのクラス名、ディレクトリ、ファイル名を決定
262      *
263      *  @access public
264      *  @param  string  $type   プラグインの種類
265      *  @param  string  $name   プラグインの名前 (nullのときは親クラス)
266      *  @param  string  $appid  アプリケーションID (廃止予定)
267      *  @return array   プラグインのクラス名、ファイル名の配列
268      */
269     function getPluginNaming($type, $name = null, $appid = 'Ethna')
270     {
271         $ext = $this->ctl->getExt('php');
272
273         $plugin_class_name = array(
274             $appid,
275             'Plugin',
276             $type,
277         );
278
279         if ($name !== null) {
280             $plugin_class_name[] = $name;
281         }
282         else {
283             $name = $type;
284         }
285
286         $class = implode('_', $plugin_class_name);
287         $file  = "{$name}.{$ext}";
288
289         return array($class, $file);
290     }
291
292     /**
293      *  プラグインのソースを include する
294      *
295      *  @access private
296      *  @param  string  $class  クラス名
297      *  @param  string  $dir    ディレクトリ名
298      *  @param  string  $file   ファイル名
299      *  @param  bool    $parent 親クラスかどうかのフラグ
300      *  @return true|Ethna_Error
301      */
302     function &_includePluginSrc($class, $dir, $file, $parent = false)
303     {
304         $true = true;
305         if (class_exists($class)) {
306             return $true;
307         }
308
309         $file = $dir . '/' . $file;
310         if (file_exists_ex($file) === false) {
311             if ($parent === false) {
312                 return Ethna::raiseWarning('plugin file is not found: [%s]',
313                                            E_PLUGIN_NOTFOUND, $file);
314             } else {
315                 return $true;
316             }
317         }
318
319         include_once $file;
320
321         if (class_exists($class) === false) {
322             if ($parent === false) {
323                 return Ethna::raiseWarning('plugin class [%s] is not defined',
324                     E_PLUGIN_NOTFOUND, $class);
325             } else {
326                 return $true;
327             }
328         }
329
330         if ($parent === false) {
331             $this->logger->log(LOG_DEBUG, 'plugin class [%s] is defined', $class);
332         }
333         return $true;
334     }
335
336     /**
337      *  プラグインのソースディレクトリを決定する
338      *
339      *  @param  string  $type   プラグインの種類
340      *  @param  string  $name   プラグインの名前 (nullのときは親クラス)
341      *  @retur  string  directory
342      */
343     function _searchPluginSrcDir($type, $name)
344     {
345         list(, $file) = $this->getPluginNaming($type, $name);
346
347         $dir_prefix = "";
348         if ($name !== null) {
349             $dir_prefix = DIRECTORY_SEPARATOR . $type;
350         }
351
352         // dirlist にしたがって検索
353         foreach ($this->_dirlist as $dir) {
354             $dir .= $dir_prefix;
355
356             if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) {
357                 return $dir;
358             }
359         }
360
361         return Ethna::raiseWarning('plugin file is not found in search directories: [%s]',
362                                    E_PLUGIN_NOTFOUND, $file);
363     }
364
365     /**
366      *  アプリケーション, extlib, Ethna の順でプラグインのソースを検索する
367      *
368      *  @access private
369      *  @param  string  $type   プラグインの種類
370      *  @param  string  $name   プラグインの名前
371      *  @return array   class, dir, file
372      */
373     function _searchPluginSrc($type, $name)
374     {
375         list($class, $file) = $this->getPluginNaming($type, $name);
376         if (class_exists($class)) {
377             // すでにクラスが存在する場合は特別にスキップ
378             if (isset($this->src_registry[$type]) == false) {
379                 $this->src_registry[$type] = array();
380             }
381         }
382
383         $dir = $this->_searchPluginSrcDir($type, $name);
384
385         if (Ethna::isError($dir)) {
386             $this->src_registry[$type][$name] = null;
387             return ;
388         }
389
390         if (file_exists("{$dir}/{$file}")) {
391             $this->logger->log(LOG_DEBUG, 'plugin file is found in search: [%s/%s]',
392                                $dir, $file);
393             if (isset($this->src_registry[$type]) == false) {
394                 $this->src_registry[$type] = array();
395             }
396             $this->src_registry[$type][$name] = array($class, $dir, $file);
397             return;
398         }
399
400         // 見つからなかった場合 (nullで記憶しておく)
401         $this->logger->log(LOG_WARNING, 'plugin file for [type=%s, name=%s] is not found in search', $type, $name);
402         $this->src_registry[$type][$name] = null;
403     }
404
405     /**
406      *  プラグインの種類 ($type) をすべて検索する
407      *
408      *  @access public
409      *  @return array
410      */
411     function searchAllPluginType()
412     {
413         $type_list = array();
414         foreach($this->_dirlist as $dir) {
415             $type_dir= glob($dir . DIRECTORY_SEPARATOR . "*", GLOB_ONLYDIR);
416             if (!$type_dir) {
417                 continue;
418             }
419             foreach ($type_dir as $dir) {
420                 if ($type_dir{0} != '.') {
421                     $type_list[basename($dir)] = 0;
422                 }
423             }
424         }
425         return array_keys($type_list);
426     }
427
428     /**
429      *  指定された $type のプラグイン (のソース) をすべて検索する
430      *
431      *  @access public
432      *  @param  string  $type   プラグインの種類
433      */
434     function searchAllPluginSrc($type)
435     {
436         // 後で見付かったもので上書きするので $this->appid_list の逆順とする
437         $name_list = array();
438         $ext = $this->ctl->getExt('php');
439
440         foreach($this->_dirlist as $dir) {
441             $files = glob($dir . DIRECTORY_SEPARATOR . $type . DIRECTORY_SEPARATOR . "/*." . $ext);
442             if (!$files) {
443                 $this->logger->log(LOG_DEBUG, 'cannot open plugin directory: [%s/%s]', $dir, $type);
444                 continue;
445             }
446             $this->logger->log(LOG_DEBUG, 'plugin directory opened: [%s]', $dir);
447             foreach ($files as $plugin_file) {
448                 $plugin_name = substr(basename($plugin_file), 0, - strlen($ext) - 1);
449                 $name_list[$plugin_name] = 0;
450             }
451         }
452
453         foreach (array_keys($name_list) as $name) {
454             // 冗長だがもう一度探しなおす
455             $this->_searchPluginSrc($type, $name);
456         }
457     }
458     // }}}
459
460     // {{{ static な include メソッド
461     /**
462      *  Ethna 本体付属のプラグインのソースを include する
463      *
464      *  @access public
465      *  @param  string  $type   プラグインの種類
466      *  @param  string  $name   プラグインの名前
467      *  @static
468      */
469     function includeEthnaPlugin($type, $name)
470     {
471         Ethna_Plugin::includePlugin($type, $name, 'Ethna');
472     }
473
474     /**
475      *  プラグインのソースを include する
476      *
477      *  @access public
478      *  @param  string  $type   プラグインの種類
479      *  @param  string  $name   プラグインの名前
480      *  @param  string  $appid  アプリケーションID
481      *  @static
482      */
483     function includePlugin($type, $name, $appid = null)
484     {
485         $ctl =& Ethna_Controller::getInstance();
486         $plugin =& $ctl->getPlugin();
487
488         if ($appid === null) {
489             $appid = $ctl->getAppId();
490         }
491
492         list($class, $file) = $plugin->getPluginNaming($type, $name);
493         $dir = $this->_searchPluginSrcDir($type, $name);
494         $plugin->_includePluginSrc($class, $dir, $file);
495     }
496     // }}}
497 }
498 // }}}
499 ?>