OSDN Git Service

fixed: references
[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     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     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     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     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     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     function getURL()
427     {
428         return $this->url;
429     }
430
431     /**
432      *  アプリケーションベースディレクトリを返す
433      *
434      *  @access public
435      *  @return string  アプリケーションベースディレクトリ
436      */
437     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     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     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     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     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     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     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     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     function getClassFactory()
561     {
562         return $this->class_factory;
563     }
564
565     /**
566      *  アクションエラーオブジェクトのアクセサ
567      *
568      *  @access public
569      *  @return object  Ethna_ActionError   アクションエラーオブジェクト
570      */
571     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     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     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     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     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     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     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     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     function getManagerList()
730     {
731         return $this->manager;
732     }
733
734     /**
735      *  実行中のアクション名を返す
736      *
737      *  @access public
738      *  @return string  実行中のアクション名
739      */
740     function getCurrentActionName()
741     {
742         return $this->action_name;
743     }
744
745     /**
746      *  実行中のXMLRPCメソッド名を返す
747      *
748      *  @access public
749      *  @return string  実行中のXMLRPCメソッド名
750      */
751     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     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     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     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     function getClientEncoding()
804     {
805         return $this->client_encoding;
806     }
807
808     /**
809      *  クライアントエンコーディング名へのアクセサ(W)
810      *
811      *  @access public
812      *  @param  string  $client_encoding クライアントエンコーディング名
813      */
814     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     function getGateway()
827     {
828         return $this->gateway;
829     }
830
831     /**
832      *  ゲートウェイモードを設定する
833      *
834      *  @access public
835      */
836     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     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     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     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     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     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     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     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     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     function _sortForward($action_name, $retval)
1425     {
1426         return $retval;
1427     }
1428
1429     /**
1430      *  フィルタチェインを生成する
1431      *
1432      *  @access private
1433      */
1434     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     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     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     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     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     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     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     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     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     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     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     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     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     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     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     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         $_ret_object = $this->getTemplateEngine();
1774         return $_ret_object;
1775     }
1776
1777     /**
1778      *  テンプレートエンジン取得する
1779      *
1780      *  @access public
1781      *  @return object  Ethna_Renderer  レンダラオブジェクト
1782      *  @obsolete
1783      */
1784     public function getTemplateEngine()
1785     {
1786         if (is_object($this->renderer)) {
1787             return $this->renderer;
1788         }
1789
1790         $this->renderer = $this->class_factory->getObject('renderer');
1791
1792         //テンプレートエンジンのデフォルトの設定
1793         $this->_setDefaultTemplateEngine($this->renderer);
1794         // }}}
1795
1796         return $this->renderer;
1797     }
1798
1799     /**
1800      *  テンプレートエンジンのデフォルト状態を設定する
1801      *
1802      *  @access protected
1803      *  @param  object  Ethna_Renderer  レンダラオブジェクト
1804      *  @obsolete
1805      */
1806     protected function _setDefaultTemplateEngine($renderer)
1807     {
1808     }
1809
1810     /**
1811      *  使用言語、ロケールを設定する
1812      *  条件によって使用言語、ロケールを切り替えたい場合は、
1813      *  このメソッドをオーバーライドする。
1814      *
1815      *  @access protected
1816      *  @param  string  $locale             ロケール名(ja_JP, en_US等)
1817      *                                      (ll_cc の形式。ll = 言語コード cc = 国コード)
1818      *  @param  string  $system_encoding    システムエンコーディング名
1819      *  @param  string  $client_encoding    クライアントエンコーディング(テンプレートのエンコーディングと考えれば良い)
1820      *  @see    http://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html
1821      *  @see    Ethna_Controller#_getDefaultLanguage
1822      */
1823     protected function _setLanguage($locale, $system_encoding = null, $client_encoding = null)
1824     {
1825         $this->locale = $locale;
1826         $this->system_encoding = $system_encoding;
1827         $this->client_encoding = $client_encoding;
1828
1829         //   $this->locale, $this->client_encoding を書き換えた場合は
1830         //   必ず Ethna_I18N クラスの setLanguageメソッドも呼ぶこと!
1831         //   さもないとカタログその他が再ロードされない!
1832         $i18n = $this->getI18N();
1833         $i18n->setLanguage($locale, $system_encoding, $client_encoding);
1834     }
1835
1836     /**
1837      *  デフォルト状態での使用言語を取得する
1838      *  外部に出力されるEthnaのエラーメッセージ等のエンコーディングを
1839      *  切り替えたい場合は、このメソッドをオーバーライドする。
1840      *
1841      *  @access protected
1842      *  @return array   ロケール名(e.x ja_JP, en_US 等),
1843      *                  システムエンコーディング名,
1844      *                  クライアントエンコーディング名
1845      *                  (= テンプレートのエンコーディングと考えてよい) の配列
1846      *                  (ロケール名は ll_cc の形式。ll = 言語コード cc = 国コード)
1847      *
1848      *  WARNING!! : クライアントエンコーディング名が、フレームワークの内部エンコーデ
1849      *              ィングとして設定されます。つまり、クライアントエンコーディングで
1850      *              ブラウザからの入力は入ってくるものと想定しています!
1851      */
1852     protected function _getDefaultLanguage()
1853     {
1854         return array('ja_JP', 'UTF-8', 'UTF-8');
1855     }
1856
1857     /**
1858      *  デフォルト状態でのゲートウェイを取得する
1859      *
1860      *  @access protected
1861      *  @return int     ゲートウェイ定義(GATEWAY_WWW, GATEWAY_CLI...)
1862      */
1863     protected function _getDefaultGateway($gateway)
1864     {
1865         if (is_null($GLOBALS['_Ethna_gateway']) == false) {
1866             return $GLOBALS['_Ethna_gateway'];
1867         }
1868         return GATEWAY_WWW;
1869     }
1870
1871     /**
1872      *  ゲートウェイに対応したクラス名のプレフィクスを取得する
1873      *
1874      *  @access public
1875      *  @param  string  $gateway    ゲートウェイ
1876      *  @return string  ゲートウェイクラスプレフィクス
1877      */
1878     protected function _getGatewayPrefix($gateway = null)
1879     {
1880         $gateway = is_null($gateway) ? $this->getGateway() : $gateway;
1881         switch ($gateway) {
1882         case GATEWAY_WWW:
1883             $prefix = '';
1884             break;
1885         case GATEWAY_CLI:
1886             $prefix = 'Cli';
1887             break;
1888         case GATEWAY_XMLRPC:
1889             $prefix = 'Xmlrpc';
1890             break;
1891         default:
1892             $prefix = '';
1893             break;
1894         }
1895
1896         return $prefix;
1897     }
1898
1899     /**
1900      *  マネージャクラス名を取得する
1901      *
1902      *  @access public
1903      *  @param  string  $name   マネージャキー
1904      *  @return string  マネージャクラス名
1905      */
1906     public function getManagerClassName($name)
1907     {
1908         //   アプリケーションIDと、渡された名前のはじめを大文字にして、
1909         //   組み合わせたものが返される
1910         $manager_id = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($name));
1911         return sprintf('%s_%sManager', $this->getAppId(), ucfirst($manager_id));
1912     }
1913
1914     /**
1915      *  アプリケーションオブジェクトクラス名を取得する
1916      *
1917      *  @access public
1918      *  @param  string  $name   アプリケーションオブジェクトキー
1919      *  @return string  マネージャクラス名
1920      */
1921     public function getObjectClassName($name)
1922     {
1923         //  引数のはじめの一文字目と、アンダーバー直後の
1924         //  1文字を必ず大文字にする。アンダーバーは削除される。
1925         $name = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($name));
1926
1927         //  $name に foo_bar を渡し、AppID が Hogeの場合
1928         //  [Appid]_FooBar が返される
1929         return sprintf('%s_%s', $this->getAppId(), $name);
1930     }
1931
1932     /**
1933      *  アクションスクリプトをインクルードする
1934      *
1935      *  ただし、インクルードしたファイルにクラスが正しく定義されているかどうかは保証しない
1936      *
1937      *  @access private
1938      *  @param  array   $action_obj     アクション定義
1939      *  @param  string  $action_name    アクション名
1940      */
1941     protected function _includeActionScript($action_obj, $action_name)
1942     {
1943         $class_path = $form_path = null;
1944
1945         $action_dir = $this->getActiondir();
1946
1947         // class_path属性チェック
1948         if (isset($action_obj['class_path'])) {
1949             // フルパス指定サポート
1950             $tmp_path = $action_obj['class_path'];
1951             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1952                 $tmp_path = $action_dir . $tmp_path;
1953             }
1954
1955             if (file_exists($tmp_path) == false) {
1956                 $this->logger->log(LOG_WARNING, 'class_path file not found [%s] -> try default', $tmp_path);
1957             } else {
1958                 include_once $tmp_path;
1959                 $class_path = $tmp_path;
1960             }
1961         }
1962
1963         // デフォルトチェック
1964         if (is_null($class_path)) {
1965             $class_path = $this->getDefaultActionPath($action_name);
1966             if (file_exists($action_dir . $class_path)) {
1967                 include_once $action_dir . $class_path;
1968             } else {
1969                 $this->logger->log(LOG_DEBUG, 'default action file not found [%s] -> try all files', $class_path);
1970                 return;
1971             }
1972         }
1973
1974         // form_path属性チェック
1975         if (isset($action_obj['form_path'])) {
1976             // フルパス指定サポート
1977             $tmp_path = $action_obj['form_path'];
1978             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1979                 $tmp_path = $action_dir . $tmp_path;
1980             }
1981
1982             if ($tmp_path == $class_path) {
1983                 return;
1984             }
1985             if (file_exists($tmp_path) == false) {
1986                 $this->logger->log(LOG_WARNING, 'form_path file not found [%s] -> try default', $tmp_path);
1987             } else {
1988                 include_once $tmp_path;
1989                 $form_path = $tmp_path;
1990             }
1991         }
1992
1993         // デフォルトチェック
1994         if (is_null($form_path)) {
1995             $form_path = $this->getDefaultFormPath($action_name);
1996             if ($form_path == $class_path) {
1997                 return;
1998             }
1999             if (file_exists($action_dir . $form_path)) {
2000                 include_once $action_dir . $form_path;
2001             } else {
2002                 $this->logger->log(LOG_DEBUG, 'default form file not found [%s] -> maybe falling back to default form class', $form_path);
2003             }
2004         }
2005     }
2006
2007     /**
2008      *  ビュースクリプトをインクルードする
2009      *
2010      *  ただし、インクルードしたファイルにクラスが正しく定義されているかどうかは保証しない
2011      *
2012      *  @access private
2013      *  @param  array   $forward_obj    遷移定義
2014      *  @param  string  $forward_name   遷移名
2015      */
2016     protected function _includeViewScript($forward_obj, $forward_name)
2017     {
2018         $view_dir = $this->getViewdir();
2019
2020         // view_path属性チェック
2021         if (isset($forward_obj['view_path'])) {
2022             // フルパス指定サポート
2023             $tmp_path = $forward_obj['view_path'];
2024             if (Ethna_Util::isAbsolute($tmp_path) == false) {
2025                 $tmp_path = $view_dir . $tmp_path;
2026             }
2027
2028             if (file_exists($tmp_path) == false) {
2029                 $this->logger->log(LOG_WARNING, 'view_path file not found [%s] -> try default', $tmp_path);
2030             } else {
2031                 include_once $tmp_path;
2032                 return;
2033             }
2034         }
2035
2036         // デフォルトチェック
2037         $view_path = $this->getDefaultViewPath($forward_name);
2038         if (file_exists($view_dir . $view_path)) {
2039             include_once $view_dir . $view_path;
2040             return;
2041         } else {
2042             $this->logger->log(LOG_DEBUG, 'default view file not found [%s]', $view_path);
2043             $view_path = null;
2044         }
2045     }
2046
2047     /**
2048      *  ディレクトリ以下の全てのスクリプトをインクルードする
2049      *
2050      *  @access private
2051      */
2052     protected function _includeDirectory($dir)
2053     {
2054         $ext = "." . $this->ext['php'];
2055         $ext_len = strlen($ext);
2056
2057         if (is_dir($dir) == false) {
2058             return;
2059         }
2060
2061         $dh = opendir($dir);
2062         if ($dh) {
2063             while (($file = readdir($dh)) !== false) {
2064                 if ($file != '.' && $file != '..' && is_dir("$dir/$file")) {
2065                     $this->_includeDirectory("$dir/$file");
2066                 }
2067                 if (substr($file, -$ext_len, $ext_len) != $ext) {
2068                     continue;
2069                 }
2070                 include_once $dir . '/' . $file;
2071             }
2072         }
2073         closedir($dh);
2074     }
2075
2076     /**
2077      *  設定ファイルのDSN定義から使用するデータを再構築する(スレーブアクセス分岐等)
2078      *
2079      *  DSNの定義方法(デフォルト:設定ファイル)を変えたい場合はここをオーバーライドする
2080      *
2081      *  @access protected
2082      *  @return array   DSN定義(array('DBキー1' => 'dsn1', 'DBキー2' => 'dsn2', ...))
2083      */
2084     protected function _prepareDSN()
2085     {
2086         $r = array();
2087
2088         foreach ($this->db as $key => $value) {
2089             $config_key = "dsn";
2090             if ($key != "") {
2091                 $config_key .= "_$key";
2092             }
2093             $dsn = $this->config->get($config_key);
2094             if (is_array($dsn)) {
2095                 // 種別1つにつき複数DSNが定義されている場合はアクセス分岐
2096                 $dsn = $this->_selectDSN($key, $dsn);
2097             }
2098             $r[$key] = $dsn;
2099         }
2100         return $r;
2101     }
2102
2103     /**
2104      *  DSNのアクセス分岐を行う
2105      *
2106      *  スレーブサーバへの振分け処理(デフォルト:ランダム)を変更したい場合はこのメソッドをオーバーライドする
2107      *
2108      *  @access protected
2109      *  @param  string  $type       DB種別
2110      *  @param  array   $dsn_list   DSN一覧
2111      *  @return string  選択されたDSN
2112      */
2113     protected function _selectDSN($type, $dsn_list)
2114     {
2115         if (is_array($dsn_list) == false) {
2116             return $dsn_list;
2117         }
2118
2119         // デフォルト:ランダム
2120         list($usec, $sec) = explode(' ', microtime());
2121         mt_srand($sec + ((float) $usec * 100000));
2122         $n = mt_rand(0, count($dsn_list)-1);
2123
2124         return $dsn_list[$n];
2125     }
2126
2127     /**
2128      *  Ethnaマネージャを設定する
2129      *
2130      *  不要な場合は空のメソッドとしてオーバーライドしてもよい
2131      *
2132      *  @access protected
2133      */
2134     protected function _activateEthnaManager()
2135     {
2136         if ($this->config->get('debug') == false) {
2137             return;
2138         }
2139
2140         require_once ETHNA_BASE . '/class/InfoManager.php';
2141
2142         // see if we have simpletest
2143         if (file_exists_ex('simpletest/unit_tester.php', true)) {
2144             require_once ETHNA_BASE . '/class/UnitTestManager.php';
2145         }
2146
2147         // action設定
2148         $this->action['__ethna_info__'] = array(
2149             'form_name' =>  'Ethna_Form_Info',
2150             'form_path' =>  sprintf('%s/class/Action/Info.php', ETHNA_BASE),
2151             'class_name' => 'Ethna_Action_Info',
2152             'class_path' => sprintf('%s/class/Action/Info.php', ETHNA_BASE),
2153         );
2154
2155         // forward設定
2156         $this->forward['__ethna_info__'] = array(
2157             'forward_path'  => sprintf('%s/tpl/info.tpl', ETHNA_BASE),
2158             'view_name'     => 'Ethna_View_Info',
2159             'view_path'     => sprintf('%s/class/View/Info.php', ETHNA_BASE),
2160         );
2161
2162
2163         // action設定
2164         $this->action['__ethna_unittest__'] = array(
2165             'form_name' =>  'Ethna_Form_UnitTest',
2166             'form_path' =>  sprintf('%s/class/Action/UnitTest.php', ETHNA_BASE),
2167             'class_name' => 'Ethna_Action_UnitTest',
2168             'class_path' => sprintf('%s/class/Action/UnitTest.php', ETHNA_BASE),
2169         );
2170
2171         // forward設定
2172         $this->forward['__ethna_unittest__'] = array(
2173             'forward_path'  => sprintf('%s/tpl/unittest.tpl', ETHNA_BASE),
2174             'view_name'     => 'Ethna_View_UnitTest',
2175             'view_path'     => sprintf('%s/class/View/UnitTest.php', ETHNA_BASE),
2176         );
2177
2178     }
2179
2180     /**
2181      *  Ethnaマネージャが実行可能かをチェックする
2182      *
2183      *  Ethnaマネージャを実行するよう指示されているにも関わらず、
2184      *  debug が trueでない場合は実行を停止する。
2185      *
2186      *  @access private
2187      */
2188     protected function _ethnaManagerEnabledCheck($action_name)
2189     {
2190         if ($this->config->get('debug') == false
2191          && ($action_name == '__ethna_info__' || $action_name == '__ethna_unittest__')) {
2192             $this->ethnaManagerCheckErrorMsg($action_name);
2193             exit(0);
2194         }
2195     }
2196
2197     /**
2198      *  Ethnaマネージャが実行不能な場合のエラーメッセージを
2199      *  表示する。運用上の都合でこのメッセージを出力したくない
2200      *  場合は、このメソッドをオーバーライドせよ
2201      *
2202      *  @access protected
2203      */
2204      protected function ethnaManagerCheckErrorMsg($action_name)
2205      {
2206          $appid = strtolower($this->getAppId());
2207          $run_action = ($action_name == '__ethna_info__')
2208                      ? ' show Application Info List '
2209                      : ' run Unit Test ';
2210          echo "Ethna cannot {$run_action} under your application setting.<br>";
2211          echo "HINT: You must set {$appid}/etc/{$appid}-ini.php debug setting 'true'.<br>";
2212          echo "<br>";
2213          echo "In {$appid}-ini.php, please set as follows :<br><br>";
2214          echo "\$config = array ( 'debug' => true, );";
2215      }
2216
2217     /**
2218      *  CLI実行中フラグを取得する
2219      *
2220      *  @access public
2221      *  @return bool    CLI実行中フラグ
2222      *  @obsolete
2223      */
2224     public function getCLI()
2225     {
2226         return $this->gateway == GATEWAY_CLI ? true : false;
2227     }
2228
2229     /**
2230      *  CLI実行中フラグを設定する
2231      *
2232      *  @access public
2233      *  @param  bool    CLI実行中フラグ
2234      *  @obsolete
2235      */
2236     public function setCLI($cli)
2237     {
2238         $this->gateway = $cli ? GATEWAY_CLI : $this->_getDefaultGateway();
2239     }
2240 }
2241 // }}}
2242
2243 /**
2244  *  XMLRPCゲートウェイのスタブクラス
2245  *
2246  *  @access     public
2247  */
2248 function _Ethna_XmlrpcGateway($method_stub, $param)
2249 {
2250     $ctl = Ethna_Controller::getInstance();
2251     $method = $ctl->getXmlrpcMethodName();
2252     $r = $ctl->trigger_XMLRPC($method, $param);
2253     if (Ethna::isError($r)) {
2254         return array(
2255             'faultCode' => $r->getCode(),
2256             'faultString' => $r->getMessage(),
2257         );
2258     }
2259     return $r;
2260 }