OSDN Git Service

misc: fixed spaces and indents
[ethna/ethna.git] / class / Controller.php
1 <?php
2 // vim: foldmethod=marker
3 /**
4  *  Controller.php
5  *
6  *  @author     Masaki Fujimoto <fujimoto@php.net>
7  *  @license    http://www.opensource.org/licenses/bsd-license.php The BSD License
8  *  @package    Ethna
9  *  @version    $Id$
10  */
11
12 // {{{ Ethna_Controller
13 /**
14  *  コントローラクラス
15  *
16  *  @todo       gatewayでswitchしてるところがダサダサ
17  *
18  *  @author     Masaki Fujimoto <fujimoto@php.net>
19  *  @access     public
20  *  @package    Ethna
21  */
22 class Ethna_Controller
23 {
24     /**#@+
25      *  @access protected
26      */
27
28     /** @var    string      アプリケーションID */
29     protected $appid = 'ETHNA';
30
31     /** @var    string      アプリケーションベースディレクトリ */
32     protected $base = '';
33
34     /** @protected    string      アプリケーションベースURL */
35     protected $url = '';
36
37     /** @protected    string      アプリケーションDSN(Data Source Name) */
38     protected $dsn;
39
40     /** @protected    array       アプリケーションディレクトリ */
41     protected $directory = array();
42
43     /** @protected    array       アプリケーションディレクトリ(デフォルト) */
44     protected $directory_default = array(
45         'action'        => 'app/action',
46         'action_cli'    => 'app/action_cli',
47         'action_xmlrpc' => 'app/action_xmlrpc',
48         'app'           => 'app',
49         'plugin'        => 'app/plugin',
50         'bin'           => 'bin',
51         'etc'           => 'etc',
52         'filter'        => 'app/filter',
53         'locale'        => 'locale',
54         'log'           => 'log',
55         'plugins'       => array(),
56         'template'      => 'template',
57         'template_c'    => 'tmp',
58         'tmp'           => 'tmp',
59         'view'          => 'app/view',
60         'www'           => 'www',
61         'test'          => 'app/test',
62     );
63
64     /** @protected    array       DBアクセス定義 */
65     protected $db = array(
66         ''              => DB_TYPE_RW,
67     );
68
69     /** @protected    array       拡張子設定 */
70     protected $ext = array(
71         'php'           => 'php',
72         'tpl'           => 'tpl',
73     );
74
75     /** @protected    array       クラス設定 */
76     public $class = array();
77
78     /** @protected    array       クラス設定(デフォルト) */
79     public $class_default = array(
80         'class'         => 'Ethna_ClassFactory',
81         'backend'       => 'Ethna_Backend',
82         'config'        => 'Ethna_Config',
83         'db'            => 'Ethna_DB',
84         'error'         => 'Ethna_ActionError',
85         'form'          => 'Ethna_ActionForm',
86         'i18n'          => 'Ethna_I18N',
87         'logger'        => 'Ethna_Logger',
88         'plugin'        => 'Ethna_Plugin',
89         'renderer'      => 'Ethna_Renderer_Smarty',
90         'session'       => 'Ethna_Session',
91         'sql'           => 'Ethna_AppSQL',
92         'view'          => 'Ethna_ViewClass',
93         'url_handler'   => 'Ethna_UrlHandler',
94     );
95
96     /** @protected    array       フィルタ設定 */
97     protected $filter = array(
98     );
99
100     /** @protected    string      使用ロケール設定 */
101     protected $locale;
102
103     /** @protected    string      システム側エンコーディング */
104     protected $system_encoding;
105
106     /** @protected    string      クライアント側エンコーディング */
107     /**                     ブラウザからのエンコーディングを指す  */
108     protected $client_encoding;
109
110     /** @protected    string  現在実行中のアクション名 */
111     protected $action_name;
112
113     /** @protected    string  現在実行中のXMLRPCメソッド名 */
114     protected $xmlrpc_method_name;
115
116     /** @protected    array   forward定義 */
117     protected $forward = array();
118
119     /** @protected    array   デフォルトのforward定義 */
120     protected $forward_default = array(
121         '403' => array( 'view_name' => 'Ethna_View_403',),
122         '404' => array( 'view_name' => 'Ethna_View_404',),
123         '500' => array( 'view_name' => 'Ethna_View_500',), 
124         'json' => array( 'view_name' => 'Ethna_View_Json',),
125         'redirect' => array( 'view_name' => 'Ethna_View_Redirect',),
126     );
127
128     /** @protected    array   action定義 */
129     protected $action = array();
130
131     /** @protected    array   action(CLI)定義 */
132     protected $action_cli = array();
133
134     /** @protected    array   action(XMLRPC)定義 */
135     protected $action_xmlrpc = array();
136
137     /** @protected    array   アプリケーションマネージャ定義 */
138     protected $manager = array();
139
140     /** @protected    object  レンダラー */
141     protected $renderer = null;
142
143     /** @protected    array   フィルターチェイン(Ethna_Filterオブジェクトの配列) */
144     protected $filter_chain = array();
145
146     /** @protected    object  Ethna_ClassFactory  クラスファクトリオブジェクト */
147     protected $class_factory = null;
148
149     /** @protected    object  Ethna_ActionForm    フォームオブジェクト */
150     protected $action_form = null;
151
152     /** @protected    object  Ethna_View          ビューオブジェクト */
153     protected $view = null;
154
155     /** @protected    object  Ethna_Config        設定オブジェクト */
156     protected $config = null;
157
158     /** @protected    object  Ethna_Logger        ログオブジェクト */
159     protected $logger = null;
160
161     /** @protected    object  Ethna_Plugin        プラグインオブジェクト */
162     protected $plugin = null;
163
164     /** @protected    string  リクエストのゲートウェイ(www/cli/rest/xmlrpc/soap...) */
165     protected $gateway = GATEWAY_WWW;
166
167     /**#@-*/
168
169
170     /**
171      *  Ethna_Controllerクラスのコンストラクタ
172      *
173      *  @access     public
174      */
175     public function __construct($gateway = GATEWAY_WWW)
176     {
177         $GLOBALS['_Ethna_controller'] = $this;
178         if ($this->base === "") {
179             // EthnaコマンドなどでBASEが定義されていない場合がある
180             if (defined('BASE')) {
181                 $this->base = BASE;
182             }
183         }
184
185         $this->gateway = $gateway;
186
187         // クラス設定の未定義値を補完
188         foreach ($this->class_default as $key => $val) {
189             if (isset($this->class[$key]) == false) {
190                 $this->class[$key] = $val;
191             }
192         }
193
194         // ディレクトリ設定の未定義値を補完
195         foreach ($this->directory_default as $key => $val) {
196             if (isset($this->directory[$key]) == false) {
197                 $this->directory[$key] = $val;
198             }
199         }
200
201         // クラスファクトリオブジェクトの生成
202         $class_factory = $this->class['class'];
203         $this->class_factory = new $class_factory($this, $this->class);
204
205         // エラーハンドラの設定
206         Ethna::setErrorCallback(array($this, 'handleError'));
207
208         // ディレクトリ名の設定(相対パス->絶対パス)
209         foreach ($this->directory as $key => $value) {
210             if ($key == 'plugins') {
211                 // Smartyプラグインディレクトリは配列で指定する
212                 $tmp = array();
213                 foreach (to_array($value) as $elt) {
214                     if (Ethna_Util::isAbsolute($elt) == false) {
215                         $tmp[] = $this->base . (empty($this->base) ? '' : '/') . $elt;
216                     }
217                 }
218                 $this->directory[$key] = $tmp;
219             } else {
220                 if (Ethna_Util::isAbsolute($value) == false) {
221                     $this->directory[$key] = $this->base . (empty($this->base) ? '' : '/') . $value;
222                 }
223             }
224         }
225
226         // 遷移先設定をマージ
227         // 但し、キーは完全に維持する
228         $this->forward = $this->forward + $this->forward_default;
229
230         // 初期設定
231         // フレームワークとしての内部エンコーディングはクライアント
232         // エンコーディング(=ブラウザからのエンコーディング)
233         //
234         // @see Ethna_Controller#_getDefaultLanguage
235         list($this->locale, $this->system_encoding, $this->client_encoding) = $this->_getDefaultLanguage();
236         if (mb_enabled()) {
237             mb_internal_encoding($this->client_encoding);
238             mb_regex_encoding($this->client_encoding);
239         }
240         $this->config = $this->getConfig();
241         $this->dsn = $this->_prepareDSN();
242         $this->url = $this->config->get('url');
243
244         // プラグインオブジェクトの用意
245         $this->plugin = $this->getPlugin();
246
247         // include Ethna_Plugin_Abstract for all plugins
248         $this->plugin->includePlugin('Abstract');
249
250         //// assert (experimental)
251         //if ($this->config->get('debug') === false) {
252         //    ini_set('assert.active', 0);
253         //}
254
255         // ログ出力開始
256         $this->logger = $this->getLogger();
257         $this->plugin->setLogger($this->logger);
258         $this->logger->begin();
259
260         // Ethnaマネージャ設定
261         $this->_activateEthnaManager();
262     }
263
264     /**
265      *  アプリケーション実行後の後始末を行います。
266      *
267      *  @access protected
268      */
269     protected function end()
270     {
271         //  必要に応じてオーバライドして下さい。
272         $this->logger->end();
273     }
274
275     /**
276      *  (現在アクティブな)コントローラのインスタンスを返す
277      *
278      *  @access public
279      *  @return object  Ethna_Controller    コントローラのインスタンス
280      *  @static
281      */
282     public static function getInstance()
283     {
284         if (isset($GLOBALS['_Ethna_controller'])) {
285             return $GLOBALS['_Ethna_controller'];
286         } else {
287             $_ret_object = null;
288             return $_ret_object;
289         }
290     }
291
292     /**
293      *  アプリケーションIDを返す
294      *
295      *  @access public
296      *  @return string  アプリケーションID
297      */
298     public function getAppId()
299     {
300         return ucfirst(strtolower($this->appid));
301     }
302
303     /**
304      *  アプリケーションIDをチェックする
305      *
306      *  @access public
307      *  @param  string  $id     アプリケーションID
308      *  @return mixed   true:OK Ethna_Error:NG
309      *  @static
310      */
311     public static function checkAppId($id)
312     {
313         $true = true;
314         if (strcasecmp($id, 'ethna') === 0
315             || strcasecmp($id, 'app') === 0) {
316             return Ethna::raiseError("Application Id [$id] is reserved\n");
317         }
318
319         //    アプリケーションIDはクラス名のprefixともなるため、
320         //    数字で始まっていてはいけない
321         //    @see http://www.php.net/manual/en/language.variables.php
322         if (preg_match('/^[a-zA-Z][a-zA-Z0-9]*$/', $id) === 0) {
323             $msg = (preg_match('/^[0-9]$/', $id[0]))
324                  ? "Application ID must NOT start with Number.\n"
325                  : "Only Numeric(0-9) and Alphabetical(A-Z) is allowed for Application Id\n";
326             return Ethna::raiseError($msg);
327         }
328         return $true;
329     }
330
331     /**
332      *  アクション名をチェックする
333      *
334      *  @access public
335      *  @param  string  $action_name    アクション名
336      *  @return mixed   true:OK Ethna_Error:NG
337      *  @static
338      */
339     public static function checkActionName($action_name)
340     {
341         $true = true;
342         if (preg_match('/^[a-zA-Z\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/',
343                        $action_name) === 0) {
344             return Ethna::raiseError("invalid action name [$action_name]");
345         }
346         return $true;
347     }
348
349     /**
350      *  ビュー名をチェックする
351      *
352      *  @access public
353      *  @param  string  $view_name    ビュー名
354      *  @return mixed   true:OK Ethna_Error:NG
355      *  @static
356      */
357     public static function checkViewName($view_name)
358     {
359         $true = true;
360         if (preg_match('/^[a-zA-Z\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/',
361                        $view_name) === 0) {
362             return Ethna::raiseError("invalid view name [$view_name]");
363         }
364         return $true;
365     }
366
367     /**
368      *  DSNを返す
369      *
370      *  @access public
371      *  @param  string  $db_key DBキー
372      *  @return string  DSN
373      */
374     public function getDSN($db_key = "")
375     {
376         if (isset($this->dsn[$db_key]) == false) {
377             return null;
378         }
379         return $this->dsn[$db_key];
380     }
381
382     /**
383      *  DSNの持続接続設定を返す
384      *
385      *  @access public
386      *  @param  string  $db_key DBキー
387      *  @return bool    true:persistent false:non-persistent(あるいは設定無し)
388      */
389     public function getDSN_persistent($db_key = "")
390     {
391         $key = sprintf("dsn%s_persistent", $db_key == "" ? "" : "_$db_key");
392
393         $dsn_persistent = $this->config->get($key);
394         if (is_null($dsn_persistent)) {
395             return false;
396         }
397         return $dsn_persistent;
398     }
399
400     /**
401      *  DB設定を返す
402      *
403      *  @access public
404      *  @param  string  $db_key DBキー("", "r", "rw", "default", "blog_r"...)
405      *  @return string  $db_keyに対応するDB種別定義(設定が無い場合はnull)
406      */
407     public function getDBType($db_key = null)
408     {
409         if (is_null($db_key)) {
410             // 一覧を返す
411             return $this->db;
412         }
413
414         if (isset($this->db[$db_key]) == false) {
415             return null;
416         }
417         return $this->db[$db_key];
418     }
419
420     /**
421      *  アプリケーションベースURLを返す
422      *
423      *  @access public
424      *  @return string  アプリケーションベースURL
425      */
426     public function getURL()
427     {
428         return $this->url;
429     }
430
431     /**
432      *  アプリケーションベースディレクトリを返す
433      *
434      *  @access public
435      *  @return string  アプリケーションベースディレクトリ
436      */
437     public function getBasedir()
438     {
439         return $this->base;
440     }
441
442     /**
443      *  クライアントタイプ/言語からテンプレートディレクトリ名を決定する
444      *  デフォルトでは [appid]/template/ja_JP/ (ja_JPはロケール名)
445      *  ロケール名は _getDefaultLanguage で決定される。
446      *
447      *  @access public
448      *  @return string  テンプレートディレクトリ
449      *  @see    Ethna_Controller#_getDefaultLanguage
450      */
451     public function getTemplatedir()
452     {
453         $template = $this->getDirectory('template');
454
455         // 言語別ディレクトリ
456         // _getDerfaultLanguageメソッドでロケールが指定されていた場合は、
457         // テンプレートディレクトリにも自動的にそれを付加する。
458         if (!empty($this->locale)) {
459             $template .= '/' . $this->locale;
460         }
461
462         return $template;
463     }
464
465     /**
466      *  アクションディレクトリ名を決定する
467      *
468      *  @access public
469      *  @return string  アクションディレクトリ
470      */
471     public function getActiondir($gateway = null)
472     {
473         $key = 'action';
474         $gateway = is_null($gateway) ? $this->getGateway() : $gateway;
475         switch ($gateway) {
476         case GATEWAY_WWW:
477             $key = 'action';
478             break;
479         case GATEWAY_CLI:
480             $key = 'action_cli';
481             break;
482         case GATEWAY_XMLRPC:
483             $key = 'action_xmlrpc';
484             break;
485         }
486
487         return (empty($this->directory[$key]) ? ($this->base . (empty($this->base) ? '' : '/')) : ($this->directory[$key] . "/"));
488     }
489
490     /**
491      *  ビューディレクトリ名を決定する
492      *
493      *  @access public
494      *  @return string  ビューディレクトリ
495      */
496     public function getViewdir()
497     {
498         return (empty($this->directory['view']) ? ($this->base . (empty($this->base) ? '' : '/')) : ($this->directory['view'] . "/"));
499     }
500
501     /**
502      *  (action,view以外の)テストケースを置くディレクトリ名を決定する
503      *
504      *  @access public
505      *  @return string  テストケースを置くディレクトリ
506      */
507     public function getTestdir()
508     {
509         return (empty($this->directory['test']) ? ($this->base . (empty($this->base) ? '' : '/')) : ($this->directory['test'] . "/"));
510     }
511
512     /**
513      *  アプリケーションディレクトリ設定を返す
514      *
515      *  @access public
516      *  @param  string  $key    ディレクトリタイプ("tmp", "template"...)
517      *  @return string  $keyに対応したアプリケーションディレクトリ(設定が無い場合はnull)
518      */
519     public function getDirectory($key)
520     {
521         if (isset($this->directory[$key]) == false) {
522             return null;
523         }
524         return $this->directory[$key];
525     }
526     /**
527      *  アプリケーションディレクトリ設定を返す
528      *
529      *  @access public
530      *  @param  string  $key    type
531      *  @return string  $key    directory
532      */
533     public function setDirectory($key, $value)
534     {
535         $this->directory[$key] = $value;
536     }
537
538
539     /**
540      *  アプリケーション拡張子設定を返す
541      *
542      *  @access public
543      *  @param  string  $key    拡張子タイプ("php", "tpl"...)
544      *  @return string  $keyに対応した拡張子(設定が無い場合はnull)
545      */
546     public function getExt($key)
547     {
548         if (isset($this->ext[$key]) == false) {
549             return null;
550         }
551         return $this->ext[$key];
552     }
553
554     /**
555      *  クラスファクトリオブジェクトのアクセサ(R)
556      *
557      *  @access public
558      *  @return object  Ethna_ClassFactory  クラスファクトリオブジェクト
559      */
560     public function getClassFactory()
561     {
562         return $this->class_factory;
563     }
564
565     /**
566      *  アクションエラーオブジェクトのアクセサ
567      *
568      *  @access public
569      *  @return object  Ethna_ActionError   アクションエラーオブジェクト
570      */
571     public function getActionError()
572     {
573         return $this->class_factory->getObject('error');
574     }
575
576     /**
577      *  Accessor for ActionForm
578      *
579      *  @access public
580      *  @return object  Ethna_ActionForm    アクションフォームオブジェクト
581      */
582     public function getActionForm()
583     {
584         // 明示的にクラスファクトリを利用していない
585         return $this->action_form;
586     }
587
588     /**
589      *  Setter for ActionForm
590      *  if the ::$action_form class is not null, then cannot set the view
591      *
592      *  @access public
593      *  @return object  Ethna_ActionForm    アクションフォームオブジェクト
594      */
595     public function setActionForm($af)
596     {
597         if ($this->action_form !== null) {
598             return false;
599         }
600         $this->action_form = $af;
601         return true;
602     }
603
604
605     /**
606      *  Accessor for ViewClass
607      *
608      *  @access public
609      *  @return object  Ethna_View          ビューオブジェクト
610      */
611     public function getView()
612     {
613         // 明示的にクラスファクトリを利用していない
614         return $this->view;
615     }
616
617     /**
618      *  Setter for ViewClass
619      *  if the ::$view class is not null, then cannot set the view
620      *
621      *  @access public
622      *  @param  $view object  Ethna_ViewClass
623      *  @return boolean
624      */
625     public function setView($view)
626     {
627         if ($this->view !== null) {
628             return false;
629         }
630         $this->view = $view;
631         return true;
632     }
633
634     /**
635      *  backendオブジェクトのアクセサ
636      *
637      *  @access public
638      *  @return object  Ethna_Backend   backendオブジェクト
639      */
640     public function getBackend()
641     {
642         return $this->class_factory->getObject('backend');
643     }
644
645     /**
646      *  設定オブジェクトのアクセサ
647      *
648      *  @access public
649      *  @return object  Ethna_Config    設定オブジェクト
650      */
651     public function getConfig()
652     {
653         return $this->class_factory->getObject('config');
654     }
655
656     /**
657      *  i18nオブジェクトのアクセサ(R)
658      *
659      *  @access public
660      *  @return object  Ethna_I18N  i18nオブジェクト
661      */
662     public function getI18N()
663     {
664         return $this->class_factory->getObject('i18n');
665     }
666
667     /**
668      *  ログオブジェクトのアクセサ
669      *
670      *  @access public
671      *  @return object  Ethna_Logger        ログオブジェクト
672      */
673     public function getLogger()
674     {
675         return $this->class_factory->getObject('logger');
676     }
677
678     /**
679      *  セッションオブジェクトのアクセサ
680      *
681      *  @access public
682      *  @return object  Ethna_Session       セッションオブジェクト
683      */
684     public function getSession()
685     {
686         return $this->class_factory->getObject('session');
687     }
688
689     /**
690      *  SQLオブジェクトのアクセサ
691      *
692      *  @access public
693      *  @return object  Ethna_AppSQL    SQLオブジェクト
694      */
695     public function getSQL()
696     {
697         return $this->class_factory->getObject('sql');
698     }
699
700     /**
701      *  プラグインオブジェクトのアクセサ
702      *
703      *  @access public
704      *  @return object  Ethna_Plugin    プラグインオブジェクト
705      */
706     public function getPlugin()
707     {
708         return $this->class_factory->getObject('plugin');
709     }
710
711     /**
712      *  URLハンドラオブジェクトのアクセサ
713      *
714      *  @access public
715      *  @return object  Ethna_UrlHandler    URLハンドラオブジェクト
716      */
717     public function getUrlHandler()
718     {
719         return $this->class_factory->getObject('url_handler');
720     }
721
722     /**
723      *  マネージャ一覧を返す
724      *
725      *  @access public
726      *  @return array   マネージャ一覧
727      *  @obsolete
728      */
729     public  function getManagerList()
730     {
731         return $this->manager;
732     }
733
734     /**
735      *  実行中のアクション名を返す
736      *
737      *  @access public
738      *  @return string  実行中のアクション名
739      */
740     public function getCurrentActionName()
741     {
742         return $this->action_name;
743     }
744
745     /**
746      *  実行中のXMLRPCメソッド名を返す
747      *
748      *  @access public
749      *  @return string  実行中のXMLRPCメソッド名
750      */
751     public function getXmlrpcMethodName()
752     {
753         return $this->xmlrpc_method_name;
754     }
755
756     /**
757      *  ロケール設定、使用言語を取得する
758      *
759      *  @access public
760      *  @return array   ロケール名(e.x ja_JP, en_US 等),
761      *                  システムエンコーディング名,
762      *                  クライアントエンコーディング名 の配列
763      *                  (ロケール名は、ll_cc の形式。ll = 言語コード cc = 国コード)
764      *  @see http://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html
765      */
766     public function getLanguage()
767     {
768         return array($this->locale, $this->system_encoding, $this->client_encoding);
769     }
770
771     /**
772      *  ロケール名へのアクセサ(R)
773      *
774      *  @access public
775      *  @return string  ロケール名(e.x ja_JP, en_US 等),
776      *                  (ロケール名は、ll_cc の形式。ll = 言語コード cc = 国コード)
777      */
778     public function getLocale()
779     {
780         return $this->locale;
781     }
782
783     /**
784      *  ロケール名へのアクセサ(W)
785      *
786      *  @access public
787      *  @param $locale ロケール名(e.x ja_JP, en_US 等),
788      *                 (ロケール名は、ll_cc の形式。ll = 言語コード cc = 国コード)
789      */
790     public function setLocale($locale)
791     {
792         $this->locale = $locale;
793         $i18n = $this->getI18N();
794         $i18n->setLanguage($this->locale, $this->system_encoding, $this->client_encoding);
795     }
796
797     /**
798      *  クライアントエンコーディング名へのアクセサ(R)
799      *
800      *  @access public
801      *  @return string  $client_encoding クライアントエンコーディング名
802      */
803     public function getClientEncoding()
804     {
805         return $this->client_encoding;
806     }
807
808     /**
809      *  クライアントエンコーディング名へのアクセサ(W)
810      *
811      *  @access public
812      *  @param  string  $client_encoding クライアントエンコーディング名
813      */
814     public function setClientEncoding($client_encoding)
815     {
816         $this->client_encoding = $client_encoding;
817         $i18n = $this->getI18N();
818         $i18n->setLanguage($this->locale, $this->system_encoding, $this->client_encoding);
819     }
820
821     /**
822      *  ゲートウェイを取得する
823      *
824      *  @access public
825      */
826     public function getGateway()
827     {
828         return $this->gateway;
829     }
830
831     /**
832      *  ゲートウェイモードを設定する
833      *
834      *  @access public
835      */
836     public function setGateway($gateway)
837     {
838         $this->gateway = $gateway;
839     }
840
841     /**
842      *  アプリケーションのエントリポイント
843      *
844      *  @access public
845      *  @param  string  $class_name     アプリケーションコントローラのクラス名
846      *  @param  mixed   $action_name    指定のアクション名(省略可)
847      *  @param  mixed   $fallback_action_name   アクションが決定できなかった場合に実行されるアクション名(省略可)
848      *  @static
849      */
850     public static function main($class_name, $action_name = "", $fallback_action_name = "")
851     {
852         $c = new $class_name;
853         $c->trigger($action_name, $fallback_action_name);
854         $c->end();
855     }
856
857     /**
858      *  CLIアプリケーションのエントリポイント
859      *
860      *  @access public
861      *  @param  string  $class_name     アプリケーションコントローラのクラス名
862      *  @param  string  $action_name    実行するアクション名
863      *  @param  bool    $enable_filter  フィルタチェインを有効にするかどうか
864      *  @static
865      */
866     public static function main_CLI($class_name, $action_name, $enable_filter = true)
867     {
868         $c = new $class_name(GATEWAY_CLI);
869         $c->action_cli[$action_name] = array();
870         $c->trigger($action_name, "", $enable_filter);
871         $c->end();
872     }
873
874     /**
875      *  XMLRPCアプリケーションのエントリポイント
876      *
877      *  @access public
878      *  @static
879      */
880     public static function main_XMLRPC($class_name)
881     {
882         if (extension_loaded('xmlrpc') == false) {
883             die("xmlrpc extension is required to enable this gateway");
884         }
885
886         $c = new $class_name(GATEWAY_XMLRPC);
887         $c->trigger("", "", false);
888         $c->end();
889     }
890
891     /**
892      *  SOAPアプリケーションのエントリポイント
893      *
894      *  @access public
895      *  @param  string  $class_name     アプリケーションコントローラのクラス名
896      *  @param  mixed   $action_name    指定のアクション名(省略可)
897      *  @param  mixed   $fallback_action_name   アクションが決定できなかった場合に実行されるアクション名(省略可)
898      *  @static
899      */
900     public static function main_SOAP($class_name, $action_name = "", $fallback_action_name = "")
901     {
902         $c = new $class_name(GATEWAY_SOAP);
903         $c->trigger($action_name, $fallback_action_name);
904         $c->end();
905     }
906
907     /**
908      *  フレームワークの処理を開始する
909      *
910      *  @access public
911      *  @param  mixed   $default_action_name    指定のアクション名
912      *  @param  mixed   $fallback_action_name   アクション名が決定できなかった場合に実行されるアクション名
913      *  @param  bool    $enable_filter  フィルタチェインを有効にするかどうか
914      *  @return mixed   0:正常終了 Ethna_Error:エラー
915      */
916     public function trigger($default_action_name = "", $fallback_action_name = "", $enable_filter = true)
917     {
918         // フィルターの生成
919         if ($enable_filter) {
920             $this->_createFilterChain();
921         }
922
923         // 実行前フィルタ
924         for ($i = 0; $i < count($this->filter_chain); $i++) {
925             $r = $this->filter_chain[$i]->preFilter();
926             if (Ethna::isError($r)) {
927                 return $r;
928             }
929         }
930
931         // trigger
932         switch ($this->getGateway()) {
933         case GATEWAY_WWW:
934             $this->_trigger_WWW($default_action_name, $fallback_action_name);
935             break;
936         case GATEWAY_CLI:
937             $this->_trigger_CLI($default_action_name);
938             break;
939         case GATEWAY_XMLRPC:
940             $this->_trigger_XMLRPC();
941             break;
942         case GATEWAY_SOAP:
943             $this->_trigger_SOAP();
944             break;
945         }
946
947         // 実行後フィルタ
948         for ($i = count($this->filter_chain) - 1; $i >= 0; $i--) {
949             $r = $this->filter_chain[$i]->postFilter();
950             if (Ethna::isError($r)) {
951                 return $r;
952             }
953         }
954     }
955
956     /**
957      *  フレームワークの処理を実行する(WWW)
958      *
959      *  引数$default_action_nameに配列が指定された場合、その配列で指定された
960      *  アクション以外は受け付けない(指定されていないアクションが指定された
961      *  場合、配列の先頭で指定されたアクションが実行される)
962      *
963      *  @access private
964      *  @param  mixed   $default_action_name    指定のアクション名
965      *  @param  mixed   $fallback_action_name   アクション名が決定できなかった場合に実行されるアクション名
966      *  @return mixed   0:正常終了 Ethna_Error:エラー
967      */
968     private function _trigger_WWW($default_action_name = "", $fallback_action_name = "")
969     {
970         // アクション名の取得
971         $action_name = $this->_getActionName($default_action_name, $fallback_action_name);
972
973         // マネージャ実行チェック
974         $this->_ethnaManagerEnabledCheck($action_name);
975
976         // アクション定義の取得
977         $action_obj = $this->_getAction($action_name);
978         if (is_null($action_obj)) {
979             if ($fallback_action_name != "") {
980                 $this->logger->log(LOG_DEBUG, 'undefined action [%s] -> try fallback action [%s]', $action_name, $fallback_action_name);
981                 $action_obj = $this->_getAction($fallback_action_name);
982             }
983             if (is_null($action_obj)) {
984                 return Ethna::raiseError("undefined action [%s]", E_APP_UNDEFINED_ACTION, $action_name);
985             } else {
986                 $action_name = $fallback_action_name;
987             }
988         }
989
990         // アクション実行前フィルタ
991         for ($i = 0; $i < count($this->filter_chain); $i++) {
992             $r = $this->filter_chain[$i]->preActionFilter($action_name);
993             if ($r != null) {
994                 $this->logger->log(LOG_DEBUG, 'action [%s] -> [%s] by %s', $action_name, $r, get_class($this->filter_chain[$i]));
995                 $action_name = $r;
996             }
997         }
998         $this->action_name = $action_name;
999
1000         // オブジェクト生成
1001         $backend = $this->getBackend();
1002         $session = $this->getSession();
1003         $session->restore();
1004
1005         // 言語切り替えフックを呼ぶ
1006         $this->_setLanguage($this->locale, $this->system_encoding, $this->client_encoding);
1007
1008         // アクションフォーム初期化
1009         // フォーム定義、フォーム値設定
1010         $form_name = $this->getActionFormName($action_name);
1011         $this->action_form = new $form_name($this);
1012         $this->action_form->setFormDef_PreHelper();
1013         $this->action_form->setFormVars();
1014         $backend->setActionForm($this->action_form);
1015
1016         // バックエンド処理実行
1017         $forward_name = $backend->perform($action_name);
1018
1019         // アクション実行後フィルタ
1020         for ($i = count($this->filter_chain) - 1; $i >= 0; $i--) {
1021             $r = $this->filter_chain[$i]->postActionFilter($action_name, $forward_name);
1022             if ($r != null) {
1023                 $this->logger->log(LOG_DEBUG, 'forward [%s] -> [%s] by %s', $forward_name, $r, get_class($this->filter_chain[$i]));
1024                 $forward_name = $r;
1025             }
1026         }
1027
1028         // コントローラで遷移先を決定する(オプション)
1029         $forward_name_params = $this->_sortForward($action_name, $forward_name);
1030
1031         // Viewへの引数があれば取り出す
1032         $preforward_params = array();
1033         if (is_array($forward_name_params)) {
1034             $forward_name = array_shift($forward_name_params);
1035             $preforward_params = $forward_name_params;
1036         }
1037         else {
1038             $forward_name = $forward_name_params;
1039         }
1040
1041         if ($forward_name != null) {
1042             $view_class_name = $this->getViewClassName($forward_name);
1043             $this->view = new $view_class_name($backend, $forward_name, $this->_getForwardPath($forward_name));
1044             call_user_func_array(array($this->view, 'preforward'), $preforward_params);
1045             $this->view->forward();
1046         }
1047
1048         return 0;
1049     }
1050
1051     /**
1052      *  フレームワークの処理を実行する(CLI)
1053      *
1054      *  @access private
1055      *  @param  mixed   $default_action_name    指定のアクション名
1056      *  @return mixed   0:正常終了 Ethna_Error:エラー
1057      */
1058     private function _trigger_CLI($default_action_name = "")
1059     {
1060         return $this->_trigger_WWW($default_action_name);
1061     }
1062
1063     /**
1064      *  フレームワークの処理を実行する(XMLRPC)
1065      *
1066      *  @access private
1067      *  @param  mixed   $action_name    指定のアクション名
1068      *  @return mixed   0:正常終了 Ethna_Error:エラー
1069      */
1070     private function _trigger_XMLRPC($action_name = "")
1071     {
1072         // prepare xmlrpc server
1073         $xmlrpc_gateway_method_name = "_Ethna_XmlrpcGateway";
1074         $xmlrpc_server = xmlrpc_server_create();
1075
1076         $method = null;
1077         $param = xmlrpc_decode_request(file_get_contents('php://input'), $method);
1078         $this->xmlrpc_method_name = $method;
1079
1080         $request = xmlrpc_encode_request(
1081             $xmlrpc_gateway_method_name,
1082             $param,
1083             array(
1084                 'output_type'   => 'xml',
1085                 'verbosity'     => 'pretty',
1086                 'escaping'      => array('markup'),
1087                 'version'       => 'xmlrpc',
1088                 'encoding'      => 'utf-8'
1089             )
1090         );
1091
1092         xmlrpc_server_register_method(
1093             $xmlrpc_server,
1094             $xmlrpc_gateway_method_name,
1095             $xmlrpc_gateway_method_name
1096         );
1097
1098         // send request
1099         $r = xmlrpc_server_call_method(
1100             $xmlrpc_server,
1101             $request,
1102             null,
1103             array(
1104                 'output_type'   => 'xml',
1105                 'verbosity'     => 'pretty',
1106                 'escaping'      => array('markup'),
1107                 'version'       => 'xmlrpc',
1108                 'encoding'      => 'utf-8'
1109             )
1110         );
1111
1112         header('Content-Length: ' . strlen($r));
1113         header('Content-Type: text/xml; charset=UTF-8');
1114         print $r;
1115     }
1116
1117     /**
1118      *  _trigger_XMLRPCのコールバックメソッド
1119      *
1120      *  @access public
1121      */
1122     private function trigger_XMLRPC($method, $param)
1123     {
1124         // アクション定義の取得
1125         $action_obj = $this->_getAction($method);
1126         if (is_null($action_obj)) {
1127             return Ethna::raiseError("undefined xmlrpc method [%s]", E_APP_UNDEFINED_ACTION, $method);
1128         }
1129
1130         // オブジェクト生成
1131         $backend = $this->getBackend();
1132
1133         $form_name = $this->getActionFormName($method);
1134         $this->action_form = new $form_name($this);
1135         $def = $this->action_form->getDef();
1136         $n = 0;
1137         foreach ($def as $key => $value) {
1138             if (isset($param[$n]) == false) {
1139                 $this->action_form->set($key, null);
1140             } else {
1141                 $this->action_form->set($key, $param[$n]);
1142             }
1143             $n++;
1144         }
1145
1146         // バックエンド処理実行
1147         $backend->setActionForm($this->action_form);
1148
1149         $session = $this->getSession();
1150         $session->restore();
1151         $r = $backend->perform($method);
1152
1153         return $r;
1154     }
1155
1156     /**
1157      *  SOAPフレームワークの処理を実行する
1158      *
1159      *  @access private
1160      */
1161     private function _trigger_SOAP()
1162     {
1163         // SOAPエントリクラス
1164         $gg = new Ethna_SOAP_GatewayGenerator();
1165         $script = $gg->generate();
1166         eval($script);
1167
1168         // SOAPリクエスト処理
1169         $server = new SoapServer(null, array('uri' => $this->config->get('url')));
1170         $server->setClass($gg->getClassName());
1171         $server->handle();
1172     }
1173
1174     /**
1175      *  エラーハンドラ
1176      *
1177      *  エラー発生時の追加処理を行いたい場合はこのメソッドをオーバーライドする
1178      *  (アラートメール送信等−デフォルトではログ出力時にアラートメール
1179      *  が送信されるが、エラー発生時に別にアラートメールをここで送信
1180      *  させることも可能)
1181      *
1182      *  @access public
1183      *  @param  object  Ethna_Error     エラーオブジェクト
1184      */
1185     public function handleError(&$error)
1186     {
1187         // ログ出力
1188         list ($log_level, $dummy) = $this->logger->errorLevelToLogLevel($error->getLevel());
1189         $message = $error->getMessage();
1190         $this->logger->log($log_level, sprintf("%s [ERROR CODE(%d)]", $message, $error->getCode()));
1191     }
1192
1193     /**
1194      *  エラーメッセージを取得する
1195      *
1196      *  @access public
1197      *  @param  int     $code       エラーコード
1198      *  @return string  エラーメッセージ
1199      */
1200     public function getErrorMessage($code)
1201     {
1202         $message_list = $GLOBALS['_Ethna_error_message_list'];
1203         for ($i = count($message_list)-1; $i >= 0; $i--) {
1204             if (array_key_exists($code, $message_list[$i])) {
1205                 return $message_list[$i][$code];
1206             }
1207         }
1208         return null;
1209     }
1210
1211     /**
1212      *  実行するアクション名を返す
1213      *
1214      *  @access private
1215      *  @param  mixed   $default_action_name    指定のアクション名
1216      *  @return string  実行するアクション名
1217      */
1218     private function _getActionName($default_action_name, $fallback_action_name)
1219     {
1220         // フォームから要求されたアクション名を取得する
1221         $form_action_name = $this->_getActionName_Form();
1222         $form_action_name = preg_replace('/[^a-z0-9\-_]+/i', '', $form_action_name);
1223         $this->logger->log(LOG_DEBUG, 'form_action_name[%s]', $form_action_name);
1224
1225         // Ethnaマネージャへのフォームからのリクエストは拒否
1226         if ($form_action_name == "__ethna_info__" ||
1227             $form_action_name == "__ethna_unittest__") {
1228             $form_action_name = "";
1229         }
1230
1231         // フォームからの指定が無い場合はエントリポイントに指定されたデフォルト値を利用する
1232         if ($form_action_name == "" && count($default_action_name) > 0) {
1233             $tmp = is_array($default_action_name) ? $default_action_name[0] : $default_action_name;
1234             if ($tmp{strlen($tmp)-1} == '*') {
1235                 $tmp = substr($tmp, 0, -1);
1236             }
1237             $this->logger->log(LOG_DEBUG, '-> default_action_name[%s]', $tmp);
1238             $action_name = $tmp;
1239         } else {
1240             $action_name = $form_action_name;
1241         }
1242
1243         // エントリポイントに配列が指定されている場合は指定以外のアクション名は拒否する
1244         if (is_array($default_action_name)) {
1245             if ($this->_isAcceptableActionName($action_name, $default_action_name) == false) {
1246                 // 指定以外のアクション名で合った場合は$fallback_action_name(or デフォルト)
1247                 $tmp = $fallback_action_name != "" ? $fallback_action_name : $default_action_name[0];
1248                 if ($tmp{strlen($tmp)-1} == '*') {
1249                     $tmp = substr($tmp, 0, -1);
1250                 }
1251                 $this->logger->log(LOG_DEBUG, '-> fallback_action_name[%s]', $tmp);
1252                 $action_name = $tmp;
1253             }
1254         }
1255
1256         $this->logger->log(LOG_DEBUG, '<<< action_name[%s] >>>', $action_name);
1257
1258         return $action_name;
1259     }
1260
1261     /**
1262      *  フォームにより要求されたアクション名を返す
1263      *
1264      *  アプリケーションの性質に応じてこのメソッドをオーバーライドして下さい。
1265      *  デフォルトでは"action_"で始まるフォーム値の"action_"の部分を除いたもの
1266      *  ("action_sample"なら"sample")がアクション名として扱われます
1267      *
1268      *  @access protected
1269      *  @return string  フォームにより要求されたアクション名
1270      */
1271     protected function _getActionName_Form()
1272     {
1273         if (isset($_SERVER['REQUEST_METHOD']) == false) {
1274             return null;
1275         }
1276
1277         $url_handler = $this->getUrlHandler();
1278         if ($_SERVER['REQUEST_METHOD'] == "GET") {
1279             $tmp_vars = $_GET;
1280         } else if ($_SERVER['REQUEST_METHOD'] == "POST") {
1281             $tmp_vars = $_POST;
1282         }
1283
1284         if (empty($_SERVER['URL_HANDLER']) == false) {
1285             $tmp_vars['__url_handler__'] = $_SERVER['URL_HANDLER'];
1286             $tmp_vars['__url_info__'] = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : null;
1287             $tmp_vars = $url_handler->requestToAction($tmp_vars);
1288
1289             if ($_SERVER['REQUEST_METHOD'] == "GET") {
1290                 $_GET = array_merge($_GET, $tmp_vars);
1291             } else if ($_SERVER['REQUEST_METHOD'] == "POST") {
1292                 $_POST = array_merge($_POST, $tmp_vars);
1293             }
1294             $_REQUEST = array_merge($_REQUEST, $tmp_vars);
1295         }
1296
1297         if (strcasecmp($_SERVER['REQUEST_METHOD'], 'post') == 0) {
1298             $http_vars = $_POST;
1299         } else {
1300             $http_vars = $_GET;
1301         }
1302
1303         // フォーム値からリクエストされたアクション名を取得する
1304         $action_name = $sub_action_name = null;
1305         foreach ($http_vars as $name => $value) {
1306             if ($value == "" || strncmp($name, 'action_', 7) != 0) {
1307                 continue;
1308             }
1309
1310             $tmp = substr($name, 7);
1311
1312             // type="image"対応
1313             if (preg_match('/_x$/', $name) || preg_match('/_y$/', $name)) {
1314                 $tmp = substr($tmp, 0, strlen($tmp)-2);
1315             }
1316
1317             // value="dummy"となっているものは優先度を下げる
1318             if ($value == "dummy") {
1319                 $sub_action_name = $tmp;
1320             } else {
1321                 $action_name = $tmp;
1322             }
1323         }
1324         if ($action_name == null) {
1325             $action_name = $sub_action_name;
1326         }
1327
1328         return $action_name;
1329     }
1330
1331     /**
1332      *  アクション名を指定するクエリ/HTMLを生成する
1333      *
1334      *  @access public
1335      *  @param  string  $action action to request
1336      *  @param  string  $type   hidden, url...
1337      *  @todo   consider gateway
1338      */
1339     public function getActionRequest($action, $type = "hidden")
1340     {
1341         $s = null;
1342         if ($type == "hidden") {
1343             $s = sprintf('<input type="hidden" name="action_%s" value="true" />', htmlspecialchars($action, ENT_QUOTES));
1344         } else if ($type == "url") {
1345             $s = sprintf('action_%s=true', urlencode($action));
1346         }
1347         return $s;
1348     }
1349
1350     /**
1351      *  フォームにより要求されたアクション名に対応する定義を返す
1352      *
1353      *  @access private
1354      *  @param  string  $action_name    アクション名
1355      *  @return array   アクション定義
1356      */
1357     public function _getAction($action_name, $gateway = null)
1358     {
1359         $action = array();
1360         $gateway = is_null($gateway) ? $this->getGateway() : $gateway;
1361         switch ($gateway) {
1362         case GATEWAY_WWW:
1363             $action = $this->action;
1364             break;
1365         case GATEWAY_CLI:
1366             $action = $this->action_cli;
1367             break;
1368         case GATEWAY_XMLRPC:
1369             $action = $this->action_xmlrpc;
1370             break;
1371         }
1372
1373         $action_obj = array();
1374         if (isset($action[$action_name])) {
1375             $action_obj = $action[$action_name];
1376             if (isset($action_obj['inspect']) && $action_obj['inspect']) {
1377                 return $action_obj;
1378             }
1379         } else {
1380             $this->logger->log(LOG_DEBUG, "action [%s] is not defined -> try default", $action_name);
1381         }
1382
1383         // アクションスクリプトのインクルード
1384         $this->_includeActionScript($action_obj, $action_name);
1385
1386         // 省略値の補正
1387         if (isset($action_obj['class_name']) == false) {
1388             $action_obj['class_name'] = $this->getDefaultActionClass($action_name);
1389         }
1390
1391         if (isset($action_obj['form_name']) == false) {
1392             $action_obj['form_name'] = $this->getDefaultFormClass($action_name);
1393         } else if (class_exists($action_obj['form_name']) == false) {
1394             // 明示指定されたフォームクラスが定義されていない場合は警告
1395             $this->logger->log(LOG_WARNING, 'stated form class is not defined [%s]', $action_obj['form_name']);
1396         }
1397
1398         // 必要条件の確認
1399         if (class_exists($action_obj['class_name']) == false) {
1400             $this->logger->log(LOG_NOTICE, 'action class is not defined [%s]', $action_obj['class_name']);
1401             $_ret_object = null;
1402             return $_ret_object;
1403         }
1404         if (class_exists($action_obj['form_name']) == false) {
1405             // フォームクラスは未定義でも良い
1406             $class_name = $this->class_factory->getObjectName('form');
1407             $this->logger->log(LOG_DEBUG, 'form class is not defined [%s] -> falling back to default [%s]', $action_obj['form_name'], $class_name);
1408             $action_obj['form_name'] = $class_name;
1409         }
1410
1411         $action_obj['inspect'] = true;
1412         $action[$action_name] = $action_obj;
1413         return $action[$action_name];
1414     }
1415
1416     /**
1417      *  アクション名とアクションクラスからの戻り値に基づいて遷移先を決定する
1418      *
1419      *  @access protected
1420      *  @param  string  $action_name    アクション名
1421      *  @param  string  $retval         アクションクラスからの戻り値
1422      *  @return string  遷移先
1423      */
1424     protected function _sortForward($action_name, $retval)
1425     {
1426         return $retval;
1427     }
1428
1429     /**
1430      *  フィルタチェインを生成する
1431      *
1432      *  @access protected
1433      */
1434     protected function _createFilterChain()
1435     {
1436         $this->filter_chain = array();
1437         foreach ($this->filter as $filter) {
1438             $filter_plugin = $this->plugin->getPlugin('Filter', $filter);
1439             if (Ethna::isError($filter_plugin)) {
1440                 continue;
1441             }
1442
1443             $this->filter_chain[] = $filter_plugin;
1444         }
1445     }
1446
1447     /**
1448      *  アクション名が実行許可されているものかどうかを返す
1449      *
1450      *  @access private
1451      *  @param  string  $action_name            リクエストされたアクション名
1452      *  @param  array   $default_action_name    許可されているアクション名
1453      *  @return bool    true:許可 false:不許可
1454      */
1455     private function _isAcceptableActionName($action_name, $default_action_name)
1456     {
1457         foreach (to_array($default_action_name) as $name) {
1458             if ($action_name == $name) {
1459                 return true;
1460             } else if ($name{strlen($name)-1} == '*') {
1461                 if (strncmp($action_name, substr($name, 0, -1), strlen($name)-1) == 0) {
1462                     return true;
1463                 }
1464             }
1465         }
1466         return false;
1467     }
1468
1469     /**
1470      *  指定されたアクションのフォームクラス名を返す(オブジェクトの生成は行わない)
1471      *
1472      *  @access public
1473      *  @param  string  $action_name    アクション名
1474      *  @return string  アクションのフォームクラス名
1475      */
1476     public function getActionFormName($action_name)
1477     {
1478         $action_obj = $this->_getAction($action_name);
1479         if (is_null($action_obj)) {
1480             return null;
1481         }
1482
1483         return $action_obj['form_name'];
1484     }
1485
1486     /**
1487      *  アクションに対応するフォームクラス名が省略された場合のデフォルトクラス名を返す
1488      *
1489      *  デフォルトでは[プロジェクトID]_Form_[アクション名]となるので好み応じてオーバライドする
1490      *
1491      *  @access public
1492      *  @param  string  $action_name    アクション名
1493      *  @return string  アクションフォーム名
1494      */
1495     public function getDefaultFormClass($action_name, $gateway = null)
1496     {
1497         $gateway_prefix = $this->_getGatewayPrefix($gateway);
1498
1499         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($action_name));
1500         $r = sprintf("%s_%sForm_%s", $this->getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
1501         $this->logger->log(LOG_DEBUG, "default action class [%s]", $r);
1502
1503         return $r;
1504     }
1505
1506     /**
1507      *  getDefaultFormClass()で取得したクラス名からアクション名を取得する
1508      *
1509      *  getDefaultFormClass()をオーバーライドした場合、こちらも合わせてオーバーライド
1510      *  することを推奨(必須ではない)
1511      *
1512      *  @access public
1513      *  @param  string  $class_name     フォームクラス名
1514      *  @return string  アクション名
1515      */
1516     public function actionFormToName($class_name)
1517     {
1518         $prefix = sprintf("%s_Form_", $this->getAppId());
1519         if (preg_match("/$prefix(.*)/", $class_name, $match) == 0) {
1520             // 不明なクラス名
1521             return null;
1522         }
1523         $target = $match[1];
1524
1525         $action_name = substr(preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $target), 1);
1526
1527         return $action_name;
1528     }
1529
1530     /**
1531      *  アクションに対応するフォームパス名が省略された場合のデフォルトパス名を返す
1532      *
1533      *  デフォルトでは_getDefaultActionPath()と同じ結果を返す(1ファイルに
1534      *  アクションクラスとフォームクラスが記述される)ので、好みに応じて
1535      *  オーバーライドする
1536      *
1537      *  @access public
1538      *  @param  string  $action_name    アクション名
1539      *  @return string  form classが定義されるスクリプトのパス名
1540      */
1541     public function getDefaultFormPath($action_name)
1542     {
1543         return $this->getDefaultActionPath($action_name);
1544     }
1545
1546     /**
1547      *  指定されたアクションのクラス名を返す(オブジェクトの生成は行わない)
1548      *
1549      *  @access public
1550      *  @param  string  $action_name    アクションの名称
1551      *  @return string  アクションのクラス名
1552      */
1553     public function getActionClassName($action_name)
1554     {
1555         $action_obj = $this->_getAction($action_name);
1556         if ($action_obj == null) {
1557             return null;
1558         }
1559
1560         return $action_obj['class_name'];
1561     }
1562
1563     /**
1564      *  アクションに対応するアクションクラス名が省略された場合のデフォルトクラス名を返す
1565      *
1566      *  デフォルトでは[プロジェクトID]_Action_[アクション名]となるので好み応じてオーバライドする
1567      *
1568      *  @access public
1569      *  @param  string  $action_name    アクション名
1570      *  @return string  アクションクラス名
1571      */
1572     public function getDefaultActionClass($action_name, $gateway = null)
1573     {
1574         $gateway_prefix = $this->_getGatewayPrefix($gateway);
1575
1576         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($action_name));
1577         $r = sprintf("%s_%sAction_%s", $this->getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
1578         $this->logger->log(LOG_DEBUG, "default action class [%s]", $r);
1579
1580         return $r;
1581     }
1582
1583     /**
1584      *  getDefaultActionClass()で取得したクラス名からアクション名を取得する
1585      *
1586      *  getDefaultActionClass()をオーバーライドした場合、こちらも合わせてオーバーライド
1587      *  することを推奨(必須ではない)
1588      *
1589      *  @access public
1590      *  @param  string  $class_name     アクションクラス名
1591      *  @return string  アクション名
1592      */
1593     public function actionClassToName($class_name)
1594     {
1595         $prefix = sprintf("%s_Action_", $this->getAppId());
1596         if (preg_match("/$prefix(.*)/", $class_name, $match) == 0) {
1597             // 不明なクラス名
1598             return null;
1599         }
1600         $target = $match[1];
1601
1602         $action_name = substr(preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $target), 1);
1603
1604         return $action_name;
1605     }
1606
1607     /**
1608      *  アクションに対応するアクションパス名が省略された場合のデフォルトパス名を返す
1609      *
1610      *  デフォルトでは"foo_bar" -> "/Foo/Bar.php"となるので好み応じてオーバーライドする
1611      *
1612      *  @access public
1613      *  @param  string  $action_name    アクション名
1614      *  @return string  アクションクラスが定義されるスクリプトのパス名
1615      */
1616     public function getDefaultActionPath($action_name)
1617     {
1618         $r = preg_replace('/_(.)/e', "'/' . strtoupper('\$1')", ucfirst($action_name)) . '.' . $this->getExt('php');
1619         $this->logger->log(LOG_DEBUG, "default action path [%s]", $r);
1620
1621         return $r;
1622     }
1623
1624     /**
1625      *  指定された遷移名に対応するビュークラス名を返す(オブジェクトの生成は行わない)
1626      *
1627      *  @access public
1628      *  @param  string  $forward_name   遷移先の名称
1629      *  @return string  view classのクラス名
1630      */
1631     public function getViewClassName($forward_name)
1632     {
1633         if ($forward_name == null) {
1634             return null;
1635         }
1636
1637         if (isset($this->forward[$forward_name])) {
1638             $forward_obj = $this->forward[$forward_name];
1639         } else {
1640             $forward_obj = array();
1641         }
1642
1643         if (isset($forward_obj['view_name'])) {
1644             $class_name = $forward_obj['view_name'];
1645             if (class_exists($class_name)) {
1646                 return $class_name;
1647             }
1648         } else {
1649             $class_name = null;
1650         }
1651
1652         // viewのインクルード
1653         $this->_includeViewScript($forward_obj, $forward_name);
1654
1655         if (is_null($class_name) == false && class_exists($class_name)) {
1656             return $class_name;
1657         } else if (is_null($class_name) == false) {
1658             $this->logger->log(LOG_WARNING, 'stated view class is not defined [%s] -> try default', $class_name);
1659         }
1660
1661         $class_name = $this->getDefaultViewClass($forward_name);
1662         if (class_exists($class_name)) {
1663             return $class_name;
1664         } else {
1665             $class_name = $this->class_factory->getObjectName('view');
1666             $this->logger->log(LOG_DEBUG, 'view class is not defined for [%s] -> use default [%s]', $forward_name, $class_name);
1667             return $class_name;
1668         }
1669     }
1670
1671     /**
1672      *  遷移名に対応するビュークラス名が省略された場合のデフォルトクラス名を返す
1673      *
1674      *  デフォルトでは[プロジェクトID]_View_[遷移名]となるので好み応じてオーバライドする
1675      *
1676      *  @access public
1677      *  @param  string  $forward_name   forward名
1678      *  @return string  view classクラス名
1679      */
1680     public function getDefaultViewClass($forward_name, $gateway = null)
1681     {
1682         $gateway_prefix = $this->_getGatewayPrefix($gateway);
1683
1684         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($forward_name));
1685         $r = sprintf("%s_%sView_%s", $this->getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
1686         $this->logger->log(LOG_DEBUG, "default view class [%s]", $r);
1687
1688         return $r;
1689     }
1690
1691     /**
1692      *  遷移名に対応するビューパス名が省略された場合のデフォルトパス名を返す
1693      *
1694      *  デフォルトでは"foo_bar" -> "/Foo/Bar.php"となるので好み応じてオーバーライドする
1695      *
1696      *  @access public
1697      *  @param  string  $forward_name   forward名
1698      *  @return string  view classが定義されるスクリプトのパス名
1699      */
1700     public function getDefaultViewPath($forward_name)
1701     {
1702         $r = preg_replace('/_(.)/e', "'/' . strtoupper('\$1')", ucfirst($forward_name)) . '.' . $this->getExt('php');
1703         $this->logger->log(LOG_DEBUG, "default view path [%s]", $r);
1704
1705         return $r;
1706     }
1707
1708     /**
1709      *  遷移名に対応するテンプレートパス名が省略された場合のデフォルトパス名を返す
1710      *
1711      *  デフォルトでは"foo_bar"というforward名が"foo/bar" + テンプレート拡張子となる
1712      *  ので好み応じてオーバライドする
1713      *
1714      *  @access public
1715      *  @param  string  $forward_name   forward名
1716      *  @return string  forwardパス名
1717      */
1718     public function getDefaultForwardPath($forward_name)
1719     {
1720         return str_replace('_', '/', $forward_name) . '.' . $this->ext['tpl'];
1721     }
1722
1723     /**
1724      *  テンプレートパス名から遷移名を取得する
1725      *
1726      *  getDefaultForwardPath()をオーバーライドした場合、こちらも合わせてオーバーライド
1727      *  することを推奨(必須ではない)
1728      *
1729      *  @access public
1730      *  @param  string  $forward_path   テンプレートパス名
1731      *  @return string  遷移名
1732      */
1733     public function forwardPathToName($forward_path)
1734     {
1735         $forward_path = preg_replace('/^\/+/', '', $forward_path);
1736         $forward_path = preg_replace(sprintf('/\.%s$/', $this->getExt('tpl')), '', $forward_path);
1737
1738         return str_replace('/', '_', $forward_path);
1739     }
1740
1741     /**
1742      *  遷移名からテンプレートファイルのパス名を取得する
1743      *
1744      *  @access private
1745      *  @param  string  $forward_name   forward名
1746      *  @return string  テンプレートファイルのパス名
1747      */
1748     public function _getForwardPath($forward_name)
1749     {
1750         $forward_obj = null;
1751
1752         if (isset($this->forward[$forward_name]) == false) {
1753             // try default
1754             $this->forward[$forward_name] = array();
1755         }
1756         $forward_obj = $this->forward[$forward_name];
1757         if (isset($forward_obj['forward_path']) == false) {
1758             // 省略値補正
1759             $forward_obj['forward_path'] = $this->getDefaultForwardPath($forward_name);
1760         }
1761
1762         return $forward_obj['forward_path'];
1763     }
1764
1765     /**
1766      *  レンダラを取得する(getTemplateEngine()はそのうち廃止されgetRenderer()に統合される予定)
1767      *
1768      *  @access public
1769      *  @return object  Ethna_Renderer  レンダラオブジェクト
1770      */
1771     public function getRenderer()
1772     {
1773         // if action is __ethna_info__, the renderer must be Smarty2
1774         if ($this->action_name == '__ethna_info__') {
1775             require_once ETHNA_BASE . '/class/Renderer/Smarty.php';
1776             return new Ethna_Renderer_Smarty($this);
1777         }
1778         $_ret_object = $this->getTemplateEngine();
1779         return $_ret_object;
1780     }
1781
1782     /**
1783      *  テンプレートエンジン取得する
1784      *
1785      *  @access public
1786      *  @return object  Ethna_Renderer  レンダラオブジェクト
1787      *  @obsolete
1788      */
1789     public function getTemplateEngine()
1790     {
1791         if (is_object($this->renderer)) {
1792             return $this->renderer;
1793         }
1794
1795         $this->renderer = $this->class_factory->getObject('renderer');
1796
1797         //テンプレートエンジンのデフォルトの設定
1798         $this->_setDefaultTemplateEngine($this->renderer);
1799         // }}}
1800
1801         return $this->renderer;
1802     }
1803
1804     /**
1805      *  テンプレートエンジンのデフォルト状態を設定する
1806      *
1807      *  @access protected
1808      *  @param  object  Ethna_Renderer  レンダラオブジェクト
1809      *  @obsolete
1810      */
1811     protected function _setDefaultTemplateEngine($renderer)
1812     {
1813     }
1814
1815     /**
1816      *  使用言語、ロケールを設定する
1817      *  条件によって使用言語、ロケールを切り替えたい場合は、
1818      *  このメソッドをオーバーライドする。
1819      *
1820      *  @access protected
1821      *  @param  string  $locale             ロケール名(ja_JP, en_US等)
1822      *                                      (ll_cc の形式。ll = 言語コード cc = 国コード)
1823      *  @param  string  $system_encoding    システムエンコーディング名
1824      *  @param  string  $client_encoding    クライアントエンコーディング(テンプレートのエンコーディングと考えれば良い)
1825      *  @see    http://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html
1826      *  @see    Ethna_Controller#_getDefaultLanguage
1827      */
1828     protected function _setLanguage($locale, $system_encoding = null, $client_encoding = null)
1829     {
1830         $this->locale = $locale;
1831         $this->system_encoding = $system_encoding;
1832         $this->client_encoding = $client_encoding;
1833
1834         //   $this->locale, $this->client_encoding を書き換えた場合は
1835         //   必ず Ethna_I18N クラスの setLanguageメソッドも呼ぶこと!
1836         //   さもないとカタログその他が再ロードされない!
1837         $i18n = $this->getI18N();
1838         $i18n->setLanguage($locale, $system_encoding, $client_encoding);
1839     }
1840
1841     /**
1842      *  デフォルト状態での使用言語を取得する
1843      *  外部に出力されるEthnaのエラーメッセージ等のエンコーディングを
1844      *  切り替えたい場合は、このメソッドをオーバーライドする。
1845      *
1846      *  @access protected
1847      *  @return array   ロケール名(e.x ja_JP, en_US 等),
1848      *                  システムエンコーディング名,
1849      *                  クライアントエンコーディング名
1850      *                  (= テンプレートのエンコーディングと考えてよい) の配列
1851      *                  (ロケール名は ll_cc の形式。ll = 言語コード cc = 国コード)
1852      *
1853      *  WARNING!! : クライアントエンコーディング名が、フレームワークの内部エンコーデ
1854      *              ィングとして設定されます。つまり、クライアントエンコーディングで
1855      *              ブラウザからの入力は入ってくるものと想定しています!
1856      */
1857     protected function _getDefaultLanguage()
1858     {
1859         return array('ja_JP', 'UTF-8', 'UTF-8');
1860     }
1861
1862     /**
1863      *  デフォルト状態でのゲートウェイを取得する
1864      *
1865      *  @access protected
1866      *  @return int     ゲートウェイ定義(GATEWAY_WWW, GATEWAY_CLI...)
1867      */
1868     protected function _getDefaultGateway($gateway)
1869     {
1870         if (is_null($GLOBALS['_Ethna_gateway']) == false) {
1871             return $GLOBALS['_Ethna_gateway'];
1872         }
1873         return GATEWAY_WWW;
1874     }
1875
1876     /**
1877      *  ゲートウェイに対応したクラス名のプレフィクスを取得する
1878      *
1879      *  @access public
1880      *  @param  string  $gateway    ゲートウェイ
1881      *  @return string  ゲートウェイクラスプレフィクス
1882      */
1883     protected function _getGatewayPrefix($gateway = null)
1884     {
1885         $gateway = is_null($gateway) ? $this->getGateway() : $gateway;
1886         switch ($gateway) {
1887         case GATEWAY_WWW:
1888             $prefix = '';
1889             break;
1890         case GATEWAY_CLI:
1891             $prefix = 'Cli';
1892             break;
1893         case GATEWAY_XMLRPC:
1894             $prefix = 'Xmlrpc';
1895             break;
1896         default:
1897             $prefix = '';
1898             break;
1899         }
1900
1901         return $prefix;
1902     }
1903
1904     /**
1905      *  マネージャクラス名を取得する
1906      *
1907      *  @access public
1908      *  @param  string  $name   マネージャキー
1909      *  @return string  マネージャクラス名
1910      */
1911     public function getManagerClassName($name)
1912     {
1913         //   アプリケーションIDと、渡された名前のはじめを大文字にして、
1914         //   組み合わせたものが返される
1915         $manager_id = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($name));
1916         return sprintf('%s_%sManager', $this->getAppId(), ucfirst($manager_id));
1917     }
1918
1919     /**
1920      *  アプリケーションオブジェクトクラス名を取得する
1921      *
1922      *  @access public
1923      *  @param  string  $name   アプリケーションオブジェクトキー
1924      *  @return string  マネージャクラス名
1925      */
1926     public function getObjectClassName($name)
1927     {
1928         //  引数のはじめの一文字目と、アンダーバー直後の
1929         //  1文字を必ず大文字にする。アンダーバーは削除される。
1930         $name = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($name));
1931
1932         //  $name に foo_bar を渡し、AppID が Hogeの場合
1933         //  [Appid]_FooBar が返される
1934         return sprintf('%s_%s', $this->getAppId(), $name);
1935     }
1936
1937     /**
1938      *  アクションスクリプトをインクルードする
1939      *
1940      *  ただし、インクルードしたファイルにクラスが正しく定義されているかどうかは保証しない
1941      *
1942      *  @access private
1943      *  @param  array   $action_obj     アクション定義
1944      *  @param  string  $action_name    アクション名
1945      */
1946     protected function _includeActionScript($action_obj, $action_name)
1947     {
1948         $class_path = $form_path = null;
1949
1950         $action_dir = $this->getActiondir();
1951
1952         // class_path属性チェック
1953         if (isset($action_obj['class_path'])) {
1954             // フルパス指定サポート
1955             $tmp_path = $action_obj['class_path'];
1956             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1957                 $tmp_path = $action_dir . $tmp_path;
1958             }
1959
1960             if (file_exists($tmp_path) == false) {
1961                 $this->logger->log(LOG_WARNING, 'class_path file not found [%s] -> try default', $tmp_path);
1962             } else {
1963                 include_once $tmp_path;
1964                 $class_path = $tmp_path;
1965             }
1966         }
1967
1968         // デフォルトチェック
1969         if (is_null($class_path)) {
1970             $class_path = $this->getDefaultActionPath($action_name);
1971             if (file_exists($action_dir . $class_path)) {
1972                 include_once $action_dir . $class_path;
1973             } else {
1974                 $this->logger->log(LOG_DEBUG, 'default action file not found [%s] -> try all files', $class_path);
1975                 return;
1976             }
1977         }
1978
1979         // form_path属性チェック
1980         if (isset($action_obj['form_path'])) {
1981             // フルパス指定サポート
1982             $tmp_path = $action_obj['form_path'];
1983             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1984                 $tmp_path = $action_dir . $tmp_path;
1985             }
1986
1987             if ($tmp_path == $class_path) {
1988                 return;
1989             }
1990             if (file_exists($tmp_path) == false) {
1991                 $this->logger->log(LOG_WARNING, 'form_path file not found [%s] -> try default', $tmp_path);
1992             } else {
1993                 include_once $tmp_path;
1994                 $form_path = $tmp_path;
1995             }
1996         }
1997
1998         // デフォルトチェック
1999         if (is_null($form_path)) {
2000             $form_path = $this->getDefaultFormPath($action_name);
2001             if ($form_path == $class_path) {
2002                 return;
2003             }
2004             if (file_exists($action_dir . $form_path)) {
2005                 include_once $action_dir . $form_path;
2006             } else {
2007                 $this->logger->log(LOG_DEBUG, 'default form file not found [%s] -> maybe falling back to default form class', $form_path);
2008             }
2009         }
2010     }
2011
2012     /**
2013      *  ビュースクリプトをインクルードする
2014      *
2015      *  ただし、インクルードしたファイルにクラスが正しく定義されているかどうかは保証しない
2016      *
2017      *  @access private
2018      *  @param  array   $forward_obj    遷移定義
2019      *  @param  string  $forward_name   遷移名
2020      */
2021     protected function _includeViewScript($forward_obj, $forward_name)
2022     {
2023         $view_dir = $this->getViewdir();
2024
2025         // view_path属性チェック
2026         if (isset($forward_obj['view_path'])) {
2027             // フルパス指定サポート
2028             $tmp_path = $forward_obj['view_path'];
2029             if (Ethna_Util::isAbsolute($tmp_path) == false) {
2030                 $tmp_path = $view_dir . $tmp_path;
2031             }
2032
2033             if (file_exists($tmp_path) == false) {
2034                 $this->logger->log(LOG_WARNING, 'view_path file not found [%s] -> try default', $tmp_path);
2035             } else {
2036                 include_once $tmp_path;
2037                 return;
2038             }
2039         }
2040
2041         // デフォルトチェック
2042         $view_path = $this->getDefaultViewPath($forward_name);
2043         if (file_exists($view_dir . $view_path)) {
2044             include_once $view_dir . $view_path;
2045             return;
2046         } else {
2047             $this->logger->log(LOG_DEBUG, 'default view file not found [%s]', $view_path);
2048             $view_path = null;
2049         }
2050     }
2051
2052     /**
2053      *  ディレクトリ以下の全てのスクリプトをインクルードする
2054      *
2055      *  @access private
2056      */
2057     protected function _includeDirectory($dir)
2058     {
2059         $ext = "." . $this->ext['php'];
2060         $ext_len = strlen($ext);
2061
2062         if (is_dir($dir) == false) {
2063             return;
2064         }
2065
2066         $dh = opendir($dir);
2067         if ($dh) {
2068             while (($file = readdir($dh)) !== false) {
2069                 if ($file != '.' && $file != '..' && is_dir("$dir/$file")) {
2070                     $this->_includeDirectory("$dir/$file");
2071                 }
2072                 if (substr($file, -$ext_len, $ext_len) != $ext) {
2073                     continue;
2074                 }
2075                 include_once $dir . '/' . $file;
2076             }
2077         }
2078         closedir($dh);
2079     }
2080
2081     /**
2082      *  設定ファイルのDSN定義から使用するデータを再構築する(スレーブアクセス分岐等)
2083      *
2084      *  DSNの定義方法(デフォルト:設定ファイル)を変えたい場合はここをオーバーライドする
2085      *
2086      *  @access protected
2087      *  @return array   DSN定義(array('DBキー1' => 'dsn1', 'DBキー2' => 'dsn2', ...))
2088      */
2089     protected function _prepareDSN()
2090     {
2091         $r = array();
2092
2093         foreach ($this->db as $key => $value) {
2094             $config_key = "dsn";
2095             if ($key != "") {
2096                 $config_key .= "_$key";
2097             }
2098             $dsn = $this->config->get($config_key);
2099             if (is_array($dsn)) {
2100                 // 種別1つにつき複数DSNが定義されている場合はアクセス分岐
2101                 $dsn = $this->_selectDSN($key, $dsn);
2102             }
2103             $r[$key] = $dsn;
2104         }
2105         return $r;
2106     }
2107
2108     /**
2109      *  DSNのアクセス分岐を行う
2110      *
2111      *  スレーブサーバへの振分け処理(デフォルト:ランダム)を変更したい場合はこのメソッドをオーバーライドする
2112      *
2113      *  @access protected
2114      *  @param  string  $type       DB種別
2115      *  @param  array   $dsn_list   DSN一覧
2116      *  @return string  選択されたDSN
2117      */
2118     protected function _selectDSN($type, $dsn_list)
2119     {
2120         if (is_array($dsn_list) == false) {
2121             return $dsn_list;
2122         }
2123
2124         // デフォルト:ランダム
2125         list($usec, $sec) = explode(' ', microtime());
2126         mt_srand($sec + ((float) $usec * 100000));
2127         $n = mt_rand(0, count($dsn_list)-1);
2128
2129         return $dsn_list[$n];
2130     }
2131
2132     /**
2133      *  Ethnaマネージャを設定する
2134      *
2135      *  不要な場合は空のメソッドとしてオーバーライドしてもよい
2136      *
2137      *  @access protected
2138      */
2139     protected function _activateEthnaManager()
2140     {
2141         if ($this->config->get('debug') == false) {
2142             return;
2143         }
2144
2145         require_once ETHNA_BASE . '/class/InfoManager.php';
2146
2147         // see if we have simpletest
2148         if (file_exists_ex('simpletest/unit_tester.php', true)) {
2149             require_once ETHNA_BASE . '/class/UnitTestManager.php';
2150         }
2151
2152         // action設定
2153         $this->action['__ethna_info__'] = array(
2154             'form_name' =>  'Ethna_Form_Info',
2155             'form_path' =>  sprintf('%s/class/Action/Info.php', ETHNA_BASE),
2156             'class_name' => 'Ethna_Action_Info',
2157             'class_path' => sprintf('%s/class/Action/Info.php', ETHNA_BASE),
2158         );
2159
2160         // forward設定
2161         $this->forward['__ethna_info__'] = array(
2162             'forward_path'  => sprintf('%s/tpl/info.tpl', ETHNA_BASE),
2163             'view_name'     => 'Ethna_View_Info',
2164             'view_path'     => sprintf('%s/class/View/Info.php', ETHNA_BASE),
2165         );
2166
2167
2168         // action設定
2169         $this->action['__ethna_unittest__'] = array(
2170             'form_name' =>  'Ethna_Form_UnitTest',
2171             'form_path' =>  sprintf('%s/class/Action/UnitTest.php', ETHNA_BASE),
2172             'class_name' => 'Ethna_Action_UnitTest',
2173             'class_path' => sprintf('%s/class/Action/UnitTest.php', ETHNA_BASE),
2174         );
2175
2176         // forward設定
2177         $this->forward['__ethna_unittest__'] = array(
2178             'forward_path'  => sprintf('%s/tpl/unittest.tpl', ETHNA_BASE),
2179             'view_name'     => 'Ethna_View_UnitTest',
2180             'view_path'     => sprintf('%s/class/View/UnitTest.php', ETHNA_BASE),
2181         );
2182
2183     }
2184
2185     /**
2186      *  Ethnaマネージャが実行可能かをチェックする
2187      *
2188      *  Ethnaマネージャを実行するよう指示されているにも関わらず、
2189      *  debug が trueでない場合は実行を停止する。
2190      *
2191      *  @access private
2192      */
2193     protected function _ethnaManagerEnabledCheck($action_name)
2194     {
2195         if ($this->config->get('debug') == false
2196             && ($action_name == '__ethna_info__' || $action_name == '__ethna_unittest__'))
2197         {
2198             $this->ethnaManagerCheckErrorMsg($action_name);
2199             exit(0);
2200         }
2201     }
2202
2203     /**
2204      *  Ethnaマネージャが実行不能な場合のエラーメッセージを
2205      *  表示する。運用上の都合でこのメッセージを出力したくない
2206      *  場合は、このメソッドをオーバーライドせよ
2207      *
2208      *  @access protected
2209      */
2210      protected function ethnaManagerCheckErrorMsg($action_name)
2211      {
2212          $appid = strtolower($this->getAppId());
2213          $run_action = ($action_name == '__ethna_info__')
2214                      ? ' show Application Info List '
2215                      : ' run Unit Test ';
2216          echo "Ethna cannot {$run_action} under your application setting.<br>";
2217          echo "HINT: You must set {$appid}/etc/{$appid}-ini.php debug setting 'true'.<br>";
2218          echo "<br>";
2219          echo "In {$appid}-ini.php, please set as follows :<br><br>";
2220          echo "\$config = array ( 'debug' => true, );";
2221      }
2222
2223     /**
2224      *  CLI実行中フラグを取得する
2225      *
2226      *  @access public
2227      *  @return bool    CLI実行中フラグ
2228      *  @obsolete
2229      */
2230     public function getCLI()
2231     {
2232         return $this->gateway == GATEWAY_CLI ? true : false;
2233     }
2234
2235     /**
2236      *  CLI実行中フラグを設定する
2237      *
2238      *  @access public
2239      *  @param  bool    CLI実行中フラグ
2240      *  @obsolete
2241      */
2242     public function setCLI($cli)
2243     {
2244         $this->gateway = $cli ? GATEWAY_CLI : $this->_getDefaultGateway();
2245     }
2246 }
2247 // }}}
2248
2249 /**
2250  *  XMLRPCゲートウェイのスタブクラス
2251  *
2252  *  @access     public
2253  */
2254 function _Ethna_XmlrpcGateway($method_stub, $param)
2255 {
2256     $ctl = Ethna_Controller::getInstance();
2257     $method = $ctl->getXmlrpcMethodName();
2258     $r = $ctl->trigger_XMLRPC($method, $param);
2259     if (Ethna::isError($r)) {
2260         return array(
2261             'faultCode' => $r->getCode(),
2262             'faultString' => $r->getMessage(),
2263         );
2264     }
2265     return $r;
2266 }