OSDN Git Service

fixed: modifier
[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     protected $class = array();
77
78     /** @protected    array       クラス設定(デフォルト) */
79     protected $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      *
530      *  @access public
531      *  @param  string  $key    拡張子タイプ("php", "tpl"...)
532      *  @return string  $keyに対応した拡張子(設定が無い場合はnull)
533      */
534     function getExt($key)
535     {
536         if (isset($this->ext[$key]) == false) {
537             return null;
538         }
539         return $this->ext[$key];
540     }
541
542     /**
543      *  クラスファクトリオブジェクトのアクセサ(R)
544      *
545      *  @access public
546      *  @return object  Ethna_ClassFactory  クラスファクトリオブジェクト
547      */
548     function getClassFactory()
549     {
550         return $this->class_factory;
551     }
552
553     /**
554      *  アクションエラーオブジェクトのアクセサ
555      *
556      *  @access public
557      *  @return object  Ethna_ActionError   アクションエラーオブジェクト
558      */
559     function getActionError()
560     {
561         return $this->class_factory->getObject('error');
562     }
563
564     /**
565      *  アクションフォームオブジェクトのアクセサ
566      *
567      *  @access public
568      *  @return object  Ethna_ActionForm    アクションフォームオブジェクト
569      */
570     function getActionForm()
571     {
572         // 明示的にクラスファクトリを利用していない
573         return $this->action_form;
574     }
575
576     /**
577      *  ビューオブジェクトのアクセサ
578      *
579      *  @access public
580      *  @return object  Ethna_View          ビューオブジェクト
581      */
582     function getView()
583     {
584         // 明示的にクラスファクトリを利用していない
585         return $this->view;
586     }
587
588     /**
589      *  backendオブジェクトのアクセサ
590      *
591      *  @access public
592      *  @return object  Ethna_Backend   backendオブジェクト
593      */
594     function getBackend()
595     {
596         return $this->class_factory->getObject('backend');
597     }
598
599     /**
600      *  設定オブジェクトのアクセサ
601      *
602      *  @access public
603      *  @return object  Ethna_Config    設定オブジェクト
604      */
605     function getConfig()
606     {
607         return $this->class_factory->getObject('config');
608     }
609
610     /**
611      *  i18nオブジェクトのアクセサ(R)
612      *
613      *  @access public
614      *  @return object  Ethna_I18N  i18nオブジェクト
615      */
616     function getI18N()
617     {
618         return $this->class_factory->getObject('i18n');
619     }
620
621     /**
622      *  ログオブジェクトのアクセサ
623      *
624      *  @access public
625      *  @return object  Ethna_Logger        ログオブジェクト
626      */
627     function getLogger()
628     {
629         return $this->class_factory->getObject('logger');
630     }
631
632     /**
633      *  セッションオブジェクトのアクセサ
634      *
635      *  @access public
636      *  @return object  Ethna_Session       セッションオブジェクト
637      */
638     function getSession()
639     {
640         return $this->class_factory->getObject('session');
641     }
642
643     /**
644      *  SQLオブジェクトのアクセサ
645      *
646      *  @access public
647      *  @return object  Ethna_AppSQL    SQLオブジェクト
648      */
649     function getSQL()
650     {
651         return $this->class_factory->getObject('sql');
652     }
653
654     /**
655      *  プラグインオブジェクトのアクセサ
656      *
657      *  @access public
658      *  @return object  Ethna_Plugin    プラグインオブジェクト
659      */
660     function getPlugin()
661     {
662         return $this->class_factory->getObject('plugin');
663     }
664
665     /**
666      *  URLハンドラオブジェクトのアクセサ
667      *
668      *  @access public
669      *  @return object  Ethna_UrlHandler    URLハンドラオブジェクト
670      */
671     function getUrlHandler()
672     {
673         return $this->class_factory->getObject('url_handler');
674     }
675
676     /**
677      *  マネージャ一覧を返す
678      *
679      *  @access public
680      *  @return array   マネージャ一覧
681      *  @obsolete
682      */
683     function getManagerList()
684     {
685         return $this->manager;
686     }
687
688     /**
689      *  実行中のアクション名を返す
690      *
691      *  @access public
692      *  @return string  実行中のアクション名
693      */
694     function getCurrentActionName()
695     {
696         return $this->action_name;
697     }
698
699     /**
700      *  実行中のXMLRPCメソッド名を返す
701      *
702      *  @access public
703      *  @return string  実行中のXMLRPCメソッド名
704      */
705     function getXmlrpcMethodName()
706     {
707         return $this->xmlrpc_method_name;
708     }
709
710     /**
711      *  ロケール設定、使用言語を取得する
712      *
713      *  @access public
714      *  @return array   ロケール名(e.x ja_JP, en_US 等),
715      *                  システムエンコーディング名,
716      *                  クライアントエンコーディング名 の配列
717      *                  (ロケール名は、ll_cc の形式。ll = 言語コード cc = 国コード)
718      *  @see http://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html
719      */
720     function getLanguage()
721     {
722         return array($this->locale, $this->system_encoding, $this->client_encoding);
723     }
724
725     /**
726      *  ロケール名へのアクセサ(R)
727      *
728      *  @access public
729      *  @return string  ロケール名(e.x ja_JP, en_US 等),
730      *                  (ロケール名は、ll_cc の形式。ll = 言語コード cc = 国コード)
731      */
732     function getLocale()
733     {
734         return $this->locale;
735     }
736
737     /**
738      *  ロケール名へのアクセサ(W)
739      *
740      *  @access public
741      *  @param $locale ロケール名(e.x ja_JP, en_US 等),
742      *                 (ロケール名は、ll_cc の形式。ll = 言語コード cc = 国コード)
743      */
744     function setLocale($locale)
745     {
746         $this->locale = $locale;
747         $i18n = $this->getI18N();
748         $i18n->setLanguage($this->locale, $this->system_encoding, $this->client_encoding);
749     }
750
751     /**
752      *  クライアントエンコーディング名へのアクセサ(R)
753      *
754      *  @access public
755      *  @return string  $client_encoding クライアントエンコーディング名
756      */
757     function getClientEncoding()
758     {
759         return $this->client_encoding;
760     }
761
762     /**
763      *  クライアントエンコーディング名へのアクセサ(W)
764      *
765      *  @access public
766      *  @param  string  $client_encoding クライアントエンコーディング名
767      */
768     function setClientEncoding($client_encoding)
769     {
770         $this->client_encoding = $client_encoding;
771         $i18n = $this->getI18N();
772         $i18n->setLanguage($this->locale, $this->system_encoding, $this->client_encoding);
773     }
774
775     /**
776      *  ゲートウェイを取得する
777      *
778      *  @access public
779      */
780     function getGateway()
781     {
782         return $this->gateway;
783     }
784
785     /**
786      *  ゲートウェイモードを設定する
787      *
788      *  @access public
789      */
790     function setGateway($gateway)
791     {
792         $this->gateway = $gateway;
793     }
794
795     /**
796      *  アプリケーションのエントリポイント
797      *
798      *  @access public
799      *  @param  string  $class_name     アプリケーションコントローラのクラス名
800      *  @param  mixed   $action_name    指定のアクション名(省略可)
801      *  @param  mixed   $fallback_action_name   アクションが決定できなかった場合に実行されるアクション名(省略可)
802      *  @static
803      */
804     public static function main($class_name, $action_name = "", $fallback_action_name = "")
805     {
806         $c = new $class_name;
807         $c->trigger($action_name, $fallback_action_name);
808         $c->end();
809     }
810
811     /**
812      *  CLIアプリケーションのエントリポイント
813      *
814      *  @access public
815      *  @param  string  $class_name     アプリケーションコントローラのクラス名
816      *  @param  string  $action_name    実行するアクション名
817      *  @param  bool    $enable_filter  フィルタチェインを有効にするかどうか
818      *  @static
819      */
820     public static function main_CLI($class_name, $action_name, $enable_filter = true)
821     {
822         $c = new $class_name(GATEWAY_CLI);
823         $c->action_cli[$action_name] = array();
824         $c->trigger($action_name, "", $enable_filter);
825         $c->end();
826     }
827
828     /**
829      *  XMLRPCアプリケーションのエントリポイント
830      *
831      *  @access public
832      *  @static
833      */
834     public static function main_XMLRPC($class_name)
835     {
836         if (extension_loaded('xmlrpc') == false) {
837             die("xmlrpc extension is required to enable this gateway");
838         }
839
840         $c = new $class_name(GATEWAY_XMLRPC);
841         $c->trigger("", "", false);
842         $c->end();
843     }
844
845     /**
846      *  SOAPアプリケーションのエントリポイント
847      *
848      *  @access public
849      *  @param  string  $class_name     アプリケーションコントローラのクラス名
850      *  @param  mixed   $action_name    指定のアクション名(省略可)
851      *  @param  mixed   $fallback_action_name   アクションが決定できなかった場合に実行されるアクション名(省略可)
852      *  @static
853      */
854     public static function main_SOAP($class_name, $action_name = "", $fallback_action_name = "")
855     {
856         $c = new $class_name(GATEWAY_SOAP);
857         $c->trigger($action_name, $fallback_action_name);
858         $c->end();
859     }
860
861     /**
862      *  フレームワークの処理を開始する
863      *
864      *  @access public
865      *  @param  mixed   $default_action_name    指定のアクション名
866      *  @param  mixed   $fallback_action_name   アクション名が決定できなかった場合に実行されるアクション名
867      *  @param  bool    $enable_filter  フィルタチェインを有効にするかどうか
868      *  @return mixed   0:正常終了 Ethna_Error:エラー
869      */
870     function trigger($default_action_name = "", $fallback_action_name = "", $enable_filter = true)
871     {
872         // フィルターの生成
873         if ($enable_filter) {
874             $this->_createFilterChain();
875         }
876
877         // 実行前フィルタ
878         for ($i = 0; $i < count($this->filter_chain); $i++) {
879             $r = $this->filter_chain[$i]->preFilter();
880             if (Ethna::isError($r)) {
881                 return $r;
882             }
883         }
884
885         // trigger
886         switch ($this->getGateway()) {
887         case GATEWAY_WWW:
888             $this->_trigger_WWW($default_action_name, $fallback_action_name);
889             break;
890         case GATEWAY_CLI:
891             $this->_trigger_CLI($default_action_name);
892             break;
893         case GATEWAY_XMLRPC:
894             $this->_trigger_XMLRPC();
895             break;
896         case GATEWAY_SOAP:
897             $this->_trigger_SOAP();
898             break;
899         }
900
901         // 実行後フィルタ
902         for ($i = count($this->filter_chain) - 1; $i >= 0; $i--) {
903             $r = $this->filter_chain[$i]->postFilter();
904             if (Ethna::isError($r)) {
905                 return $r;
906             }
907         }
908     }
909
910     /**
911      *  フレームワークの処理を実行する(WWW)
912      *
913      *  引数$default_action_nameに配列が指定された場合、その配列で指定された
914      *  アクション以外は受け付けない(指定されていないアクションが指定された
915      *  場合、配列の先頭で指定されたアクションが実行される)
916      *
917      *  @access private
918      *  @param  mixed   $default_action_name    指定のアクション名
919      *  @param  mixed   $fallback_action_name   アクション名が決定できなかった場合に実行されるアクション名
920      *  @return mixed   0:正常終了 Ethna_Error:エラー
921      */
922     function _trigger_WWW($default_action_name = "", $fallback_action_name = "")
923     {
924         // アクション名の取得
925         $action_name = $this->_getActionName($default_action_name, $fallback_action_name);
926
927         // マネージャ実行チェック
928         $this->_ethnaManagerEnabledCheck($action_name);
929
930         // アクション定義の取得
931         $action_obj = $this->_getAction($action_name);
932         if (is_null($action_obj)) {
933             if ($fallback_action_name != "") {
934                 $this->logger->log(LOG_DEBUG, 'undefined action [%s] -> try fallback action [%s]', $action_name, $fallback_action_name);
935                 $action_obj = $this->_getAction($fallback_action_name);
936             }
937             if (is_null($action_obj)) {
938                 return Ethna::raiseError("undefined action [%s]", E_APP_UNDEFINED_ACTION, $action_name);
939             } else {
940                 $action_name = $fallback_action_name;
941             }
942         }
943
944         // アクション実行前フィルタ
945         for ($i = 0; $i < count($this->filter_chain); $i++) {
946             $r = $this->filter_chain[$i]->preActionFilter($action_name);
947             if ($r != null) {
948                 $this->logger->log(LOG_DEBUG, 'action [%s] -> [%s] by %s', $action_name, $r, get_class($this->filter_chain[$i]));
949                 $action_name = $r;
950             }
951         }
952         $this->action_name = $action_name;
953
954         // オブジェクト生成
955         $backend = $this->getBackend();
956         $session = $this->getSession();
957         $session->restore();
958
959         // 言語切り替えフックを呼ぶ
960         $this->_setLanguage($this->locale, $this->system_encoding, $this->client_encoding);
961
962         // アクションフォーム初期化
963         // フォーム定義、フォーム値設定
964         $form_name = $this->getActionFormName($action_name);
965         $this->action_form = new $form_name($this);
966         $this->action_form->setFormDef_PreHelper();
967         $this->action_form->setFormVars();
968         $backend->setActionForm($this->action_form);
969
970         // バックエンド処理実行
971         $forward_name = $backend->perform($action_name);
972
973         // アクション実行後フィルタ
974         for ($i = count($this->filter_chain) - 1; $i >= 0; $i--) {
975             $r = $this->filter_chain[$i]->postActionFilter($action_name, $forward_name);
976             if ($r != null) {
977                 $this->logger->log(LOG_DEBUG, 'forward [%s] -> [%s] by %s', $forward_name, $r, get_class($this->filter_chain[$i]));
978                 $forward_name = $r;
979             }
980         }
981
982         // コントローラで遷移先を決定する(オプション)
983         $forward_name_params = $this->_sortForward($action_name, $forward_name);
984
985         // Viewへの引数があれば取り出す
986         $preforward_params = array();
987         if (is_array($forward_name_params)) {
988             $forward_name = array_shift($forward_name_params);
989             $preforward_params = $forward_name_params;
990         }
991         else {
992             $forward_name = $forward_name_params;
993         }
994
995         if ($forward_name != null) {
996             $view_class_name = $this->getViewClassName($forward_name);
997             $this->view = new $view_class_name($backend, $forward_name, $this->_getForwardPath($forward_name));
998             call_user_func_array(array($this->view, 'preforward'), $preforward_params);
999             $this->view->forward();
1000         }
1001
1002         return 0;
1003     }
1004
1005     /**
1006      *  フレームワークの処理を実行する(CLI)
1007      *
1008      *  @access private
1009      *  @param  mixed   $default_action_name    指定のアクション名
1010      *  @return mixed   0:正常終了 Ethna_Error:エラー
1011      */
1012     function _trigger_CLI($default_action_name = "")
1013     {
1014         return $this->_trigger_WWW($default_action_name);
1015     }
1016
1017     /**
1018      *  フレームワークの処理を実行する(XMLRPC)
1019      *
1020      *  @access private
1021      *  @param  mixed   $action_name    指定のアクション名
1022      *  @return mixed   0:正常終了 Ethna_Error:エラー
1023      */
1024     function _trigger_XMLRPC($action_name = "")
1025     {
1026         // prepare xmlrpc server
1027         $xmlrpc_gateway_method_name = "_Ethna_XmlrpcGateway";
1028         $xmlrpc_server = xmlrpc_server_create();
1029
1030         $method = null;
1031         $param = xmlrpc_decode_request(file_get_contents('php://input'), $method);
1032         $this->xmlrpc_method_name = $method;
1033
1034         $request = xmlrpc_encode_request(
1035             $xmlrpc_gateway_method_name,
1036             $param,
1037             array(
1038                 'output_type'   => 'xml',
1039                 'verbosity'     => 'pretty',
1040                 'escaping'      => array('markup'),
1041                 'version'       => 'xmlrpc',
1042                 'encoding'      => 'utf-8'
1043             )
1044         );
1045
1046         xmlrpc_server_register_method(
1047             $xmlrpc_server,
1048             $xmlrpc_gateway_method_name,
1049             $xmlrpc_gateway_method_name
1050         );
1051
1052         // send request
1053         $r = xmlrpc_server_call_method(
1054             $xmlrpc_server,
1055             $request,
1056             null,
1057             array(
1058                 'output_type'   => 'xml',
1059                 'verbosity'     => 'pretty',
1060                 'escaping'      => array('markup'),
1061                 'version'       => 'xmlrpc',
1062                 'encoding'      => 'utf-8'
1063             )
1064         );
1065
1066         header('Content-Length: ' . strlen($r));
1067         header('Content-Type: text/xml; charset=UTF-8');
1068         print $r;
1069     }
1070
1071     /**
1072      *  _trigger_XMLRPCのコールバックメソッド
1073      *
1074      *  @access public
1075      */
1076     function trigger_XMLRPC($method, $param)
1077     {
1078         // アクション定義の取得
1079         $action_obj = $this->_getAction($method);
1080         if (is_null($action_obj)) {
1081             return Ethna::raiseError("undefined xmlrpc method [%s]", E_APP_UNDEFINED_ACTION, $method);
1082         }
1083
1084         // オブジェクト生成
1085         $backend = $this->getBackend();
1086
1087         $form_name = $this->getActionFormName($method);
1088         $this->action_form = new $form_name($this);
1089         $def = $this->action_form->getDef();
1090         $n = 0;
1091         foreach ($def as $key => $value) {
1092             if (isset($param[$n]) == false) {
1093                 $this->action_form->set($key, null);
1094             } else {
1095                 $this->action_form->set($key, $param[$n]);
1096             }
1097             $n++;
1098         }
1099
1100         // バックエンド処理実行
1101         $backend->setActionForm($this->action_form);
1102
1103         $session = $this->getSession();
1104         $session->restore();
1105         $r = $backend->perform($method);
1106
1107         return $r;
1108     }
1109
1110     /**
1111      *  SOAPフレームワークの処理を実行する
1112      *
1113      *  @access private
1114      */
1115     function _trigger_SOAP()
1116     {
1117         // SOAPエントリクラス
1118         $gg = new Ethna_SOAP_GatewayGenerator();
1119         $script = $gg->generate();
1120         eval($script);
1121
1122         // SOAPリクエスト処理
1123         $server = new SoapServer(null, array('uri' => $this->config->get('url')));
1124         $server->setClass($gg->getClassName());
1125         $server->handle();
1126     }
1127
1128     /**
1129      *  エラーハンドラ
1130      *
1131      *  エラー発生時の追加処理を行いたい場合はこのメソッドをオーバーライドする
1132      *  (アラートメール送信等−デフォルトではログ出力時にアラートメール
1133      *  が送信されるが、エラー発生時に別にアラートメールをここで送信
1134      *  させることも可能)
1135      *
1136      *  @access public
1137      *  @param  object  Ethna_Error     エラーオブジェクト
1138      */
1139     function handleError(&$error)
1140     {
1141         // ログ出力
1142         list ($log_level, $dummy) = $this->logger->errorLevelToLogLevel($error->getLevel());
1143         $message = $error->getMessage();
1144         $this->logger->log($log_level, sprintf("%s [ERROR CODE(%d)]", $message, $error->getCode()));
1145     }
1146
1147     /**
1148      *  エラーメッセージを取得する
1149      *
1150      *  @access public
1151      *  @param  int     $code       エラーコード
1152      *  @return string  エラーメッセージ
1153      */
1154     function getErrorMessage($code)
1155     {
1156         $message_list = $GLOBALS['_Ethna_error_message_list'];
1157         for ($i = count($message_list)-1; $i >= 0; $i--) {
1158             if (array_key_exists($code, $message_list[$i])) {
1159                 return $message_list[$i][$code];
1160             }
1161         }
1162         return null;
1163     }
1164
1165     /**
1166      *  実行するアクション名を返す
1167      *
1168      *  @access private
1169      *  @param  mixed   $default_action_name    指定のアクション名
1170      *  @return string  実行するアクション名
1171      */
1172     function _getActionName($default_action_name, $fallback_action_name)
1173     {
1174         // フォームから要求されたアクション名を取得する
1175         $form_action_name = $this->_getActionName_Form();
1176         $form_action_name = preg_replace('/[^a-z0-9\-_]+/i', '', $form_action_name);
1177         $this->logger->log(LOG_DEBUG, 'form_action_name[%s]', $form_action_name);
1178
1179         // Ethnaマネージャへのフォームからのリクエストは拒否
1180         if ($form_action_name == "__ethna_info__" ||
1181             $form_action_name == "__ethna_unittest__") {
1182             $form_action_name = "";
1183         }
1184
1185         // フォームからの指定が無い場合はエントリポイントに指定されたデフォルト値を利用する
1186         if ($form_action_name == "" && count($default_action_name) > 0) {
1187             $tmp = is_array($default_action_name) ? $default_action_name[0] : $default_action_name;
1188             if ($tmp{strlen($tmp)-1} == '*') {
1189                 $tmp = substr($tmp, 0, -1);
1190             }
1191             $this->logger->log(LOG_DEBUG, '-> default_action_name[%s]', $tmp);
1192             $action_name = $tmp;
1193         } else {
1194             $action_name = $form_action_name;
1195         }
1196
1197         // エントリポイントに配列が指定されている場合は指定以外のアクション名は拒否する
1198         if (is_array($default_action_name)) {
1199             if ($this->_isAcceptableActionName($action_name, $default_action_name) == false) {
1200                 // 指定以外のアクション名で合った場合は$fallback_action_name(or デフォルト)
1201                 $tmp = $fallback_action_name != "" ? $fallback_action_name : $default_action_name[0];
1202                 if ($tmp{strlen($tmp)-1} == '*') {
1203                     $tmp = substr($tmp, 0, -1);
1204                 }
1205                 $this->logger->log(LOG_DEBUG, '-> fallback_action_name[%s]', $tmp);
1206                 $action_name = $tmp;
1207             }
1208         }
1209
1210         $this->logger->log(LOG_DEBUG, '<<< action_name[%s] >>>', $action_name);
1211
1212         return $action_name;
1213     }
1214
1215     /**
1216      *  フォームにより要求されたアクション名を返す
1217      *
1218      *  アプリケーションの性質に応じてこのメソッドをオーバーライドして下さい。
1219      *  デフォルトでは"action_"で始まるフォーム値の"action_"の部分を除いたもの
1220      *  ("action_sample"なら"sample")がアクション名として扱われます
1221      *
1222      *  @access protected
1223      *  @return string  フォームにより要求されたアクション名
1224      */
1225     function _getActionName_Form()
1226     {
1227         if (isset($_SERVER['REQUEST_METHOD']) == false) {
1228             return null;
1229         }
1230
1231         $url_handler = $this->getUrlHandler();
1232         if ($_SERVER['REQUEST_METHOD'] == "GET") {
1233             $tmp_vars = $_GET;
1234         } else if ($_SERVER['REQUEST_METHOD'] == "POST") {
1235             $tmp_vars = $_POST;
1236         }
1237
1238         if (empty($_SERVER['URL_HANDLER']) == false) {
1239             $tmp_vars['__url_handler__'] = $_SERVER['URL_HANDLER'];
1240             $tmp_vars['__url_info__'] = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : null;
1241             $tmp_vars = $url_handler->requestToAction($tmp_vars);
1242
1243             if ($_SERVER['REQUEST_METHOD'] == "GET") {
1244                 $_GET = array_merge($_GET, $tmp_vars);
1245             } else if ($_SERVER['REQUEST_METHOD'] == "POST") {
1246                 $_POST = array_merge($_POST, $tmp_vars);
1247             }
1248             $_REQUEST = array_merge($_REQUEST, $tmp_vars);
1249         }
1250
1251         if (strcasecmp($_SERVER['REQUEST_METHOD'], 'post') == 0) {
1252             $http_vars = $_POST;
1253         } else {
1254             $http_vars = $_GET;
1255         }
1256
1257         // フォーム値からリクエストされたアクション名を取得する
1258         $action_name = $sub_action_name = null;
1259         foreach ($http_vars as $name => $value) {
1260             if ($value == "" || strncmp($name, 'action_', 7) != 0) {
1261                 continue;
1262             }
1263
1264             $tmp = substr($name, 7);
1265
1266             // type="image"対応
1267             if (preg_match('/_x$/', $name) || preg_match('/_y$/', $name)) {
1268                 $tmp = substr($tmp, 0, strlen($tmp)-2);
1269             }
1270
1271             // value="dummy"となっているものは優先度を下げる
1272             if ($value == "dummy") {
1273                 $sub_action_name = $tmp;
1274             } else {
1275                 $action_name = $tmp;
1276             }
1277         }
1278         if ($action_name == null) {
1279             $action_name = $sub_action_name;
1280         }
1281
1282         return $action_name;
1283     }
1284
1285     /**
1286      *  アクション名を指定するクエリ/HTMLを生成する
1287      *
1288      *  @access public
1289      *  @param  string  $action action to request
1290      *  @param  string  $type   hidden, url...
1291      *  @todo   consider gateway
1292      */
1293     function getActionRequest($action, $type = "hidden")
1294     {
1295         $s = null;
1296         if ($type == "hidden") {
1297             $s = sprintf('<input type="hidden" name="action_%s" value="true" />', htmlspecialchars($action, ENT_QUOTES));
1298         } else if ($type == "url") {
1299             $s = sprintf('action_%s=true', urlencode($action));
1300         }
1301         return $s;
1302     }
1303
1304     /**
1305      *  フォームにより要求されたアクション名に対応する定義を返す
1306      *
1307      *  @access private
1308      *  @param  string  $action_name    アクション名
1309      *  @return array   アクション定義
1310      */
1311     function _getAction($action_name, $gateway = null)
1312     {
1313         $action = array();
1314         $gateway = is_null($gateway) ? $this->getGateway() : $gateway;
1315         switch ($gateway) {
1316         case GATEWAY_WWW:
1317             $action = $this->action;
1318             break;
1319         case GATEWAY_CLI:
1320             $action = $this->action_cli;
1321             break;
1322         case GATEWAY_XMLRPC:
1323             $action = $this->action_xmlrpc;
1324             break;
1325         }
1326
1327         $action_obj = array();
1328         if (isset($action[$action_name])) {
1329             $action_obj = $action[$action_name];
1330             if (isset($action_obj['inspect']) && $action_obj['inspect']) {
1331                 return $action_obj;
1332             }
1333         } else {
1334             $this->logger->log(LOG_DEBUG, "action [%s] is not defined -> try default", $action_name);
1335         }
1336
1337         // アクションスクリプトのインクルード
1338         $this->_includeActionScript($action_obj, $action_name);
1339
1340         // 省略値の補正
1341         if (isset($action_obj['class_name']) == false) {
1342             $action_obj['class_name'] = $this->getDefaultActionClass($action_name);
1343         }
1344
1345         if (isset($action_obj['form_name']) == false) {
1346             $action_obj['form_name'] = $this->getDefaultFormClass($action_name);
1347         } else if (class_exists($action_obj['form_name']) == false) {
1348             // 明示指定されたフォームクラスが定義されていない場合は警告
1349             $this->logger->log(LOG_WARNING, 'stated form class is not defined [%s]', $action_obj['form_name']);
1350         }
1351
1352         // 必要条件の確認
1353         if (class_exists($action_obj['class_name']) == false) {
1354             $this->logger->log(LOG_NOTICE, 'action class is not defined [%s]', $action_obj['class_name']);
1355             $_ret_object = null;
1356             return $_ret_object;
1357         }
1358         if (class_exists($action_obj['form_name']) == false) {
1359             // フォームクラスは未定義でも良い
1360             $class_name = $this->class_factory->getObjectName('form');
1361             $this->logger->log(LOG_DEBUG, 'form class is not defined [%s] -> falling back to default [%s]', $action_obj['form_name'], $class_name);
1362             $action_obj['form_name'] = $class_name;
1363         }
1364
1365         $action_obj['inspect'] = true;
1366         $action[$action_name] = $action_obj;
1367         return $action[$action_name];
1368     }
1369
1370     /**
1371      *  アクション名とアクションクラスからの戻り値に基づいて遷移先を決定する
1372      *
1373      *  @access protected
1374      *  @param  string  $action_name    アクション名
1375      *  @param  string  $retval         アクションクラスからの戻り値
1376      *  @return string  遷移先
1377      */
1378     function _sortForward($action_name, $retval)
1379     {
1380         return $retval;
1381     }
1382
1383     /**
1384      *  フィルタチェインを生成する
1385      *
1386      *  @access private
1387      */
1388     function _createFilterChain()
1389     {
1390         $this->filter_chain = array();
1391         foreach ($this->filter as $filter) {
1392             $filter_plugin = $this->plugin->getPlugin('Filter', $filter);
1393             if (Ethna::isError($filter_plugin)) {
1394                 continue;
1395             }
1396
1397             $this->filter_chain[] = $filter_plugin;
1398         }
1399     }
1400
1401     /**
1402      *  アクション名が実行許可されているものかどうかを返す
1403      *
1404      *  @access private
1405      *  @param  string  $action_name            リクエストされたアクション名
1406      *  @param  array   $default_action_name    許可されているアクション名
1407      *  @return bool    true:許可 false:不許可
1408      */
1409     function _isAcceptableActionName($action_name, $default_action_name)
1410     {
1411         foreach (to_array($default_action_name) as $name) {
1412             if ($action_name == $name) {
1413                 return true;
1414             } else if ($name{strlen($name)-1} == '*') {
1415                 if (strncmp($action_name, substr($name, 0, -1), strlen($name)-1) == 0) {
1416                     return true;
1417                 }
1418             }
1419         }
1420         return false;
1421     }
1422
1423     /**
1424      *  指定されたアクションのフォームクラス名を返す(オブジェクトの生成は行わない)
1425      *
1426      *  @access public
1427      *  @param  string  $action_name    アクション名
1428      *  @return string  アクションのフォームクラス名
1429      */
1430     function getActionFormName($action_name)
1431     {
1432         $action_obj = $this->_getAction($action_name);
1433         if (is_null($action_obj)) {
1434             return null;
1435         }
1436
1437         return $action_obj['form_name'];
1438     }
1439
1440     /**
1441      *  アクションに対応するフォームクラス名が省略された場合のデフォルトクラス名を返す
1442      *
1443      *  デフォルトでは[プロジェクトID]_Form_[アクション名]となるので好み応じてオーバライドする
1444      *
1445      *  @access public
1446      *  @param  string  $action_name    アクション名
1447      *  @return string  アクションフォーム名
1448      */
1449     function getDefaultFormClass($action_name, $gateway = null)
1450     {
1451         $gateway_prefix = $this->_getGatewayPrefix($gateway);
1452
1453         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($action_name));
1454         $r = sprintf("%s_%sForm_%s", $this->getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
1455         $this->logger->log(LOG_DEBUG, "default action class [%s]", $r);
1456
1457         return $r;
1458     }
1459
1460     /**
1461      *  getDefaultFormClass()で取得したクラス名からアクション名を取得する
1462      *
1463      *  getDefaultFormClass()をオーバーライドした場合、こちらも合わせてオーバーライド
1464      *  することを推奨(必須ではない)
1465      *
1466      *  @access public
1467      *  @param  string  $class_name     フォームクラス名
1468      *  @return string  アクション名
1469      */
1470     function actionFormToName($class_name)
1471     {
1472         $prefix = sprintf("%s_Form_", $this->getAppId());
1473         if (preg_match("/$prefix(.*)/", $class_name, $match) == 0) {
1474             // 不明なクラス名
1475             return null;
1476         }
1477         $target = $match[1];
1478
1479         $action_name = substr(preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $target), 1);
1480
1481         return $action_name;
1482     }
1483
1484     /**
1485      *  アクションに対応するフォームパス名が省略された場合のデフォルトパス名を返す
1486      *
1487      *  デフォルトでは_getDefaultActionPath()と同じ結果を返す(1ファイルに
1488      *  アクションクラスとフォームクラスが記述される)ので、好みに応じて
1489      *  オーバーライドする
1490      *
1491      *  @access public
1492      *  @param  string  $action_name    アクション名
1493      *  @return string  form classが定義されるスクリプトのパス名
1494      */
1495     function getDefaultFormPath($action_name)
1496     {
1497         return $this->getDefaultActionPath($action_name);
1498     }
1499
1500     /**
1501      *  指定されたアクションのクラス名を返す(オブジェクトの生成は行わない)
1502      *
1503      *  @access public
1504      *  @param  string  $action_name    アクションの名称
1505      *  @return string  アクションのクラス名
1506      */
1507     function getActionClassName($action_name)
1508     {
1509         $action_obj = $this->_getAction($action_name);
1510         if ($action_obj == null) {
1511             return null;
1512         }
1513
1514         return $action_obj['class_name'];
1515     }
1516
1517     /**
1518      *  アクションに対応するアクションクラス名が省略された場合のデフォルトクラス名を返す
1519      *
1520      *  デフォルトでは[プロジェクトID]_Action_[アクション名]となるので好み応じてオーバライドする
1521      *
1522      *  @access public
1523      *  @param  string  $action_name    アクション名
1524      *  @return string  アクションクラス名
1525      */
1526     function getDefaultActionClass($action_name, $gateway = null)
1527     {
1528         $gateway_prefix = $this->_getGatewayPrefix($gateway);
1529
1530         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($action_name));
1531         $r = sprintf("%s_%sAction_%s", $this->getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
1532         $this->logger->log(LOG_DEBUG, "default action class [%s]", $r);
1533
1534         return $r;
1535     }
1536
1537     /**
1538      *  getDefaultActionClass()で取得したクラス名からアクション名を取得する
1539      *
1540      *  getDefaultActionClass()をオーバーライドした場合、こちらも合わせてオーバーライド
1541      *  することを推奨(必須ではない)
1542      *
1543      *  @access public
1544      *  @param  string  $class_name     アクションクラス名
1545      *  @return string  アクション名
1546      */
1547     function actionClassToName($class_name)
1548     {
1549         $prefix = sprintf("%s_Action_", $this->getAppId());
1550         if (preg_match("/$prefix(.*)/", $class_name, $match) == 0) {
1551             // 不明なクラス名
1552             return null;
1553         }
1554         $target = $match[1];
1555
1556         $action_name = substr(preg_replace('/([A-Z])/e', "'_' . strtolower('\$1')", $target), 1);
1557
1558         return $action_name;
1559     }
1560
1561     /**
1562      *  アクションに対応するアクションパス名が省略された場合のデフォルトパス名を返す
1563      *
1564      *  デフォルトでは"foo_bar" -> "/Foo/Bar.php"となるので好み応じてオーバーライドする
1565      *
1566      *  @access public
1567      *  @param  string  $action_name    アクション名
1568      *  @return string  アクションクラスが定義されるスクリプトのパス名
1569      */
1570     function getDefaultActionPath($action_name)
1571     {
1572         $r = preg_replace('/_(.)/e', "'/' . strtoupper('\$1')", ucfirst($action_name)) . '.' . $this->getExt('php');
1573         $this->logger->log(LOG_DEBUG, "default action path [%s]", $r);
1574
1575         return $r;
1576     }
1577
1578     /**
1579      *  指定された遷移名に対応するビュークラス名を返す(オブジェクトの生成は行わない)
1580      *
1581      *  @access public
1582      *  @param  string  $forward_name   遷移先の名称
1583      *  @return string  view classのクラス名
1584      */
1585     function getViewClassName($forward_name)
1586     {
1587         if ($forward_name == null) {
1588             return null;
1589         }
1590
1591         if (isset($this->forward[$forward_name])) {
1592             $forward_obj = $this->forward[$forward_name];
1593         } else {
1594             $forward_obj = array();
1595         }
1596
1597         if (isset($forward_obj['view_name'])) {
1598             $class_name = $forward_obj['view_name'];
1599             if (class_exists($class_name)) {
1600                 return $class_name;
1601             }
1602         } else {
1603             $class_name = null;
1604         }
1605
1606         // viewのインクルード
1607         $this->_includeViewScript($forward_obj, $forward_name);
1608
1609         if (is_null($class_name) == false && class_exists($class_name)) {
1610             return $class_name;
1611         } else if (is_null($class_name) == false) {
1612             $this->logger->log(LOG_WARNING, 'stated view class is not defined [%s] -> try default', $class_name);
1613         }
1614
1615         $class_name = $this->getDefaultViewClass($forward_name);
1616         if (class_exists($class_name)) {
1617             return $class_name;
1618         } else {
1619             $class_name = $this->class_factory->getObjectName('view');
1620             $this->logger->log(LOG_DEBUG, 'view class is not defined for [%s] -> use default [%s]', $forward_name, $class_name);
1621             return $class_name;
1622         }
1623     }
1624
1625     /**
1626      *  遷移名に対応するビュークラス名が省略された場合のデフォルトクラス名を返す
1627      *
1628      *  デフォルトでは[プロジェクトID]_View_[遷移名]となるので好み応じてオーバライドする
1629      *
1630      *  @access public
1631      *  @param  string  $forward_name   forward名
1632      *  @return string  view classクラス名
1633      */
1634     function getDefaultViewClass($forward_name, $gateway = null)
1635     {
1636         $gateway_prefix = $this->_getGatewayPrefix($gateway);
1637
1638         $postfix = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($forward_name));
1639         $r = sprintf("%s_%sView_%s", $this->getAppId(), $gateway_prefix ? $gateway_prefix . "_" : "", $postfix);
1640         $this->logger->log(LOG_DEBUG, "default view class [%s]", $r);
1641
1642         return $r;
1643     }
1644
1645     /**
1646      *  遷移名に対応するビューパス名が省略された場合のデフォルトパス名を返す
1647      *
1648      *  デフォルトでは"foo_bar" -> "/Foo/Bar.php"となるので好み応じてオーバーライドする
1649      *
1650      *  @access public
1651      *  @param  string  $forward_name   forward名
1652      *  @return string  view classが定義されるスクリプトのパス名
1653      */
1654     function getDefaultViewPath($forward_name)
1655     {
1656         $r = preg_replace('/_(.)/e', "'/' . strtoupper('\$1')", ucfirst($forward_name)) . '.' . $this->getExt('php');
1657         $this->logger->log(LOG_DEBUG, "default view path [%s]", $r);
1658
1659         return $r;
1660     }
1661
1662     /**
1663      *  遷移名に対応するテンプレートパス名が省略された場合のデフォルトパス名を返す
1664      *
1665      *  デフォルトでは"foo_bar"というforward名が"foo/bar" + テンプレート拡張子となる
1666      *  ので好み応じてオーバライドする
1667      *
1668      *  @access public
1669      *  @param  string  $forward_name   forward名
1670      *  @return string  forwardパス名
1671      */
1672     function getDefaultForwardPath($forward_name)
1673     {
1674         return str_replace('_', '/', $forward_name) . '.' . $this->ext['tpl'];
1675     }
1676
1677     /**
1678      *  テンプレートパス名から遷移名を取得する
1679      *
1680      *  getDefaultForwardPath()をオーバーライドした場合、こちらも合わせてオーバーライド
1681      *  することを推奨(必須ではない)
1682      *
1683      *  @access public
1684      *  @param  string  $forward_path   テンプレートパス名
1685      *  @return string  遷移名
1686      */
1687     function forwardPathToName($forward_path)
1688     {
1689         $forward_path = preg_replace('/^\/+/', '', $forward_path);
1690         $forward_path = preg_replace(sprintf('/\.%s$/', $this->getExt('tpl')), '', $forward_path);
1691
1692         return str_replace('/', '_', $forward_path);
1693     }
1694
1695     /**
1696      *  遷移名からテンプレートファイルのパス名を取得する
1697      *
1698      *  @access private
1699      *  @param  string  $forward_name   forward名
1700      *  @return string  テンプレートファイルのパス名
1701      */
1702     function _getForwardPath($forward_name)
1703     {
1704         $forward_obj = null;
1705
1706         if (isset($this->forward[$forward_name]) == false) {
1707             // try default
1708             $this->forward[$forward_name] = array();
1709         }
1710         $forward_obj = $this->forward[$forward_name];
1711         if (isset($forward_obj['forward_path']) == false) {
1712             // 省略値補正
1713             $forward_obj['forward_path'] = $this->getDefaultForwardPath($forward_name);
1714         }
1715
1716         return $forward_obj['forward_path'];
1717     }
1718
1719     /**
1720      *  レンダラを取得する(getTemplateEngine()はそのうち廃止されgetRenderer()に統合される予定)
1721      *
1722      *  @access public
1723      *  @return object  Ethna_Renderer  レンダラオブジェクト
1724      */
1725     public function getRenderer()
1726     {
1727         $_ret_object = $this->getTemplateEngine();
1728         return $_ret_object;
1729     }
1730
1731     /**
1732      *  テンプレートエンジン取得する
1733      *
1734      *  @access public
1735      *  @return object  Ethna_Renderer  レンダラオブジェクト
1736      *  @obsolete
1737      */
1738     public function getTemplateEngine()
1739     {
1740         if (is_object($this->renderer)) {
1741             return $this->renderer;
1742         }
1743
1744         $this->renderer = $this->class_factory->getObject('renderer');
1745
1746         //テンプレートエンジンのデフォルトの設定
1747         $this->_setDefaultTemplateEngine($this->renderer);
1748         // }}}
1749
1750         return $this->renderer;
1751     }
1752
1753     /**
1754      *  テンプレートエンジンのデフォルト状態を設定する
1755      *
1756      *  @access protected
1757      *  @param  object  Ethna_Renderer  レンダラオブジェクト
1758      *  @obsolete
1759      */
1760     protected function _setDefaultTemplateEngine($renderer)
1761     {
1762     }
1763
1764     /**
1765      *  使用言語、ロケールを設定する
1766      *  条件によって使用言語、ロケールを切り替えたい場合は、
1767      *  このメソッドをオーバーライドする。
1768      *
1769      *  @access protected
1770      *  @param  string  $locale             ロケール名(ja_JP, en_US等)
1771      *                                      (ll_cc の形式。ll = 言語コード cc = 国コード)
1772      *  @param  string  $system_encoding    システムエンコーディング名
1773      *  @param  string  $client_encoding    クライアントエンコーディング(テンプレートのエンコーディングと考えれば良い)
1774      *  @see    http://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html
1775      *  @see    Ethna_Controller#_getDefaultLanguage
1776      */
1777     protected function _setLanguage($locale, $system_encoding = null, $client_encoding = null)
1778     {
1779         $this->locale = $locale;
1780         $this->system_encoding = $system_encoding;
1781         $this->client_encoding = $client_encoding;
1782
1783         //   $this->locale, $this->client_encoding を書き換えた場合は
1784         //   必ず Ethna_I18N クラスの setLanguageメソッドも呼ぶこと!
1785         //   さもないとカタログその他が再ロードされない!
1786         $i18n = $this->getI18N();
1787         $i18n->setLanguage($locale, $system_encoding, $client_encoding);
1788     }
1789
1790     /**
1791      *  デフォルト状態での使用言語を取得する
1792      *  外部に出力されるEthnaのエラーメッセージ等のエンコーディングを
1793      *  切り替えたい場合は、このメソッドをオーバーライドする。
1794      *
1795      *  @access protected
1796      *  @return array   ロケール名(e.x ja_JP, en_US 等),
1797      *                  システムエンコーディング名,
1798      *                  クライアントエンコーディング名
1799      *                  (= テンプレートのエンコーディングと考えてよい) の配列
1800      *                  (ロケール名は ll_cc の形式。ll = 言語コード cc = 国コード)
1801      *
1802      *  WARNING!! : クライアントエンコーディング名が、フレームワークの内部エンコーデ
1803      *              ィングとして設定されます。つまり、クライアントエンコーディングで
1804      *              ブラウザからの入力は入ってくるものと想定しています!
1805      */
1806     protected function _getDefaultLanguage()
1807     {
1808         return array('ja_JP', 'UTF-8', 'UTF-8');
1809     }
1810
1811     /**
1812      *  デフォルト状態でのゲートウェイを取得する
1813      *
1814      *  @access protected
1815      *  @return int     ゲートウェイ定義(GATEWAY_WWW, GATEWAY_CLI...)
1816      */
1817     protected function _getDefaultGateway($gateway)
1818     {
1819         if (is_null($GLOBALS['_Ethna_gateway']) == false) {
1820             return $GLOBALS['_Ethna_gateway'];
1821         }
1822         return GATEWAY_WWW;
1823     }
1824
1825     /**
1826      *  ゲートウェイに対応したクラス名のプレフィクスを取得する
1827      *
1828      *  @access public
1829      *  @param  string  $gateway    ゲートウェイ
1830      *  @return string  ゲートウェイクラスプレフィクス
1831      */
1832     protected function _getGatewayPrefix($gateway = null)
1833     {
1834         $gateway = is_null($gateway) ? $this->getGateway() : $gateway;
1835         switch ($gateway) {
1836         case GATEWAY_WWW:
1837             $prefix = '';
1838             break;
1839         case GATEWAY_CLI:
1840             $prefix = 'Cli';
1841             break;
1842         case GATEWAY_XMLRPC:
1843             $prefix = 'Xmlrpc';
1844             break;
1845         default:
1846             $prefix = '';
1847             break;
1848         }
1849
1850         return $prefix;
1851     }
1852
1853     /**
1854      *  マネージャクラス名を取得する
1855      *
1856      *  @access public
1857      *  @param  string  $name   マネージャキー
1858      *  @return string  マネージャクラス名
1859      */
1860     public function getManagerClassName($name)
1861     {
1862         //   アプリケーションIDと、渡された名前のはじめを大文字にして、
1863         //   組み合わせたものが返される
1864         $manager_id = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($name));
1865         return sprintf('%s_%sManager', $this->getAppId(), ucfirst($manager_id));
1866     }
1867
1868     /**
1869      *  アプリケーションオブジェクトクラス名を取得する
1870      *
1871      *  @access public
1872      *  @param  string  $name   アプリケーションオブジェクトキー
1873      *  @return string  マネージャクラス名
1874      */
1875     public function getObjectClassName($name)
1876     {
1877         //  引数のはじめの一文字目と、アンダーバー直後の
1878         //  1文字を必ず大文字にする。アンダーバーは削除される。
1879         $name = preg_replace('/_(.)/e', "strtoupper('\$1')", ucfirst($name));
1880
1881         //  $name に foo_bar を渡し、AppID が Hogeの場合
1882         //  [Appid]_FooBar が返される
1883         return sprintf('%s_%s', $this->getAppId(), $name);
1884     }
1885
1886     /**
1887      *  アクションスクリプトをインクルードする
1888      *
1889      *  ただし、インクルードしたファイルにクラスが正しく定義されているかどうかは保証しない
1890      *
1891      *  @access private
1892      *  @param  array   $action_obj     アクション定義
1893      *  @param  string  $action_name    アクション名
1894      */
1895     protected function _includeActionScript($action_obj, $action_name)
1896     {
1897         $class_path = $form_path = null;
1898
1899         $action_dir = $this->getActiondir();
1900
1901         // class_path属性チェック
1902         if (isset($action_obj['class_path'])) {
1903             // フルパス指定サポート
1904             $tmp_path = $action_obj['class_path'];
1905             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1906                 $tmp_path = $action_dir . $tmp_path;
1907             }
1908
1909             if (file_exists($tmp_path) == false) {
1910                 $this->logger->log(LOG_WARNING, 'class_path file not found [%s] -> try default', $tmp_path);
1911             } else {
1912                 include_once $tmp_path;
1913                 $class_path = $tmp_path;
1914             }
1915         }
1916
1917         // デフォルトチェック
1918         if (is_null($class_path)) {
1919             $class_path = $this->getDefaultActionPath($action_name);
1920             if (file_exists($action_dir . $class_path)) {
1921                 include_once $action_dir . $class_path;
1922             } else {
1923                 $this->logger->log(LOG_DEBUG, 'default action file not found [%s] -> try all files', $class_path);
1924                 return;
1925             }
1926         }
1927
1928         // form_path属性チェック
1929         if (isset($action_obj['form_path'])) {
1930             // フルパス指定サポート
1931             $tmp_path = $action_obj['form_path'];
1932             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1933                 $tmp_path = $action_dir . $tmp_path;
1934             }
1935
1936             if ($tmp_path == $class_path) {
1937                 return;
1938             }
1939             if (file_exists($tmp_path) == false) {
1940                 $this->logger->log(LOG_WARNING, 'form_path file not found [%s] -> try default', $tmp_path);
1941             } else {
1942                 include_once $tmp_path;
1943                 $form_path = $tmp_path;
1944             }
1945         }
1946
1947         // デフォルトチェック
1948         if (is_null($form_path)) {
1949             $form_path = $this->getDefaultFormPath($action_name);
1950             if ($form_path == $class_path) {
1951                 return;
1952             }
1953             if (file_exists($action_dir . $form_path)) {
1954                 include_once $action_dir . $form_path;
1955             } else {
1956                 $this->logger->log(LOG_DEBUG, 'default form file not found [%s] -> maybe falling back to default form class', $form_path);
1957             }
1958         }
1959     }
1960
1961     /**
1962      *  ビュースクリプトをインクルードする
1963      *
1964      *  ただし、インクルードしたファイルにクラスが正しく定義されているかどうかは保証しない
1965      *
1966      *  @access private
1967      *  @param  array   $forward_obj    遷移定義
1968      *  @param  string  $forward_name   遷移名
1969      */
1970     protected function _includeViewScript($forward_obj, $forward_name)
1971     {
1972         $view_dir = $this->getViewdir();
1973
1974         // view_path属性チェック
1975         if (isset($forward_obj['view_path'])) {
1976             // フルパス指定サポート
1977             $tmp_path = $forward_obj['view_path'];
1978             if (Ethna_Util::isAbsolute($tmp_path) == false) {
1979                 $tmp_path = $view_dir . $tmp_path;
1980             }
1981
1982             if (file_exists($tmp_path) == false) {
1983                 $this->logger->log(LOG_WARNING, 'view_path file not found [%s] -> try default', $tmp_path);
1984             } else {
1985                 include_once $tmp_path;
1986                 return;
1987             }
1988         }
1989
1990         // デフォルトチェック
1991         $view_path = $this->getDefaultViewPath($forward_name);
1992         if (file_exists($view_dir . $view_path)) {
1993             include_once $view_dir . $view_path;
1994             return;
1995         } else {
1996             $this->logger->log(LOG_DEBUG, 'default view file not found [%s]', $view_path);
1997             $view_path = null;
1998         }
1999     }
2000
2001     /**
2002      *  ディレクトリ以下の全てのスクリプトをインクルードする
2003      *
2004      *  @access private
2005      */
2006     protected function _includeDirectory($dir)
2007     {
2008         $ext = "." . $this->ext['php'];
2009         $ext_len = strlen($ext);
2010
2011         if (is_dir($dir) == false) {
2012             return;
2013         }
2014
2015         $dh = opendir($dir);
2016         if ($dh) {
2017             while (($file = readdir($dh)) !== false) {
2018                 if ($file != '.' && $file != '..' && is_dir("$dir/$file")) {
2019                     $this->_includeDirectory("$dir/$file");
2020                 }
2021                 if (substr($file, -$ext_len, $ext_len) != $ext) {
2022                     continue;
2023                 }
2024                 include_once $dir . '/' . $file;
2025             }
2026         }
2027         closedir($dh);
2028     }
2029
2030     /**
2031      *  設定ファイルのDSN定義から使用するデータを再構築する(スレーブアクセス分岐等)
2032      *
2033      *  DSNの定義方法(デフォルト:設定ファイル)を変えたい場合はここをオーバーライドする
2034      *
2035      *  @access protected
2036      *  @return array   DSN定義(array('DBキー1' => 'dsn1', 'DBキー2' => 'dsn2', ...))
2037      */
2038     protected function _prepareDSN()
2039     {
2040         $r = array();
2041
2042         foreach ($this->db as $key => $value) {
2043             $config_key = "dsn";
2044             if ($key != "") {
2045                 $config_key .= "_$key";
2046             }
2047             $dsn = $this->config->get($config_key);
2048             if (is_array($dsn)) {
2049                 // 種別1つにつき複数DSNが定義されている場合はアクセス分岐
2050                 $dsn = $this->_selectDSN($key, $dsn);
2051             }
2052             $r[$key] = $dsn;
2053         }
2054         return $r;
2055     }
2056
2057     /**
2058      *  DSNのアクセス分岐を行う
2059      *
2060      *  スレーブサーバへの振分け処理(デフォルト:ランダム)を変更したい場合はこのメソッドをオーバーライドする
2061      *
2062      *  @access protected
2063      *  @param  string  $type       DB種別
2064      *  @param  array   $dsn_list   DSN一覧
2065      *  @return string  選択されたDSN
2066      */
2067     protected function _selectDSN($type, $dsn_list)
2068     {
2069         if (is_array($dsn_list) == false) {
2070             return $dsn_list;
2071         }
2072
2073         // デフォルト:ランダム
2074         list($usec, $sec) = explode(' ', microtime());
2075         mt_srand($sec + ((float) $usec * 100000));
2076         $n = mt_rand(0, count($dsn_list)-1);
2077
2078         return $dsn_list[$n];
2079     }
2080
2081     /**
2082      *  Ethnaマネージャを設定する
2083      *
2084      *  不要な場合は空のメソッドとしてオーバーライドしてもよい
2085      *
2086      *  @access protected
2087      */
2088     protected function _activateEthnaManager()
2089     {
2090         if ($this->config->get('debug') == false) {
2091             return;
2092         }
2093
2094         require_once ETHNA_BASE . '/class/InfoManager.php';
2095
2096         // see if we have simpletest
2097         if (file_exists_ex('simpletest/unit_tester.php', true)) {
2098             require_once ETHNA_BASE . '/class/UnitTestManager.php';
2099         }
2100
2101         // action設定
2102         $this->action['__ethna_info__'] = array(
2103             'form_name' =>  'Ethna_Form_Info',
2104             'form_path' =>  sprintf('%s/class/Action/Info.php', ETHNA_BASE),
2105             'class_name' => 'Ethna_Action_Info',
2106             'class_path' => sprintf('%s/class/Action/Info.php', ETHNA_BASE),
2107         );
2108
2109         // forward設定
2110         $this->forward['__ethna_info__'] = array(
2111             'forward_path'  => sprintf('%s/tpl/info.tpl', ETHNA_BASE),
2112             'view_name'     => 'Ethna_View_Info',
2113             'view_path'     => sprintf('%s/class/View/Info.php', ETHNA_BASE),
2114         );
2115
2116
2117         // action設定
2118         $this->action['__ethna_unittest__'] = array(
2119             'form_name' =>  'Ethna_Form_UnitTest',
2120             'form_path' =>  sprintf('%s/class/Action/UnitTest.php', ETHNA_BASE),
2121             'class_name' => 'Ethna_Action_UnitTest',
2122             'class_path' => sprintf('%s/class/Action/UnitTest.php', ETHNA_BASE),
2123         );
2124
2125         // forward設定
2126         $this->forward['__ethna_unittest__'] = array(
2127             'forward_path'  => sprintf('%s/tpl/unittest.tpl', ETHNA_BASE),
2128             'view_name'     => 'Ethna_View_UnitTest',
2129             'view_path'     => sprintf('%s/class/View/UnitTest.php', ETHNA_BASE),
2130         );
2131
2132     }
2133
2134     /**
2135      *  Ethnaマネージャが実行可能かをチェックする
2136      *
2137      *  Ethnaマネージャを実行するよう指示されているにも関わらず、
2138      *  debug が trueでない場合は実行を停止する。
2139      *
2140      *  @access private
2141      */
2142     protected function _ethnaManagerEnabledCheck($action_name)
2143     {
2144         if ($this->config->get('debug') == false
2145          && ($action_name == '__ethna_info__' || $action_name == '__ethna_unittest__')) {
2146             $this->ethnaManagerCheckErrorMsg($action_name);
2147             exit(0);
2148         }
2149     }
2150
2151     /**
2152      *  Ethnaマネージャが実行不能な場合のエラーメッセージを
2153      *  表示する。運用上の都合でこのメッセージを出力したくない
2154      *  場合は、このメソッドをオーバーライドせよ
2155      *
2156      *  @access protected
2157      */
2158      protected function ethnaManagerCheckErrorMsg($action_name)
2159      {
2160          $appid = strtolower($this->getAppId());
2161          $run_action = ($action_name == '__ethna_info__')
2162                      ? ' show Application Info List '
2163                      : ' run Unit Test ';
2164          echo "Ethna cannot {$run_action} under your application setting.<br>";
2165          echo "HINT: You must set {$appid}/etc/{$appid}-ini.php debug setting 'true'.<br>";
2166          echo "<br>";
2167          echo "In {$appid}-ini.php, please set as follows :<br><br>";
2168          echo "\$config = array ( 'debug' => true, );";
2169      }
2170
2171     /**
2172      *  CLI実行中フラグを取得する
2173      *
2174      *  @access public
2175      *  @return bool    CLI実行中フラグ
2176      *  @obsolete
2177      */
2178     public function getCLI()
2179     {
2180         return $this->gateway == GATEWAY_CLI ? true : false;
2181     }
2182
2183     /**
2184      *  CLI実行中フラグを設定する
2185      *
2186      *  @access public
2187      *  @param  bool    CLI実行中フラグ
2188      *  @obsolete
2189      */
2190     public function setCLI($cli)
2191     {
2192         $this->gateway = $cli ? GATEWAY_CLI : $this->_getDefaultGateway();
2193     }
2194 }
2195 // }}}
2196
2197 /**
2198  *  XMLRPCゲートウェイのスタブクラス
2199  *
2200  *  @access     public
2201  */
2202 function _Ethna_XmlrpcGateway($method_stub, $param)
2203 {
2204     $ctl = Ethna_Controller::getInstance();
2205     $method = $ctl->getXmlrpcMethodName();
2206     $r = $ctl->trigger_XMLRPC($method, $param);
2207     if (Ethna::isError($r)) {
2208         return array(
2209             'faultCode' => $r->getCode(),
2210             'faultString' => $r->getMessage(),
2211         );
2212     }
2213     return $r;
2214 }