2 // vim: foldmethod=marker
4 * Ethna_InfoManager.php
6 * @author Masaki Fujimoto <fujimoto@php.net>
7 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
12 // {{{ Ethna_InfoManager
16 * @author Masaki Fujimoto <fujimoto@php.net>
20 class Ethna_InfoManager extends Ethna_AppManager
26 /** @var object Ethna_Controller コントローラオブジェクト */
29 /** @var object Ethna_ClassFactory クラスファクトリオブジェクト */
32 /** @var array アクションスクリプト解析結果キャッシュファイル */
33 var $cache_class_list_file;
35 /** @var array アクションスクリプト解析結果キャッシュ */
36 var $cache_class_list;
38 /** @var array [属性]DBタイプ一覧 */
39 var $db_type_list = array(
40 DB_TYPE_RW => array('name' => 'DB_TYPE_RW'),
41 DB_TYPE_RO => array('name' => 'DB_TYPE_RO'),
42 DB_TYPE_MISC => array('name' => 'DB_TYPE_MISC'),
45 /** @var array [属性]フォーム型一覧 */
48 /** @var array [属性]変数型一覧 */
54 * Ethna_InfoManagerのコンストラクタ
57 * @param object Ethna_Backend &$backend Ethna_Backendオブジェクト
59 function Ethna_InfoManager(&$backend)
61 $this->form_type_list = array(
62 FORM_TYPE_TEXT => array('name' => _et('TextBox')),
63 FORM_TYPE_PASSWORD => array('name' => _et('Password')),
64 FORM_TYPE_TEXTAREA => array('name' => _et('TextArea')),
65 FORM_TYPE_SELECT => array('name' => _et('SelectBox')),
66 FORM_TYPE_RADIO => array('name' => _et('RadioButton')),
67 FORM_TYPE_CHECKBOX => array('name' => _et('CheckBox')),
68 FORM_TYPE_SUBMIT => array('name' => _et('SubmitButton')),
69 FORM_TYPE_FILE => array('name' => _et('File')),
72 $this->var_type_list = array(
73 VAR_TYPE_INT => array('name' => _et('Integer')),
74 VAR_TYPE_FLOAT => array('name' => _et('Float')),
75 VAR_TYPE_STRING => array('name' => _et('String')),
76 VAR_TYPE_DATETIME => array('name' => _et('Datetime')),
77 VAR_TYPE_BOOLEAN => array('name' => _et('Boolean')),
78 VAR_TYPE_FILE => array('name' => _et('File')),
81 parent::Ethna_AppManager($backend);
82 $this->ctl =& Ethna_Controller::getInstance();
83 $this->class_factory =& $this->ctl->getClassFactory();
85 // アクションスクリプト解析結果キャッシュ取得
86 $this->cache_class_list_file = sprintf('%s/ethna_info_class_list',
87 $this->ctl->getDirectory('tmp')
89 if (file_exists($this->cache_class_list_file)
90 && filesize($this->cache_class_list_file) > 0) {
91 $fp = fopen($this->cache_class_list_file, 'r');
92 $s = fread($fp, filesize($this->cache_class_list_file));
94 $this->cache_class_list = unserialize($s);
102 * @return array アクション一覧
104 function getActionList()
109 $class_list = $this->_analyzeActionList();
112 list($manifest_action_list, $manifest_class_list) = $this->_getActionList_Manifest($class_list);
115 $implicit_action_list = $this->_getActionList_Implicit($class_list, $manifest_action_list, $manifest_class_list);
117 $r = array_merge($manifest_action_list, $implicit_action_list);
121 $r = $this->_addActionList($r);
130 * @return array 遷移先一覧
132 function getForwardList()
136 // テンプレート/ビュースクリプトを解析する
137 $forward_list = $this->_analyzeForwardList();
140 $manifest_forward_list = $this->_getForwardList_Manifest();
143 $implicit_forward_list = $this->_getForwardList_Implicit($forward_list, $manifest_forward_list);
145 $r = $manifest_forward_list + $implicit_forward_list;
152 * ディレクトリ以下のアクションスクリプトを解析する
155 * @param string $action_dir 解析対象のディレクトリ
156 * @return array アクションクラス定義一覧
158 function _analyzeActionList($action_dir = null)
161 $cache_update = false;
163 if (is_null($action_dir)) {
164 $cache_update = true;
165 $action_dir = $this->ctl->getActiondir();
167 $prefix_len = strlen($this->ctl->getActiondir());
169 $child_dir_list = array();
171 $dh = opendir($action_dir);
176 $ext = $this->ctl->getExt('php');
177 while (($file = readdir($dh)) !== false) {
178 if ($file == "." || $file == "..") {
181 $file = $action_dir . $file;
184 $child_dir_list[] = $file;
188 if (preg_match("/\.$ext\$/", $file) == 0) {
192 $key = substr($file, $prefix_len);
196 if ($this->cache_class_list[$key]['.mtime'] >= filemtime($file)) {
197 $class_list = $this->cache_class_list[$key];
199 $class_list = $this->_analyzeActionScript($file);
201 if (is_null($class_list) == false) {
202 $r[$key] = $class_list;
208 foreach ($child_dir_list as $child_dir) {
209 $tmp = $this->_analyzeActionList($child_dir . "/");
210 $r = array_merge($r, $tmp);
215 $fp = fopen($this->cache_class_list_file, 'w');
216 fwrite($fp, serialize($r));
227 * @param string $script ファイル名
228 * @return array アクションクラス定義一覧
230 function _analyzeActionScript($script)
232 $class_list = array();
233 $class_list['.mtime'] = filemtime($script);
236 $fp = fopen($script, 'r');
240 while (feof($fp) == false) {
241 $source .= fgets($fp, 8192);
245 // トークンに分割してクラス定義情報を取得
246 $token_list = token_get_all($source);
251 for ($i = 0; $i < count($token_list); $i++) {
252 $token = $token_list[$i];
254 if (is_string($token)) {
257 } else if ($token == '}') {
259 if ($state == 'T_PREPARE' || $state == 'T_PERFORM') {
260 if ($nest == $method_nest) {
261 $state = 'T_ACTION_CLASS';
263 } else if ($nest == 0) {
270 if ($token[0] == T_CLASS) {
273 $class_name = $token_list[$i][1]; // should be T_STRING
274 if ($this->_isSubclassOf($class_name, 'Ethna_ActionClass')) {
275 $state = 'T_ACTION_CLASS';
276 $current = $class_name;
277 $class_list[$current] = array('type' => 'action_class');
278 } else if ($this->_isSubclassOf($class_name, 'Ethna_ActionForm')) {
279 $state = 'T_ACTION_FORM';
280 $current = $class_name;
281 $class_list[$current] = array('type' => 'action_form');
283 $nest = 0; // for safe
284 } else if ($token[0] == T_COMMENT && strncmp($token[1], "/**", 3) == 0 && is_array($token_list[$i+2]) && $token_list[$i+2][0] == T_CLASS) {
285 // DocComment for class
286 } else if ($state == 'T_ACTION_CLASS' && $token[0] == T_FUNCTION) {
288 $method_name = $token_list[$i][1];
289 if (strcasecmp($method_name, 'prepare') == 0) {
290 $state = 'T_PREPARE';
291 $method_nest = $nest;
292 } else if (strcasecmp($method_name, 'perform') == 0) {
293 $state = 'T_PERFORM';
294 $method_nest = $nest;
296 } else if (($state == 'T_PREPARE' || $state == 'T_PERFORM') && $token[0] == T_RETURN) {
299 while ($token_list[$i+$n] !== ";") {
300 $s .= is_string($token_list[$i+$n]) ? $token_list[$i+$n] : $token_list[$i+$n][1];
303 $key = $state == 'T_PREPARE' ? 'prepare' : 'perform';
304 $class_list[$current]['return'][$key][] = $s;
308 if (count($class_list) == 0) {
315 * 指定されたクラス名を継承しているかどうかを返す
318 * @param string $class_name チェック対象のクラス名
319 * @param string $parent_name 親クラス名
320 * @return bool true:継承している false:いない
322 function _isSubclassOf($class_name, $parent_name)
324 while ($tmp = get_parent_class($class_name)) {
325 if (strcasecmp($tmp, $parent_name) == 0) {
334 * コントローラに明示的に定義されているアクション一覧を取得する
337 * @param array 定義されているクラス一覧
338 * @return array array(アクション一覧, クラス一覧)
340 function _getActionList_Manifest($class_list)
342 $manifest_action_list = array();
343 $manifest_class_list = array();
344 foreach ($this->ctl->action as $action_name => $action) {
345 if ($action_name == '__ethna_info__') {
348 $action = $this->ctl->_getAction($action_name);
351 // _analyzeActionList()で取得したクラス定義データから対応関係を取得
352 foreach ($class_list as $file => $elts) {
353 foreach ($elts as $class_name => $def) {
354 if ($def['type'] == 'action_class' && strcasecmp($class_name, $action['class_name']) == 0) {
355 $elt['action_class'] = $class_name;
356 $elt['action_class_file'] = $file;
357 $elt['action_class_info'] = $def;
358 } else if ($def['type'] == 'action_form' && strcasecmp($class_name, $action['form_name']) == 0) {
359 $elt['action_form'] = $class_name;
360 $elt['action_form_file'] = $file;
361 $elt['action_form_info'] = $def;
367 if (isset($elt['action_class']) == false) {
368 $elt['action_class'] = $action['class_name'];
369 if (class_exists($action['class_name']) == false) {
370 $elt['action_class_info'] = array('undef' => true);
373 if (isset($elt['action_form']) == false && $action['form_name'] != 'Ethna_ActionForm') {
374 $elt['action_form'] = $action['form_name'];
375 if (class_exists($action['form_name']) == false) {
376 $elt['action_form_info'] = array('undef' => true);
379 $manifest_action_list[$action_name] = $elt;
380 $manifest_class_list[] = strtolower($elt['action_class']);
383 return array($manifest_action_list, $manifest_class_list);
387 * 暗黙に定義されているアクション一覧を取得する
390 * @param array $class_list 定義されているクラス一覧
391 * @param array $manifest_action_list 明示的に定義済みのアクション一覧
392 * @param array $manifest_class_list 明示的に定義済みのクラス一覧
393 * @return array array:アクション一覧
395 function _getActionList_Implicit($class_list, $manifest_action_list, $manifest_class_list)
397 $implicit_action_list = array();
399 foreach ($class_list as $file => $elts) {
400 foreach ($elts as $class_name => $def) {
401 if (in_array(strtolower($class_name), $manifest_class_list)) {
406 if ($def['type'] == 'action_class') {
407 $action_name = $this->ctl->actionClassToName($class_name);
408 if (array_key_exists($action_name, $manifest_action_list)) {
411 $implicit_action_list[$action_name]['action_class'] = $class_name;
412 $implicit_action_list[$action_name]['action_class_file'] = $file;
413 $implicit_action_list[$action_name]['action_class_info'] = $def;
414 } else if ($def['type'] == 'action_form') {
415 $action_name = $this->ctl->actionFormToName($class_name);
416 if (array_key_exists($action_name, $manifest_action_list)) {
419 $implicit_action_list[$action_name]['action_form'] = $class_name;
420 $implicit_action_list[$action_name]['action_form_file'] = $file;
421 $implicit_action_list[$action_name]['action_form_info'] = $def;
428 return $implicit_action_list;
435 * @param array $action_list 取得したアクション一覧
436 * @return array 修正後のアクション一覧
438 function _addActionList($action_list)
440 foreach ($action_list as $action_name => $action) {
441 // アクションフォームにフォーム定義情報を追加
442 $form_name = $action['action_form'];
443 if (class_exists($form_name) == false) {
446 $af =& new $form_name($this->ctl);
449 foreach ($af->getDef() as $name => $def) {
450 $form[$name]['required'] = $def['required'] ? 'true' : 'false';
451 foreach (array('name', 'max', 'min', 'regexp', 'custom') as $key) {
452 $form[$name][$key] = $def[$key];
454 $form[$name]['filter'] = str_replace(",", "\n", $def['filter']);
455 $form[$name]['form_type'] = $this->getAttrName('form_type', $def['form_type']);
456 $form[$name]['type_is_array'] = is_array($def['type']);
457 $form[$name]['type'] = $this->getAttrName('var_type', is_array($def['type'])
458 ? $def['type'][0] : $def['type']);
460 $action['action_form_info']['form'] = $form;
461 $action_list[$action_name] = $action;
468 * ディレクトリ以下のテンプレートを解析する
471 * @param string $action_dir 解析対象のディレクトリ
472 * @return array 遷移定義一覧
474 function _analyzeForwardList($template_dir = null)
478 if (is_null($template_dir)) {
479 $template_dir = $this->ctl->getTemplatedir();
481 $prefix_len = strlen($this->ctl->getTemplatedir());
483 $child_dir_list = array();
485 $dh = opendir($template_dir);
490 $ext = $this->ctl->getExt('tpl');
491 while (($file = readdir($dh)) !== false) {
492 if ($file == "." || $file == "..") {
495 $file = $template_dir . '/' . $file;
498 $child_dir_list[] = $file;
502 if (preg_match("/\.$ext\$/", $file) == 0) {
506 $tpl = substr($file, $prefix_len);
507 $r[] = $this->ctl->forwardPathToName($tpl);
512 foreach ($child_dir_list as $child_dir) {
513 $tmp = $this->_analyzeForwardList($child_dir);
514 $r = array_merge($r, $tmp);
521 * コントローラに明示的に定義されている遷移先一覧を取得する
524 * @return array ビュー一覧
526 function _getForwardList_Manifest()
528 $manifest_forward_list = array();
529 foreach ($this->ctl->forward as $forward_name => $forward) {
530 if ($forward_name == '__ethna_info__') {
535 $elt['template_file'] = $this->ctl->_getForwardPath($forward_name);
536 if (file_exists(sprintf("%s/%s", $this->ctl->getTemplatedir(), $elt['template_file'])) == false) {
537 $elt['template_file_info'] = array('undef' => true);
540 $elt['view_class'] = $this->ctl->getViewClassName($forward_name);
541 if ($elt['view_class'] == 'Ethna_ViewClass') {
542 $elt['view_class'] = null;
543 } else if (class_exists($elt['view_class']) == false) {
544 $elt['view_class_info'] = array('undef' => true);
547 if (isset($forward['view_path']) && $forward['view_path']) {
548 $elt['view_path'] = $forward['view_path'];
549 } else if ($this->_isSubclassOf($elt['view_class'], 'Ethna_ViewClass')) {
550 $elt['view_class_file'] = $this->ctl->getDefaultViewPath($forward_name);
552 foreach ($this->cache_class_list as $file => $elts) {
553 foreach ($elts as $name => $def) {
554 if (strcasecmp($elt['view_class'], $name) == 0) {
555 $elt['view_class_file'] = $file;
562 $manifest_forward_list[$forward_name] = $elt;
565 return $manifest_forward_list;
569 * 暗黙に定義されているビュー一覧を取得する
572 * @param array $forward_list 定義されている遷移名一覧
573 * @param array $manifest_forward_list 明示的に定義済みのビュー一覧
574 * @return array array:ビュー一覧
576 function _getForwardList_Implicit($forward_list, $manifest_forward_list)
578 $implicit_forward_list = array();
579 $manifest_forward_name_list = array_keys($manifest_forward_list);
581 foreach ($forward_list as $forward_name) {
583 // エラー用のテンプレートはEthnaのデフォルトの命名規則に
585 // TODO: 2.5.0 の最終版が出るまでにこの点は改善される予定
586 // なので、改善され次第このif文は削除すべきである。
588 if (preg_match('/error[403|404|500]/', $forward_name)) {
592 if (in_array($forward_name, $manifest_forward_name_list)) {
597 $elt['template_file'] = $this->ctl->_getForwardPath($forward_name);
598 $elt['view_class'] = $this->ctl->getViewClassName($forward_name);
599 if ($elt['view_class'] == 'Ethna_ViewClass') {
600 $elt['view_class'] = null;
601 } else if (class_exists($elt['view_class']) == false) {
602 $elt['view_class'] = null;
604 $elt['view_class_file'] = $this->ctl->getDefaultViewPath($forward_name);
607 $implicit_forward_list[$forward_name] = $elt;
610 return $implicit_forward_list;
617 * @return array 設定一覧を格納した配列
618 * @todo respect access controll
620 function getConfiguration()
626 $elts[_et('Application ID')] = $this->ctl->getAppId();
627 $elts[_et('Application URL')] = $this->ctl->getURL();
628 $elts[_et('Ethna Version')] = ETHNA_VERSION;
629 $elts[_et('Ethna Base Directory')] = ETHNA_BASE;
634 $elts[_et('Backend')] = $this->class_factory->getObjectName('backend');
635 $elts[_et('ClassFactory')] = $this->class_factory->getObjectName('class');
636 $elts[_et('Config')] = $this->class_factory->getObjectName('config');
637 $elts['DB'] = $this->class_factory->getObjectName('db');
638 $elts[_et('Error')] = $this->class_factory->getObjectName('error');
639 $elts[_et('Form')] = $this->class_factory->getObjectName('form');
640 $elts[_et('Log')] = $this->class_factory->getObjectName('logger');
641 $elts['i18n'] = $this->class_factory->getObjectName('i18n');
642 $elts[_et('Plugin')] = $this->class_factory->getObjectName('plugin');
643 $elts[_et('Session')] = $this->class_factory->getObjectName('session');
644 $elts['SQL'] = $this->class_factory->getObjectName('sql');
645 $elts[_et('View')] = $this->class_factory->getObjectName('view');
646 $r[_et('Class')] = $elts;
651 foreach ($this->ctl->db as $key => $db) {
655 $tmp = sprintf('$db_%s', $key);
657 $elts[$tmp] = $this->getAttrName('db_type', $db);
658 $db_list[$key] = $tmp;
660 $r[_et('DB Type')] = $elts;
664 foreach ($db_list as $key => $name) {
667 $config_key .= "_$key";
669 $dsn = $this->config->get($config_key);
671 $elts[$name] = implode("\n", to_array($dsn));
678 $elts[_et('Application')] = $this->ctl->getBasedir();
679 $elts[_et('Action')] = $this->ctl->getActiondir();
680 $elts[_et('View')] = $this->ctl->getViewdir();
681 $elts[_et('Filter')] = $this->ctl->getDirectory('filter');
682 $elts[_et('Plugin')] = $this->ctl->getDirectory('plugin');
683 $elts[_et('Template')] = $this->ctl->getTemplatedir();
684 $elts[_et('Template Cache')] = $this->ctl->getDirectory('template_c');
685 $elts[_et('Smarty Plugin')] = implode(',', $this->ctl->getDirectory('plugins'));
686 $elts[_et('Configuration File')] = $this->ctl->getDirectory('etc');
687 $elts[_et('Locale')] = $this->ctl->getDirectory('locale');
688 $elts[_et('Logging')] = $this->ctl->getDirectory('log');
689 $elts[_et('Temporary File')] = $this->ctl->getDirectory('tmp');
690 $r[_et('Directory')] = $elts;
694 $elts[_et('Template')] = $this->ctl->getExt('tpl');
695 $elts[_et('PHP Script')] = $this->ctl->getExt('php');
696 $r[_et('File Extention')] = $elts;
701 foreach ($this->ctl->filter as $filter) {
702 $key = sprintf(_et('Filter(%d)'), $n);
703 if (class_exists($filter)) {
704 $elts[$key] = $filter;
708 $r[_et('Filter')] = $elts;
712 foreach ($this->ctl->getManagerList() as $key => $manager) {
713 $name = sprintf('$%s', $key);
714 $elts[$name] = $this->ctl->getManagerClassName($manager);
716 $r[_et('Application Manager')] = $elts;
725 * @return array 設定一覧を格納した配列
726 * @todo respect access controll
728 function getPluginList()
731 $plugin = $this->ctl->getPlugin();
732 foreach ($plugin->searchAllPluginType() as $type) {
733 $plugin->searchAllPluginSrc($type);
734 if (isset($plugin->src_registry[$type])) {
736 foreach ($plugin->src_registry[$type] as $name => $src) {
740 $elts[$name] = $src[2];