• R/O
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

PHPのフレームワークです。オートローディング、ルーティング、ORマッパ、フォームバリデータ、その他ユーティリティがセットになっています。


Commit MetaInfo

修订版165 (tree)
时间2022-04-08 11:47:25
作者tantancode

Log Message

notfoundやerrorのハンドリングを再定義

更改概述

差异

--- docs/API共通仕様.txt (nonexistent)
+++ docs/API共通仕様.txt (revision 165)
@@ -0,0 +1,100 @@
1+
2+●サンプル
3+
4+ ・API "service/echo" をGET変数 foo=FOO で呼び出す。
5+
6+ var api = new Api("service/echo", {"foo":"FOO"});
7+
8+ var response = await api.transmit();
9+ console.log(response);
10+
11+ ・API "service/echo" をGET変数 foo=FOO、POST変数 bar=BAR で呼び出す。
12+
13+ var api = new Api("service/echo", {"foo":"FOO"}, {"bar":"BAR"});
14+
15+ var response = await api.transmit();
16+ console.log(response);
17+
18+ ・await を使わない場合は...
19+
20+ var api = new Api("service/echo", {"foo":"FOO"});
21+
22+ // Promise をストレートに使うか...
23+ api.transmit().then(function(response){
24+ console.log(response);
25+ });
26+
27+ // コールバックスタイルも使える。
28+ api.transmit(function(response) {
29+ console.log(response);
30+ });
31+
32+●共通仕様
33+
34+ ※ここに書かれている基礎的処理はすでに public/js/Api.js で実装されているので、普段は参照する必要はない。
35+
36+ ・アプリベースURL
37+
38+ APIを呼び出すときは、この「アプリベースURL」に各APIの「パス」をつなげてURLを構築する。
39+ Environment["app_base"] で参照できる。
40+
41+ ・共通のGETパラメータ
42+
43+ ver
44+ クライアントが認識しているサーバ側バージョン。次のグローバル変数から取得できる。
45+
46+ var version = Environment["version_stamp"];
47+
48+ クライアントが動作中にサーバ側でこの値が更新されると、値がサーバ・クライアントの間で食い違うことになる。
49+ するとサーバはすべてのAPIでエラーコード "version" を返すようになる(後述)ので、クライアントはそれを認識して
50+ ユーザに再読み込みを促す仕組みとなる。
51+
52+ ・共通のレスポンス
53+
54+ error (struct)
55+
56+ エラーが発生している場合にセットされる。エラーがない場合はセットされない。
57+ 以下のキーを持つ。
58+
59+ code (string)
60+ 共通定義として、以下のような値がセットされる。これ以外の値が個別のAPIで定義されている場合もある。
61+
62+ exception
63+ ユーザの操作を原因としないエラー。サーバ内でのエラーや、引数に渡した値が定義範囲外の場合など。
64+ ユーザへの案内: 問い合わせコードとして inquiry キーもセットされるのでその値を案内されたい。
65+ 案内後の遷移: トップページへ。
66+ maintenance
67+ サーバメンテナンス中。
68+ ユーザへの案内: 「メンテナンス中」など。
69+ 案内後の遷移: 特に無い。トップページもメンテ状態なので、そのまま。
70+ banned
71+ BANされている。
72+ ユーザへの案内: 「措置規定に該当したためアクセスが制限されている」など
73+ 案内後の遷移: 特に無い。
74+ irregular
75+ 以下のような、ユーザ起因だが、普通に操作していれば発生しないようなエラー。
76+ ・確認画面を長時間放置して、データが期限切れになった後に確定処理をしようとした
77+ ・ブラウザの戻るや複数タブによるフォーム連投
78+ 専用のエラー処理を構えるのが面倒くさくなるくらいに稀なケースで使用される。
79+ ユーザへの案内: ユーザへの説明メッセージとして message キーもセットされるので、そのテキストをそのまま表示。
80+ 問い合わせコードとして inquiry キーもセットされるので、その値も案内されたい。
81+ 案内後の遷移: トップページへ。
82+ needlogin
83+ ログインが必要。
84+ ユーザへの案内: 「ログインしてから再度操作して下さい」など。
85+ 案内後の遷移: ログインへ。
86+ version
87+ クライアントが伝えたサーバ側バージョンと、現行のサーバ側バージョンが一致していない。
88+ リロード必須の更新がサーバ側で発生したことを意味している。
89+ ユーザへの案内: 「サーバ側にデータの更新があります」など。
90+ 案内後の遷移: トップページへ。
91+
92+ inquiry (string)
93+ お問い合わせコード。ない場合はセットされない。
94+
95+ message (string)
96+ 専用のエラー説明テキストがある場合にセットされる。
97+
98+ description (string)
99+ デバック用。開発環境でのみセットされる。
100+ 例外メッセージやスタックトレースなど。
--- lib/AppMojo.php (revision 164)
+++ lib/AppMojo.php (revision 165)
@@ -94,6 +94,25 @@
9494 return $url;
9595 }
9696
97+ //----------------------------------------------------------------------------------------------------------
98+ /**
99+ * 可能なら、現在アクセス中のユーザにおけるエラーカウントを一つ増やす。
100+ */
101+ public static function incrementErrorCount() {
102+
103+ // ユーザIDが判明していて、管理者によるユーザ代行でないなら処理する
104+ $session = Mojo::setting('process', 'mojo.session')::getSession();
105+ if($session['user_id'] && !$session['operator_code']) {
106+
107+ // そのユーザの error_count を +1 する。
108+ $user = new UserInfo($session['user_id']);
109+ if($user->load()) {
110+ $user['error_count'] += 1;
111+ $user->save();
112+ }
113+ }
114+ }
115+
97116 //---------------------------------------------------------------------------------------------------------
98117 /**
99118 * デバック用。"<html><body>" と "</body></html>" で挟んだ上で、var_dump を行い、exitする。
@@ -171,58 +190,70 @@
171190
172191 //-----------------------------------------------------------------------------------------------------
173192 /**
174- * エラー処理をオーバーライド。
193+ * オーバーライド。
194+ * 引数に指定された例外を処理する。ここから例外をスローした場合はPHP標準の方法で処理される。
175195 */
176196 protected static function processException($e) {
177197
178- // リクエストされているサイトに、settings.php の mojo.error_action で設定されているアクションが存在する場合は、そのアクションでエラーを表示する。
179- try {
198+ $error = array();
199+ $error['exception'] = $e;
200+
201+ // ログに記録して問い合わせコードと書き込み内容を得る。
202+ [ $error['inquiry'], $error['description'] ] = AppLog::exception($e);
203+
204+ // ユーザに表示するメッセージを決定。IrregularAccess なら例外から取得する。
205+ $error['message'] = ($e instanceof IrregularAccess) ? $e->getMessage() : '申しわけありません。エラーが発生しました。';
206+
207+ // HTTPエラーコードを設定。
208+ if(!headers_sent())
209+ http_response_code(500);
210+
211+ // 可能なら、ユーザのエラーカウント+1。
212+ AppMojo::incrementErrorCount();
213+
214+ // settings.php の mojo.error_direction で処理する。
215+ $direction = @self::setting('routing', 'mojo.error_direction');
216+ if($direction) {
180217 $site = self::setting('runtime', 'mojo.requested_site');
181- $action = self::setting('routing', 'mojo.error_action');
182- if(self::searchAction($site, $action)) {
183- self::forward("{$site}/{$action}", ['error'=>$e]);
218+ if( self::forward("$site/$direction", $error) )
184219 return;
185- }
186220 }
187- // そこでも失敗した場合は、その失敗例外の方をデフォルトの方法で表示する。
188- catch(Exception $miss) {
189- $e = $miss;
190- }
191221
192- // 以降、該当アクションがない場合のデフォルトエラー表示。
222+ // mojo.error_direction で処理されない場合は標準のエラー処理を行う。
193223
194224 // 見やすいように text/plain にする。
195225 if(!headers_sent())
196226 header("Content-Type: text/plain; charset=UTF-8");
197227
198- // OperationalException では例外メッセージをそのままユーザメッセージとして使用する。
199- if($e instanceof OperationalException) {
200- echo $e->getMessage();
201- return;
202- }
228+ // まずはメッセージを表示。
229+ echo $error['message'] . "\n";
203230
204- // 以降、OperationalException 以外の処理。
231+ // 問い合わせコードがあるならそれも表示。
232+ if($error['inquiry'])
233+ echo "\ncode: " . $error['inquiry'] . "\n";
205234
206- // HTTPエラーコードを設定。
207- if(!headers_sent())
208- http_response_code(500);
235+ // 本番環境以外では詳細も表示。
236+ if(static::setting('application', 'environment_type') != 'prod')
237+ echo "\n" . $error['description'];
238+ }
209239
210- // ログに記録する。
211- $log = AppLog::exception($e);
240+ //----------------------------------------------------------------------------------------------------------
241+ /**
242+ * オーバーライド。
243+ * リクエストされたアクションが見付からない場合の処理を行う。
244+ */
245+ protected static function processNotfound($name) {
212246
213- // 本番環境なら簡易なメッセージとエラーログを引くためのコードを表示する。
214- if(static::setting('application', 'environment_type') == 'prod') {
247+ // routing設定で定められているnotfoundディレクションを、リクエストされているサイト内で探して処理させる。
248+ $notfound = static::setting('routing', 'mojo.notfound_direction');
249+ if($notfound) {
250+ $site = static::setting('runtime', 'mojo.requested_site');
251+ if( static::forward("$site/$notfound") )
252+ return;
253+ }
215254
216- echo "申しわけありません。エラーが発生しました。\n"
217- . "お問い合わせコード: {$log['code']}\n";
218-
219- if($e instanceof IllegalTouchException)
220- echo "複数のタブでの操作や、ブラウザの戻る機能を利用した再送信は控えていただけると幸いです。\n";
221-
222- // 本番環境でないなら詳細に表示。
223- }else {
224- echo $log['text'];
225- }
255+ // notfoundディレクションの設定がない、あるいは該当のアクションがない場合は基底の処理とする。
256+ parent::processNotfound($name);
226257 }
227258
228259 //-----------------------------------------------------------------------------------------------------
--- lib/Localizer.php (revision 164)
+++ lib/Localizer.php (revision 165)
@@ -19,8 +19,8 @@
1919 *
2020 * ・次のように展開すると...
2121 *
22- * $Localizer = new Localizer();
23- * $r = $Localizer($s);
22+ * $localizer = new Localizer();
23+ * $r = $localizer->processSymbolicText($s);
2424 *
2525 * ・$r は次のような文字列になる。
2626 *
@@ -35,9 +35,9 @@
3535 *
3636 * 融合したシンボルを使わず、素直に関数を使って局所的に展開するほうが良いだろう。
3737 *
38- * <div><?php echo $Localizer->expandSymbol('sample:これはフー') ?></div>
39- * <div><?php echo $Localizer->expandSymbol('sample:N個あるよ', ['num'=>5]) ?></div>
40- * <div><?php echo $Localizer->expandSymbol('sample:これはバー', ['keyword'=>'<span style="color:red">'']) ?></div>
38+ * <div><?php echo $localizer->expandSymbol('sample:これはフー') ?></div>
39+ * <div><?php echo $localizer->expandSymbol('sample:N個あるよ', ['num'=>5]) ?></div>
40+ * <div><?php echo $localizer->expandSymbol('sample:これはバー', ['keyword'=>'<span style="color:red">'']) ?></div>
4141 * <div><?php echo $user_data ?></div>
4242 *
4343 * こうすれば、シンボル展開をする場所としない場所を制御できる。
@@ -352,19 +352,19 @@
352352 * /some/file/bar.txt
353353 *
354354 * 呼び出しと戻り値は次のようになる。
355- * $Localizer = new Localizer('xx');
356- * $Localizer->getSpecificFile('/some/file/foo.txt'); // 戻り値 /some/file/foo.xx.txt
357- * $Localizer = new Localizer('yy');
358- * $Localizer->getSpecificFile('/some/file/foo.txt'); // 戻り値 /some/file/foo.yy.txt
359- * $Localizer = new Localizer('zz');
360- * $Localizer->getSpecificFile('/some/file/foo.txt'); // 戻り値 /some/file/foo.xx.txt フォールバックで xx が使われる。
361- * $Localizer = new Localizer('xx');
362- * $Localizer->getSpecificFile('/some/file/bar.txt'); // 戻り値 /some/file/bar.txt 派生版がないのでそのまま
363- * $Localizer->getSpecificFile('/some/file/baz.txt'); // 戻り値 /some/file/baz.txt 存在しない場合もそのまま
355+ * $localizer = new Localizer('xx');
356+ * $localizer->getSpecificFile('/some/file/foo.txt'); // 戻り値 /some/file/foo.xx.txt
357+ * $localizer = new Localizer('yy');
358+ * $localizer->getSpecificFile('/some/file/foo.txt'); // 戻り値 /some/file/foo.yy.txt
359+ * $localizer = new Localizer('zz');
360+ * $localizer->getSpecificFile('/some/file/foo.txt'); // 戻り値 /some/file/foo.xx.txt フォールバックで xx が使われる。
361+ * $localizer = new Localizer('xx');
362+ * $localizer->getSpecificFile('/some/file/bar.txt'); // 戻り値 /some/file/bar.txt 派生版がないのでそのまま
363+ * $localizer->getSpecificFile('/some/file/baz.txt'); // 戻り値 /some/file/baz.txt 存在しない場合もそのまま
364364 *
365365 * 第二引数で基準ディレクトリを示して、第一引数は相対パスで指定したなら、戻り値もそれに従って変化する。
366- * $Localizer = new Localizer('xx');
367- * $Localizer->getSpecificFile('file/foo.txt', '/some'); // 戻り値 file/foo.xx.txt
366+ * $localizer = new Localizer('xx');
367+ * $localizer->getSpecificFile('file/foo.txt', '/some'); // 戻り値 file/foo.xx.txt
368368 *
369369 * param 調べたいパス
370370 * param 調べたいパスが相対である場合は、基準パス。
--- lib/ViewUtil.php (revision 164)
+++ lib/ViewUtil.php (revision 165)
@@ -9,7 +9,7 @@
99 /**
1010 * 引数に指定されたURLパラメータを単一の文字列に変換する。
1111 *
12- * param サイト・アクション名
12+ * param アクション名
1313 * param GETパラメータ。Mojo::getUrl() と同様。
1414 * return パラメータを単一の文字列で表したもの。unserializeParams() で復元できる。
1515 */
@@ -30,7 +30,7 @@
3030 * serializeParams() で文字列化したURLパラメータを復元する。
3131 *
3232 * param 単一の文字列化したパラメータ。serializeParams() の戻り値。
33- * return 復元したパラメータ。サイト・アクションは "@" のキーに含まれている。
33+ * return 復元したパラメータ。アクション名は "@" のキーに含まれている。
3434 */
3535 public static function unserializeParams($serial) {
3636
@@ -112,12 +112,8 @@
112112 if( !preg_match('|^([\w\-\.]+/)?([\w\-\.]+)?(\?.*)?$|', $serial) )
113113 $serial = null;
114114
115- // ないならデフォルトアクションへ。
116- if(!$serial)
117- $serial = Mojo::setting('routing', 'mojo.default_action');
118-
119115 // 復元。
120- return self::unserializeParams($serial);
116+ return self::unserializeParams($serial ?? '');
121117 }
122118
123119 //-----------------------------------------------------------------------------------------------------
--- lib/action/ActionlessView.php (revision 164)
+++ lib/action/ActionlessView.php (nonexistent)
@@ -1,93 +0,0 @@
1-<?php
2-
3-/**
4- * アクションファイルなし・ビューファイルのみでURLを有効化するためのトレイト。これを付けた NotfoundAction をサイトに置けば有効化できる。
5- */
6-trait ActionlessView {
7-
8- private $viewPath;
9-
10- //-----------------------------------------------------------------------------------------------------
11- /**
12- * コンストラクタをオーバーライド。
13- */
14- public function __construct($attributes) {
15-
16- // 先に呼ばないと、パラメータ path.site が埋まらない。
17- parent::__construct($attributes);
18-
19- // ビューファイルがあるのかチェックして、見つけたらアクションパラメータの take.canonical を書き換える。
20- $this->viewPath = $this->findView();
21- }
22-
23- /**
24- * 引数に指定されたアクションパラメータに従ってビューを探す。
25- *
26- * return ビューファイルのパス。見つからない場合は null。
27- */
28- private function findView() {
29-
30- // アクション名をグループ名とモーション名に分割。
31- @[$group, $motion] = explode('.', $this->attributes['take.action']);
32-
33- // 分割できたなら...
34- if($motion) {
35-
36- // そのアクションの標準ビューファイルがあるならそれを使う。
37- $view = $this->searchViewPath($group, $motion);
38- if($view) {
39- $this->attributes['take.canonical'] = sprintf('%s/%s', $this->attributes['take.site'], $this->attributes['take.action']);
40- return $view;
41- }
42-
43- // 分割できない、つまり "." がないなら...
44- }else {
45-
46- // xxxxx.index のビューを探す。
47- $view = $this->searchViewPath($group, 'index');
48- if($view) {
49- $this->attributes['take.canonical'] = sprintf('%s/%s.%s', $this->attributes['take.site'], $group, 'index');
50- return $view;
51- }
52-
53- // default.xxxxx のビューを探す。
54- $view = $this->searchViewPath('default', $group);
55- if($view) {
56- $this->attributes['take.canonical'] = sprintf('%s/%s.%s', $this->attributes['take.site'], 'default', $group);
57- return $view;
58- }
59- }
60-
61- // 見つからない場合は...
62- $this->attributes['take.canonical'] = sprintf('%s/%s', $this->attributes['take.site'], $this->attributes['take.action']);
63- return null;
64- }
65-
66- /**
67- * 引数で指定されたグループ名・アクション名でアクションのないビューを探す。
68- *
69- * param グループ名
70- * param モーション名
71- * return ビューファイルのパス。見つからない場合はnull。
72- */
73- private function searchViewPath($group, $motion) {
74-
75- $view = sprintf('%s/%s/%sView.html', $this->attributes['path.site'], $group, ucfirst($motion));
76- return leaf_exists($view) ? $view : null;
77- }
78-
79- //-----------------------------------------------------------------------------------------------------
80- protected function execute() {
81-
82- // リクエストされているサイト名とアクション名をビューで利用できるようにする。
83- $this->renderer['site'] = $this->attributes['take.site'];
84- $this->renderer['action'] = $this->attributes['take.action'];
85- $this->renderer['name'] = $this->attributes['take.full'];
86-
87- // ビューファイルが見つかっているならそれを使うが、見つかっていないならステータスコードを404にして、このまま NotfoundView を使う。
88- if($this->viewPath)
89- return $this->viewPath;
90- else
91- http_response_code(404);
92- }
93-}
--- lib/action/RecordEditor.php (revision 164)
+++ lib/action/RecordEditor.php (revision 165)
@@ -30,9 +30,8 @@
3030 $this->renderer['record'] = $this->record = $this->retrieveRecord();
3131
3232 // コンストラクタの引数が指定されているレコードであるにも関わらずロードできないのはIDフィクセイション。
33- // CrackingException にしても良いかもだが、ライフハック的にGET変数を指定する場合もあるだろうし、まあ普通に案内するのが筋だろう。
3433 if($this->record->pkey() && $this->record->load() === false)
35- throw new OperationalException('指定されたIDのデータは存在しません。');
34+ throw new IrregularAccess('指定されたIDのデータは存在しません。');
3635
3736 // 規約によりコール。何らかの戻り値が示されているなら従う。
3837 $view = $this->prepareAction();
@@ -59,7 +58,7 @@
5958 /**
6059 * このアクションで取り扱うレコードを返す。ここで返したレコードはビュー変数 record に格納される。
6160 * ただし、コンストラクタの引数を指定して作成したレコードインスタンスを返したときにそのレコードをロードできない場合、後続の処理で
62- * IDフィクセイションと判断され、OperationalException で処理中断される。
61+ * IDフィクセイションと判断され、IrregularAccess 例外で処理中断される。
6362 *
6463 * return MojoRecordインスタンス。
6564 */
--- lib/action/SmartyRenderer.php (revision 164)
+++ lib/action/SmartyRenderer.php (revision 165)
@@ -127,9 +127,8 @@
127127 */
128128 protected function outputFile($file) {
129129
130- // アクションの getViewDirectories() が返すセカンダリディレクトリを取得。普通は、アクションが所属するサイトのディレクトリ。
131- // これをテンプレート処理の基準位置として追加設定する。
132- $dir = $this->action->getViewDirectories()[1];
130+ // アクションが所属するサイトのディレクトリを取得。これをテンプレート処理の基準位置とする。
131+ $dir = $this->action->attributes['path.site'];
133132
134133 // 親テンプレートがある場合は取得しておく。
135134 $parent = @$this->action->attributes['smarty.parent'];
--- lib/exception/OperationalException.php (revision 164)
+++ lib/exception/OperationalException.php (nonexistent)
@@ -1,42 +0,0 @@
1-<?php
2-
3-/**
4- * ユーザの通常操作によってまれに発生しうる例外を表す。例えば次のようなもの。
5- * ・確認画面を長時間放置して、データが期限切れになった後に確定処理しようとした
6- * ・取扱中のデータに他ユーザが同時期にアクセスしており、他ユーザの確定操作のほうが早かったため、データへのアクセス権を失った
7- *
8- * この例外によるエラーはログに記載されない。
9- * ユーザにはエラーコードが表示されない代わり、この例外のコンストラクタにセットされたメッセージが表示される。
10- *
11- * IllegalTouchException と似ているので注意。
12- * IllegalTouchException
13- * ・ユーザがイレギュラーな操作(ブラウザバックとかフォーム送信の二度押しとか)を行うことで発生する
14- * ・ログされる
15- * ・専用のメッセージが表示される(カスタムできない)。
16- * OperationalException
17- * ・通常の操作でもまれに発生する
18- * ・ログされない
19- * ・カスタムしたメッセージを表示できる。
20- *
21- * ユーザに非常に簡易なエラーメッセージを表示するだけで事足りるような用途で用いる。複雑な案内が必要な場合は、個別のアクションでしっかり
22- * エラー処理したほうが良いだろう。
23- */
24-class OperationalException extends Exception {
25-
26- // エラー画面に表示する「戻る」リンクのリンク先パラメータ。
27- public $link;
28-
29- //----------------------------------------------------------------------------------------------------------
30- /**
31- * param ユーザに表示するエラーメッセージ。
32- * param メッセージ表示画面の「戻る」リンクのURLパラメータ。省略した場合は現在のアクションにGETで再度アクセスすることになる。
33- * 文字列を指定した場合は、戻り先アクション名が指定されたものと解釈する。
34- * ApiBehaviorFilterフィルタを使っているアクションでは無視される。
35- */
36- public function __construct($message, $link = ['_here'=>true]) {
37-
38- parent::__construct($message);
39-
40- $this->link = is_array($link) ? $link : array('@'=>$link);
41- }
42-}
--- lib/exception/CrackingException.php (revision 164)
+++ lib/exception/CrackingException.php (nonexistent)
@@ -1,30 +0,0 @@
1-<?php
2-
3-/**
4- * 通常のユーザ操作では考えられない、脆弱性を探るような操作を受けたときに発行する例外。ログにも記録されるし、エラーコードも表示される。
5- * user_info.cracker_index が +1 されるので、確信があるときのみ発行される。複数のタブ・端末で同時アクセスしたりすると発生しうるものはこちらではなく
6- * IllegalTouchException が使われる。
7- */
8-class CrackingException extends Exception {
9-
10- /**
11- * 例外が作成されたときに、user_info.cracker_index を +1 する。
12- */
13- public function __construct($message = "", $code = 0, Exception $previous = null) {
14-
15- parent::__construct($message, $code, $previous);
16-
17- // ユーザIDが判明していて、管理者によるユーザ代行でないなら...
18- $session = Mojo::setting('process', 'mojo.session')::getSession();
19- if($session['user_id'] && !$session['operator_code']) {
20-
21- // そのユーザの cracker_index を +1 する。
22- $user = new UserInfo($session['user_id']);
23- if($user->load()) {
24- $user['cracker_index'] += 1;
25- $user->save();
26- }
27- }
28-
29- }
30-}
--- lib/exception/IllegalTouchException.php (revision 164)
+++ lib/exception/IllegalTouchException.php (nonexistent)
@@ -1,18 +0,0 @@
1-<?php
2-
3-/**
4- * 適切ではない順序でデータを操作しようとしたときに発生する例外。複数のタブ・端末で同時アクセスしたり、ブラウザバックしたりすると発生するもの。
5- * ログにも記録されるし、エラーコードも表示されるが、表示されるエラーメッセージに複数タブによる操作などを戒める内容が追加される。
6- *
7- * OperationalException と似ているので注意。
8- * IllegalTouchException
9- * ・ユーザがイレギュラーな操作(ブラウザバックとかフォーム送信の二度押しとか)を行うことで発生する
10- * ・ログされる
11- * ・専用のメッセージが表示される(カスタムできない)。
12- * OperationalException
13- * ・通常の操作でもまれに発生する
14- * ・ログされない
15- * ・カスタムしたメッセージを表示できる。
16- */
17-class IllegalTouchException extends Exception {
18-}
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
--- lib/exception/IrregularAccess.php (nonexistent)
+++ lib/exception/IrregularAccess.php (revision 165)
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
--- lib/filters/ApiBehaviorFilter.php (revision 164)
+++ lib/filters/ApiBehaviorFilter.php (revision 165)
@@ -42,7 +42,7 @@
4242 $shutter = $action->filterByClass('MaintenanceShutterFilter');
4343 if($shutter) {
4444 $shutter->params[0] = function() use($action) {
45- $action->renderer->params = array('error'=>'maintenance');
45+ $action->renderer->params = array('error'=>['code'=>'maintenance']);
4646 $action->renderer->render();
4747 };
4848 }
@@ -49,7 +49,7 @@
4949
5050 // パラメータ post_only の処理。
5151 if($this->params['post_only'] && $_SERVER['REQUEST_METHOD'] != 'POST') {
52- $this->handleException(new CrackingException('POSTメソッドでアクセスする必要があります。'), $action);
52+ $this->handleException(new ErrorException('POSTメソッドでアクセスする必要があります。'), $action);
5353 return false;
5454 }
5555
@@ -56,7 +56,7 @@
5656 // クライアントが示したバージョンが、サーバー側バージョンと合っていない場合はエラーにする。
5757 if($this->params['check_ver']) {
5858 if( @$_GET['ver'] != AppMojo::getVersionStamp() ) {
59- $action->renderer->params = array('error'=>'version');
59+ $action->renderer->params = array('error'=>['code'=>'version']);
6060 $action->renderer->render();
6161 return false;
6262 }
@@ -69,33 +69,31 @@
6969 */
7070 protected function handleException($e, $action) {
7171
72- $response = array();
72+ $error = array();
7373
74- // ログに記録する。ただしOperationalExceptionは記録しない。
75- if( !($e instanceof OperationalException) )
76- $log = AppLog::exception($e);
74+ // 可能なら、ユーザのエラーカウント+1。
75+ AppMojo::incrementErrorCount();
7776
78- // 例外の種別に応じて、エラーコードをセット。
79- switch(true) {
80- case $e instanceof IllegalTouchException:
81- $response['error'] = 'illtouch';
82- $response['inquiry_code'] = $log['code'];
83- break;
84- case $e instanceof OperationalException:
85- $response['error'] = 'operation';
86- $response['error_message'] = $e->getMessage();
87- break;
88- default:
89- $response['error'] = 'error';
90- $response['inquiry_code'] = $log['code'];
91- }
77+ // ログに記録して問い合わせコードと書き込み内容を得る。
78+ [ $error['inquiry'], $error['description'] ] = AppLog::exception($e);
9279
93- // 本番環境以外ではエラートレースもセットする。
94- if(AppMojo::setting('application', 'environment_type') != 'prod')
95- $response['error_trace'] = Log::stringifyException($e);
80+ // ユーザに表示するメッセージを決定。IrregularAccess なら例外から取得する。
81+ if($e instanceof IrregularAccess)
82+ $error['message'] = $e->getMessage();
9683
84+ // 本番環境では詳細は伝えない。
85+ if(static::setting('application', 'environment_type') == 'prod')
86+ unset($error['description']);
87+
88+ // エラーコードの決定。
89+ $error['code'] = ($e instanceof IrregularAccess) ? 'irregular' : 'exception';
90+
91+ // HTTPエラーコードを設定。
92+ if(!headers_sent())
93+ http_response_code(500);
94+
9795 // 出力。
98- $action->renderer->params = $response;
96+ $action->renderer->params = $error;
9997 $action->renderer->render();
10098 }
10199 }
--- lib/filters/BasicAuthenticationFilter.php (revision 164)
+++ lib/filters/BasicAuthenticationFilter.php (revision 165)
@@ -4,7 +4,7 @@
44 * このフィルタを適用するとBASIC認証が必要になる。
55 *
66 * パラメータ)
7- * passwd パスワードファイルのパス。APP_DIR からの相対パスで指定する。
7+ * passwd パスワードファイルのパス。先頭が "/" で始まっていない場合は APP_DIR からの相対パスとなる。
88 * 省略した場合はアクセス中のサイトディレクトリ直下の .htpasswd となる。
99 * realm realm名。省略すると "realm" になる。
1010 * environments 対象環境を限定するときは指定する。配列で複数指定することも可能。
@@ -31,7 +31,7 @@
3131
3232 // passwd が省略されていた場合はアクションのサイトディレクトリ直下の .htpasswd とする。
3333 if( !isset($this->params['passwd']) )
34- $this->params['passwd'] = sprintf('%s/%s/.htpasswd', Mojo::setting('directories', 'mojo.sites'), $action->attributes['take.site']);
34+ $this->params['passwd'] = sprintf('%s/.htpasswd', $action->attributes['path.site']);
3535 }
3636
3737 //-----------------------------------------------------------------------------------------------------
@@ -84,9 +84,12 @@
8484 if(!$userId)
8585 return false;
8686
87- // パスワードファイルを決定。
88- $passwd = APP_DIR . '/' . $this->params['passwd'];
87+ // パスワードファイルのパスを取得。
88+ $passwd = $this->params['passwd']
89+ if($passwd[0] != '/')
90+ $passwd = APP_DIR . '/' . $passwd;
8991
92+ // 読み込み。
9093 $entries = file($passwd, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
9194
9295 // 1行ずつ照合していく。
--- lib/filters/ParamCheckFilter.php (revision 164)
+++ lib/filters/ParamCheckFilter.php (revision 165)
@@ -52,7 +52,7 @@
5252 // パラメータ rules に従って MojoForm で処理する。検証でエラーになったら例外スロー。
5353 $form = new MojoForm($this->params['rules']);
5454 if( !$form->validate($vars) )
55- throw new CrackingException(sprintf("%s変数の値がルールに適合しない。\nエラー:\n%s\n与えられた値:\n%s", $this->params['vars'], mini_dump($form->errors()), mini_dump($vars)));
55+ throw new ErrorException(sprintf("%s変数の値がルールに適合しない。\nエラー:\n%s\n与えられた値:\n%s", $this->params['vars'], mini_dump($form->errors()), mini_dump($vars)));
5656
5757 // 検証が通ったなら、ルールによる入力補正を適用して終了。
5858 $vars = $form['value'];
--- lib/log/AppLog.php (revision 164)
+++ lib/log/AppLog.php (revision 165)
@@ -80,15 +80,19 @@
8080 * 引数に指定された例外をログファイルに記録する。
8181 *
8282 * param ログに記録したい例外。
83- * param ログ名。
84- * return 次のキーを持つ配列。
85- * code 問い合わせコード。
86- * text 書き込んだ文字列。
83+ * param ログ名。省略した場合は、IrregularAccess なら 'irregular'、それ以外では 'exception' が使われる。
84+ * return 次の2つの要素を持つ序数配列。
85+ * 0 問い合わせコード。
86+ * 1 書き込んだ文字列。
8787 */
88- public static function exception($e, $name = 'exception') {
88+ public static function exception($e, $name=null) {
8989
9090 $now = time();
9191
92+ // ログ名が省略されている場合は、IrregularAccess なら 'irregular'、それ以外では 'exception'。
93+ if( is_null($name) )
94+ $name = ($e instanceof IrregularAccess) ? 'irregular' : 'exception';
95+
9296 // 例外を説明する文字列を作成。
9397 $text = static::stringifyException($e);
9498
@@ -96,7 +100,7 @@
96100 (new static($name))->time($now)->write($text."\n");
97101
98102 // 問い合わせコードを生成して書き込み内容を返す。
99- return ['code'=>static::genErrorCode($now), 'text'=>$text];
103+ return [static::genErrorCode($now), $text];
100104 }
101105
102106 /**
--- lib/tables/AccessLog.php (revision 164)
+++ lib/tables/AccessLog.php (revision 165)
@@ -52,7 +52,7 @@
5252 $access = sprintf('%s:%s:%s?%s',
5353 strtolower($_SERVER['REQUEST_SCHEME']),
5454 strtolower($_SERVER['REQUEST_METHOD']),
55- $action->attributes['take.canonical'],
55+ $action->attributes['action.full'],
5656 self::makeLogString(Mojo::setting('runtime', 'real_get'), $masks)
5757 );
5858
--- mojo/Mojo.php (revision 164)
+++ mojo/Mojo.php (revision 165)
@@ -7,9 +7,10 @@
77 * boot → initialize → (initialize.php)
88 * parseRequestUri → processRoute → processRouteEntry
99 * parsePathInfo
10- * forward → createAction → loadAction → searchAction → openAction → getActionPath
11- * processRedirect (MojoAction->run) (new MojoAction)
12- * processException (MojoAction->wake)
10+ * forward → summonAction → searchAction → openAction → getActionPath
11+ * processNotfound (MojoAction->run) (new MojoAction)
12+ * processRedirect (MojoAction->wake)
13+ * processException
1314 *
1415 * getUrl → processUrl
1516 * genUrl
@@ -117,21 +118,22 @@
117118 // PATH_INFOの正規化、ルーティングの設定などを処理する。
118119 static::parseRequestUri();
119120
120- // PATH_INFO上のGETパラメータの正規化、リクエストされているサイト、グループ・アクションの取得を行う。
121- list($site, $action) = static::parsePathInfo();
121+ // PATH_INFO上のGETパラメータの正規化、リクエストされているサイト、ディレクションの取得を行う。
122+ list($site, $direction) = static::parsePathInfo();
122123
123124 // PATH_INFO上のGETパラメータの処理などが終わったこのタイミングで、これらの変数を取っておく。
124125 static::$settings['runtime']['real_get'] = $_GET;
125126 static::$settings['runtime']['real_post'] = $_POST;
126127
127- // リクエストされているサイト、グループ・アクションを setting() で取得できるようにする。
128- $name = "{$site}/{$action}";
128+ // リクエストされているサイト、ディレクションを setting() で取得できるようにする。
129+ $name = "{$site}/{$direction}";
129130 static::$settings['runtime']['mojo.requested_site'] = $site;
130- static::$settings['runtime']['mojo.requested_action'] = $action;
131+ static::$settings['runtime']['mojo.requested_direction'] = $direction;
131132 static::$settings['runtime']['mojo.requested_name'] = $name;
132133
133- // そのアクションを処理する。
134- static::forward($name);
134+ // そのアクションを処理する。(該当のアクションがないなどで)処理できなかった場合は NotFound 処理。
135+ if( !static::forward($name) )
136+ static::processNotfound($name);
135137 }
136138 // リダイレクトの処理。この中で例外が発生しても外のtryブロックでキャッチできるように二重になっている。
137139 catch(RedirectException $e) {
@@ -141,11 +143,14 @@
141143 // ExitExceptionはスルー。
142144 catch(ExitException $e) {
143145 }
144- // スローされた例外はこちらで処理する。
145- catch(Exception $e) {
146+ // スローされた例外はこちらで処理する。processException() の内部でエラーになった場合はPHP標準の方法で処理される。
147+ catch(Throwable $e) {
146148 try {
147149 static::processException($e);
148150 }
151+ catch(RedirectException $e) {
152+ static::processRedirect($e);
153+ }
149154 catch(ExitException $e) {
150155 }
151156 }
@@ -153,39 +158,43 @@
153158
154159 //-----------------------------------------------------------------------------------------------------
155160 /**
156- * 引数で指定されたアクションを起動する。
161+ * 引数で指定されたアクションを実行する。
157162 *
158- * param サイト・アクション名。サイト名とアクション名を "/" でつなげたもの。
163+ * param アクション名。サイト名とグループ・モーション名を "/" でつなげたもの。
159164 * param アクションに渡したいパラメータがあればここに指定する。
165+ * return 実行に成功したかどうか。
160166 */
161167 public static function forward($name, $attributes = array()) {
162168
163- [$site, $action] = explode('/', $name);
169+ // アクションを起動。失敗したら false リターン。
170+ $action = static::summonAction($name, $attributes);
171+ if( !$action )
172+ return false;
164173
165- // アクションをロード。
166- $action = static::createAction($site, $action, $attributes);
167- if( !($action instanceof MojoAction) )
168- throw new ErrorException(sprintf('%s クラスをMojoActionから派生させてください。', get_class($action)));
169-
170174 // アクションスタックに登録。
171175 array_push(static::$actionStack, $action);
172176
173- // アクションを起動。
177+ // アクションを実行。
174178 $action->run();
175179
176180 // アクションスタックから削除。
177181 array_pop(static::$actionStack);
182+
183+ return true;
178184 }
179185
180186 //-----------------------------------------------------------------------------------------------------
181187 /**
182188 * 基本的に forward() と同じだが、出力をそのまま出力せずに戻り値として取得するところが異なる。
189+ * アクションを実行できなかった場合は null が返る。
183190 */
184191 public static function bring($name, $attributes = array()) {
185192
186193 ob_start();
187- static::forward($name, $attributes);
188- return ob_get_clean();
194+ $result = static::forward($name, $attributes);
195+ $ret = ob_get_clean();
196+
197+ return $result ? $ret : null;
189198 }
190199
191200 //-----------------------------------------------------------------------------------------------------
@@ -221,13 +230,13 @@
221230 /**
222231 * 引数に指定されたアクションを指すURLを生成する。
223232 *
224- * param アクション指定。"/controller/site/action" の形式で指定する。各区画は先頭から省略できる。
233+ * param アクション指定。"/controller/site/direction" の形式で指定する。各区画は先頭から省略できる。
225234 * 例)
226- * action 現在のフロントコントローラーとサイトで、action のURLとなる。
227- * site/action 現在のフロントコントローラーで、site/action のURLとなる。
228- * controller/site/action 現在のフロントコントローラーの場所で、controller.php/site/action のURLとなる。
229- * /controller/site/action /controller.php/site/action のURLとなる。
230- * 空文字を指定した場合は現在のサイト・アクションとなる。
235+ * direction 現在のフロントコントローラーとサイトで、direction のURLとなる。
236+ * site/direction 現在のフロントコントローラーで、site/direction のURLとなる。
237+ * controller/site/direction 現在のフロントコントローラーの場所で、controller.php/site/direction のURLとなる。
238+ * /controller/site/direction /controller.php/site/direction のURLとなる。
239+ * 空文字を指定した場合は現在のアクションとなる。
231240 * param GETパラメータを表す連想配列。
232241 * 以下のキーは特殊な意味で扱われる。
233242 * _self trueにすると、現在のURLからパラメータが取得される。
@@ -253,7 +262,7 @@
253262 /**
254263 * getUrl() で行うパラメータ正規化処理。
255264 *
256- * param getUrl() に渡された第一引数。サイト名とアクション名を "/" でつなげた文字列を返す。
265+ * param getUrl() に渡された第一引数。アクションを指定する文字列。
257266 * param getUrl() に渡された第二引数。GETパラメータを表す連想配列を返す。
258267 * return 使用するフロントコントローラーへのURL。
259268 */
@@ -290,17 +299,17 @@
290299 // 前方部分が省略されている場合は null を補う。これで要素数は 3 に固定される。
291300 $compartments = array_slice(array_merge(array(null, null, null), $compartments), -3);
292301
293- // サイト名、アクション名が省略されている場合はそれぞれリクエストされている値を使う。
302+ // サイト名、ディレクション名が省略されている場合はそれぞれリクエストされている値を使う。
294303 if( empty($compartments[1]) )
295304 $compartments[1] = static::setting('runtime', 'mojo.requested_site');
296305 if( empty($compartments[2]) )
297- $compartments[2] = static::setting('runtime', 'mojo.requested_action');
306+ $compartments[2] = static::setting('runtime', 'mojo.requested_direction');
298307
299308 // サイト名がデフォルトサイトと一致するなら "-" とする。
300309 if($compartments[1] == static::setting('routing', 'mojo.default_site'))
301310 $compartments[1] = '-';
302311
303- // サイト・アクション名を作成。
312+ // アクション名を作成。
304313 $name = $compartments[1] . '/' . $compartments[2];
305314
306315 // 使用するフロントコントローラーへのURLを返す。この一文で、省略されている場合と指定されているの場合の双方に対応している。
@@ -312,7 +321,7 @@
312321 * 引数リストはgetUrl()と同じだが、_self などの特殊キーはすでに処理されており、引数の意味も固定されている。
313322 *
314323 * param 使用するフロントコントローラーへのURL。
315- * param サイト・アクション名
324+ * param アクション名
316325 * param GETパラメータ。ただし、"#" はURLフラグメントとして処理される。
317326 * param GETパラメータをクエリストリングではなくパスの一部としたい場合はtrueを指定する。
318327 * return 生成したURL
@@ -372,10 +381,10 @@
372381 // 各パートを個別に取得。
373382 $components = explode('/', $apppath);
374383
375- // アクション名がデフォルトと一致しているなら...
376- if( $components[3] == static::setting('routing', 'mojo.default_action') ) {
384+ // ディレクション名がデフォルトと一致しているなら...
385+ if( $components[3] == static::setting('routing', 'mojo.default_direction') ) {
377386
378- // アクション名は省略できる。
387+ // ディレクション名は省略できる。
379388 array_pop($components);
380389
381390 // さらにサイト名が "-" になっているなら...
@@ -723,7 +732,7 @@
723732
724733 // ルーティング設定を取得。
725734 $routes = static::setting('routing');
726- unset($routes['mojo.default_site'], $routes['mojo.default_action'], $routes['mojo.notfound_action'], $routes['mojo.default_site.cli']);
735+ unset($routes['mojo.default_site'], $routes['mojo.default_direction'], $routes['mojo.notfound_direction'], $routes['mojo.default_site.cli']);
727736
728737 // ルーティング設定を一つずつ見ていく。一致したルールを検出したらそこまでとする。
729738 foreach($routes as $route => $arrow) {
@@ -787,10 +796,9 @@
787796
788797 //-----------------------------------------------------------------------------------------------------
789798 /**
790- * PATH_INFO に埋め込まれたGETパラメータの処理と、リクエストされているサイト、グループ・モーションの
791- * 抽出を行う。
799+ * PATH_INFO に埋め込まれたGETパラメータの処理と、リクエストされているサイト、ディレクションの抽出を行う。
792800 *
793- * return 第0要素にサイト、第1要素にアクション(グループ・モーション)の名前を格納した配列。
801+ * return 第0要素にサイト、第1要素にディレクションの名前を格納した配列。
794802 */
795803 protected static function parsePathInfo() {
796804
@@ -800,8 +808,8 @@
800808 });
801809 $fields = array_values($fields);
802810
803- // サイト部分とアクション部分を取得して除去する。
804- [$site, $action, $fields] = static::pickoutActionFields($fields);
811+ // サイト部分とディレクション部分を取得して除去する。
812+ [$site, $direction, $fields] = static::pickoutSummonFields($fields);
805813
806814 // サイトが省略されている場合は routing - mojo.default_site の値を使う。
807815 if(!$site || $site == '-') {
@@ -817,9 +825,9 @@
817825 $site = static::setting('routing', 'mojo.default_site');
818826 }
819827
820- // アクションが省略されている場合は routing - mojo.default_action の値を使う。
821- if(!$action || $action == '-')
822- $action = static::setting('routing', 'mojo.default_action');
828+ // ディレクションが省略されている場合は routing - mojo.default_direction の値を使う。
829+ if(!$direction || $direction == '-')
830+ $direction = static::setting('routing', 'mojo.default_direction');
823831
824832 // URIパスに埋め込まれている変数を $_GET に設定する。
825833 foreach($fields as $field) {
@@ -841,17 +849,17 @@
841849 // このとき、$_REQUEST にすでに存在するキーを上書きしないようにする。
842850 $_REQUEST += $_GET;
843851
844- // サイト名とアクション名を返す。
845- return [$site, $action];
852+ // サイト名とディレクション名を返す。
853+ return [$site, $direction];
846854 }
847855
848856 /**
849- * 指定されたPATH_INFOフィールドの配列からサイト部分とアクション部分を取得、除去して返す。
857+ * 指定されたPATH_INFOフィールドの配列からサイト部分とディレクション部分を取得、除去して返す。
850858 *
851859 * param PATH_INFOを "/" で区切った配列
852- * return [サイト指定, アクション指定, 残りのフィールド] の3要素を含む配列。
860+ * return [サイト名, ディレクション名, 残りのフィールド] の3要素を含む配列。
853861 */
854- protected static function pickoutActionFields($fields) {
862+ protected static function pickoutSummonFields($fields) {
855863
856864 $pair = [];
857865
@@ -861,11 +869,11 @@
861869 // GET変数として指定されているフィールドはスキップ。
862870 if(strpos($field, '=') !== false) continue;
863871
864- // サイト・アクション指定として取得する。このとき、特定の文字以外は除去する。
872+ // サイト・ディレクション名として取得する。このとき、特定の文字以外は除去する。
865873 $pair[] = preg_replace('/[^\w\-\.]/', '', $field);
866874 unset($fields[$i]);
867875
868- // サイト・アクションの2つが取得できたらそこまで。
876+ // サイト・ディレクションの2つが取得できたらそこまで。
869877 if(2 <= count($pair)) break;
870878 }
871879
@@ -887,7 +895,7 @@
887895
888896 //-----------------------------------------------------------------------------------------------------
889897 /**
890- * 引数に指定された例外を処理する。
898+ * 引数に指定された例外を処理する。ここから例外をスローした場合はPHP標準の方法で処理される。
891899 *
892900 * param 発生した例外。
893901 */
@@ -899,92 +907,85 @@
899907
900908 //-----------------------------------------------------------------------------------------------------
901909 /**
902- * 引数で指定された名前のサイト・アクションをロードして、インスタンスを作成して返す。
910+ * リクエストされたアクションが見付からない場合の処理を行う。
903911 *
904- * param サイト名
905- * param アクション名(グループ・モーション名)
906- * param アクションに渡すパラメータ
907- * return アクションインスタンス
912+ * param リクエストされたアクション名。
908913 */
909- protected static function createAction($site, $action, $attributes) {
914+ protected static function processNotfound($name) {
910915
911- // アクションクラスをロードして、そのクラス名とファイルパスを取得する。得られなかったらエラー。
912- $result = static::loadAction($site, $action);
913- if(!$result) {
914- http_response_code(404);
915- throw new ErrorException("アクションクラスが見付からない。(site:{$site}, action:{$action})");
916- }
917-
918- // インスタンスの基本パラメータを作成。
919- $params = array(
920- 'name.site'=>$result[2][0], 'name.group'=>$result[2][1], 'name.motion'=>$result[2][2],
921- 'take.site'=>$site, 'take.action'=>$action,
922- 'path.site'=>null, 'path.action'=>$result[1],
923- );
924-
925- // インスタンスを作成。
926- $action = new $result[0]($params + (array)$attributes);
927- $action->wake();
928- return $action;
916+ http_response_code(404);
917+ echo "$name を処理するアクションクラスが見付からない。\n";
929918 }
930919
931920 //-----------------------------------------------------------------------------------------------------
932921 /**
933- * 引数で指定された名前のサイト・アクションのクラスをロードして、クラス名と定義されているファイルパスを返す。
934- * notfound アクションなどのフォールバックも考慮される。
922+ * 引数で指定された名前のアクションのインスタンスを作成・起動して、返す。
935923 *
936- * param サイト名
937- * param アクション名(グループ・モーション名)
938- * return 次の2要素を格納する序数配列。ロードできなかった場合は null。
939- * 要素0 アクションクラス名
940- * 要素1 アクションクラスが定義されているファイルのパス
941- * 要素2 ロードしたアクションの [サイト名, グループ名, モーション名] の配列
924+ * param アクション名。サイト名とディレクション名を "/" でつなげたもの。
925+ * param アクションに渡すパラメータ
926+ * return アクションインスタンス。指定のアクションが見付からない、あるいは起動できないなら null。
942927 */
943- protected static function loadAction($site, $action) {
928+ protected static function summonAction($name, $attributes): ?MojoAction {
944929
945- // まずは通常通り探す。
946- $result = static::searchAction($site, $action);
947- if($result)
948- return $result;
930+ // サイト名とディレクション名を分離する。
931+ [$site, $direction] = explode('/', $name);
949932
950- // なかった場合は Notfound アクションを探す。
951- $notfound = static::setting('routing', 'mojo.notfound_action');
952- $result = static::searchAction($site, $notfound);
953- if($result)
954- return $result;
933+ // 指定されたアクションを探してインスタンス化。
934+ $search = static::searchAction($site, $direction);
935+ if($search) {
955936
956- // ロードできない場合は null を返す。
957- return null;
937+ // インスタンスの基本パラメータを作成。
938+ $params = array(
939+ 'action.summon'=>$name, 'path.action'=>$search[1],
940+ 'action.site'=>$search[2][0], 'action.group'=>$search[2][1], 'action.motion'=>$search[2][2],
941+ );
942+
943+ $action = new $search[0]($params + $attributes);
944+
945+ // 見付からない場合はアクションレス・ビューを探索するアクションを作成する。
946+ }else {
947+
948+ // インスタンスの基本パラメータを作成。
949+ $params = array(
950+ 'action.summon'=>$name, 'path.action'=>null,
951+ 'action.site'=>$site, 'action.group'=>null, 'action.motion'=>null,
952+ );
953+
954+ $action = new ActionlessViewAction($params + $attributes);
955+ }
956+
957+ // インスタンスを作成。
958+ return $action->wake() ? $action : null;
958959 }
959960
960961 //-----------------------------------------------------------------------------------------------------
961962 /**
962- * 関数定義はloadActionと同じだが、notfound アクションなどのフォールバックは考慮されない。指定されたアクションに対する処理を行うのみ。
963+ * 引数で指定された名前のサイト・ディレクションのクラスをロードして、クラス名と定義されているファイルパスを返す。
963964 *
964965 * param サイト名
965- * param アクション名(グループ・モーション名)
966+ * param ディレクション名(グループ・モーション名)
966967 * return 次の2要素を格納する序数配列。ロードできなかった場合は null。
967968 * 要素0 アクションクラス名
968969 * 要素1 アクションクラスが定義されているファイルのパス
969970 * 要素2 見つけたアクションの [サイト名, グループ名, モーション名] の配列
970971 */
971- protected static function searchAction($site, $action) {
972+ protected static function searchAction($site, $direction) {
972973
973- // アクション名を "." で分ける。
974- @[$group, $action] = explode('.', $action);
974+ // ディレクション名を "." で分ける。
975+ @[$group, $motion] = explode('.', $direction);
975976
976- // "." があるなら $group/$action か default/$group$action のどちらか。
977- if($action)
978- return static::openAction($site, $group, $action) ?: static::openAction($site, 'default', ucfirst($group).ucfirst($action));
977+ // "." があるなら簡単なのだが、
978+ if($motion)
979+ return static::openAction($site, $group, $motion);
979980
980981 // "." がない場合は、"xxxxx.index" を探して、それもない場合は "default.xxxxx" を探す。
981- return static::openAction($site, $group, 'index') ?: static::openAction($site, 'default', $group);
982+ else
983+ return static::openAction($site, $group, 'index') ?: static::openAction($site, 'default', $group);
982984 }
983985
984986 //-----------------------------------------------------------------------------------------------------
985987 /**
986- * 引数で指定されたサイト名・グループ名・モーション名で、アクションクラスが記述されているファイルを探し、
987- * アクションクラスの定義を読み込む。
988+ * 引数で指定されたサイト名・グループ名・モーション名で、アクションクラスが記述されているファイルを探し、アクションクラスの定義を読み込む。
988989 *
989990 * param サイト名
990991 * param グループ名
@@ -999,8 +1000,7 @@
9991000 // アクションファイル名を決定。先頭は大文字。
10001001 $file = ucfirst($motion) . 'Action';
10011002
1002- // サイト名・グループ名・アクションファイル名を指定して、アクションクラスが記述されているファイルを見つける。
1003- // 見つからなかったらロードは出来ない。
1003+ // サイト名・グループ名・アクションファイル名から、そのファイルを見つける。見つからなかったらロードは出来ない。
10041004 $path = static::getActionPath($site, $group, $file);
10051005 if(!$path)
10061006 return null;
@@ -1026,7 +1026,7 @@
10261026
10271027 //-----------------------------------------------------------------------------------------------------
10281028 /**
1029- * 引数で指定されたサイト名・グループ名・アクションファイル名から、アクションクラスが記述されているファイルを探す。
1029+ * 引数で指定されたサイト名・グループ名・アクションファイル名から、該当のファイルを探す。
10301030 *
10311031 * param サイト名
10321032 * param グループ名
--- mojo/MojoAction.php (revision 164)
+++ mojo/MojoAction.php (revision 165)
@@ -5,69 +5,34 @@
55 */
66 class MojoAction {
77
8- // アクションに渡されたパラメータを表す連想配列。どんなキーがあるかは運用によって様々だが、次のキーは標準的に定義される。
9- // name.site
10- // name.group
11- // name.motion
12- // このアクションのサイト名、グループ名、モーション名。リクエスト時に省略されていてもセットされる。
13- // name.full
14- // 上記3つを "site/group.motion" の形でつなげたもの。
15- // take.site
16- // take.action
17- // このアクションが処理しているサイト名とアクション名。
18- // NotfoundAction などもあるので、この名前は自身のクラス名や所属サイト名とは必ずしも一致しない。
19- // また、Mojo::forward() で転送されている場合もあるので、mojo.requested_site, mojo.requested_action とも必ずしも一致しない。
20- // take.full
21- // 上記2つを "site/action" の形でつなげたもの。
22- // take.canonical
23- // name.full と同様だが、アクションレスの NotfoundAction などでは代替したアクションのサイト名・アクション名がセットされる。
24- // path.action
25- // このアクションが定義されているファイルのパス。イレギュラーな場所にあるなら null。
26- // null の場合、ビューテンプレートなどの関連ファイルの探索パスが減る。
27- // path.site
28- // このアクションが所属するサイトのディレクトリ。どのサイトにも所属しないなら null。
29- // null の場合、ビューテンプレートなどの関連ファイルの探索パスが減る。
30- //
31- // 例)
32- // "site1/motion1" がリクエストされ、"site1/default.motion1" が受けた場合、次のようになる。
33- //
34- // mojo.requested_site site1
35- // mojo.requested_action motion1
36- // mojo.requested_name site1/motion1
37- // name.site site1
38- // name.group default
39- // name.motion motion1
40- // name.full site1/default.motion1
41- // take.site site1
42- // take.action motion1
43- // take.full site1/motion1
44- // take.canonical site1/default.motion1
45- //
46- // 例)
47- // "site1/group1.motion1" がリクエストされ、"site2/group2" へforward()されたところアクションがないため "site2/default.notfound" が受けて
48- // "site2/group2.index" に該当するビューを使用した場合、次のようになる。
49- //
50- // mojo.requested_site site1
51- // mojo.requested_action group1.motion1
52- // mojo.requested_name site1/group1.motion1
53- // name.site site2
54- // name.group default
55- // name.motion notfound
56- // name.full site2/default.notfound
57- // take.site site2
58- // take.action group2
59- // take.full site2/group2
60- // take.canonical site2/group2.index
61- //
62- // つまり、requested_* は常にリクエストされたアクションを、name.* は常に自分を、take.* はforward()に渡された命題を指している。
63- // ※ mojo.requested_* はアクションの属性ではなく、Mojo::setting('runtime', ...) で取得するものなので注意。
64- //
8+ /**
9+ * アクションに渡されたパラメータを表す連想配列。どんなキーがあるかは運用によって様々だが、次のキーは標準的に定義される。
10+ * action.site
11+ * action.group
12+ * action.motion
13+ * このアクションのサイト名、グループ名、モーション名。リクエスト時に省略されていてもセットされる。
14+ * action.full
15+ * 上記3つを "site/group.motion" の形でつなげたもの。
16+ * action.summon
17+ * アクションが呼び出された時(Mojo::forward()がコールされた時)に指定されたアクション名。
18+ * 基本的に action.full と同じだが、"default" や "index" が省略されている可能性がある。
19+ * path.action
20+ * このアクションが定義されているファイルのパス。イレギュラーな場所にあるなら null。
21+ * null の場合、ビューテンプレートなどの関連ファイルの探索パスが減る。
22+ * path.site
23+ * このアクションが所属するサイトのディレクトリ。どのサイトにも所属しないなら null。
24+ * null の場合、ビューテンプレートなどの関連ファイルの探索パスが減る。
25+ */
6526 public $attributes = array();
6627
67- // アクションの出力処理を担当するレンダラクラス。"default" の場合は settings.php の process - mojo.renderer の値が使われる。
28+ /**
29+ * アクションの出力処理を担当するレンダラクラス。"default" の場合は settings.php の process - mojo.renderer の値が使われる。
30+ */
6831 public $renderClass = 'default';
6932
70- // renderClass の設定をもとに作成されたレンダラインスタンス。
33+ /**
34+ * renderClass の設定をもとに作成されたレンダラ・インスタンス。
35+ */
7136 public $renderer;
7237
7338 //-----------------------------------------------------------------------------------------------------
@@ -76,29 +41,16 @@
7641 *
7742 * param このアクションのパラメータ。attributes メンバ変数にセットされる。
7843 * どんなキーが必要かはアクション次第だが、メンバ変数 $attributes で説明しているキーは必ず必要になる。
79- * ただし path.site, name.full, take.full, take.canonical は推測するので不要。
44+ * ただし action.full, path.site は推測するので不要。
8045 */
8146 public function __construct($attributes) {
8247
83- // path.site が省略されている場合は path.action から推測してみる。
84- if(!isset($attributes['path.site']) && isset($attributes['path.action'])) {
48+ // 省略されている値を取得する。
49+ if( !isset($attributes['path.site']) )
50+ $attributes['path.site'] = Mojo::getDirectory('mojo.sites') . '/' . $attributes['action.site'];
51+ if( !isset($attributes['action.full']) )
52+ $attributes['action.full'] = sprintf('%s/%s.%s', $attributes['action.site'], $attributes['action.group'], $attributes['action.motion']);
8553
86- $sites = Mojo::getDirectory('mojo.sites');
87-
88- if(strpos($attributes['path.action'], $sites) === 0) {
89- $sites = preg_quote($sites, '#');
90- $attributes['path.site'] = preg_replace("#^({$sites}/[^/]+)/.*#", '$1', $attributes['path.action']);
91- }
92- }
93-
94- // name.full, take.full, take.canonical は他の値から取得できる。
95- if( !isset($attributes['name.full']) )
96- $attributes['name.full'] = sprintf('%s/%s.%s', $attributes['name.site'], $attributes['name.group'], $attributes['name.motion']);
97- if( !isset($attributes['take.full']) )
98- $attributes['take.full'] = sprintf('%s/%s', $attributes['take.site'], $attributes['take.action']);
99- if( !isset($attributes['take.canonical']) )
100- $attributes['take.canonical'] = $attributes['name.full'];
101-
10254 // 渡されたパラメータを attributes メンバ変数で保持する。
10355 $this->attributes = array_replace($this->attributes, $attributes);
10456 }
@@ -155,10 +107,12 @@
155107 $result = array();
156108
157109 // プライマリ
158- $result[] = dirname(@$this->attributes['path.action']);
110+ if( isset($this->attributes['path.action']) )
111+ $result[] = dirname($this->attributes['path.action']);
159112
160113 // セカンダリ
161- $result[] = @$this->attributes['path.site'];
114+ if( isset($this->attributes['path.site']) )
115+ $result[] = $this->attributes['path.site'];
162116
163117 // ターシャリ
164118 $result[] = Mojo::getDirectory('mojo.sites');
@@ -365,11 +319,13 @@
365319
366320 //-----------------------------------------------------------------------------------------------------
367321 /**
368- * アクションが作成された時、コンストラクタの最後に呼ばれる。
369- * 末端のアクションクラスがその処理を記述するという位置づけなので、基本的に parent::wake() を呼ぶ必要はない。
370- * なので、末端のアクション以外ではオーバーライドしないのが原則。
322+ * アクションが作成された時、コンストラクタの直後に呼ばれる。
323+ *
324+ * return アクションの処理が可能かどうか。falseを返すと run() が呼ばれずこのアクションは処理不能として扱われる。
371325 */
372326 public function wake() {
327+
328+ return true;
373329 }
374330
375331 //-----------------------------------------------------------------------------------------------------
@@ -404,3 +360,90 @@
404360 protected function finish($view) {
405361 }
406362 }
363+
364+//==========================================================================================================
365+/**
366+ * 比較的単純な画面表示のみなど、ビューのみ準備すれば事足りるようなアクションでアクションクラスを省略できるようにするアクション。
367+ * Mojo::forward() などで、指定のアクションが見付からない場合に使用される。
368+ */
369+class ActionlessViewAction extends MojoAction {
370+
371+ private $viewPath;
372+
373+ //-----------------------------------------------------------------------------------------------------
374+ /**
375+ * オーバーライド。
376+ * アクションが作成された時、コンストラクタの直後に呼ばれる。
377+ */
378+ public function wake() {
379+
380+ // ビューファイルがあるのかチェック。
381+ $this->viewPath = $this->findView();
382+
383+ // ビューが見付からない場合は処理できない。
384+ return (bool)$this->viewPath;
385+ }
386+
387+ /**
388+ * アクションパラメータに従ってビューを探す。見つけたらアクションパラメータの action.group, action.motion, action.full を書き換える。
389+ *
390+ * return ビューファイルのパス。見つからない場合は null。
391+ */
392+ private function findView() {
393+
394+ // forward() で指定されたアクション名からグループ名とモーション名を取得。
395+ [$_, $direction] = explode('/', $this->attributes['action.summon']);
396+ @[$group, $motion] = explode('.', $direction);
397+
398+ // モーション名まで指定されている場合。
399+ if($motion) {
400+
401+ // そのアクションの標準ビューファイルがあるならそれを使う。
402+ $view = $this->searchViewPath($group, $motion);
403+
404+ // モーション名まで指定されていない場合。
405+ }else {
406+
407+ // xxxxx.index のビューを探す。
408+ $motion = 'index';
409+ $view = $this->searchViewPath($group, $motion);
410+
411+ // 見付からないなら default.xxxxx のビューを探す。
412+ if( !$view ) {
413+ $motion = $group;
414+ $group = 'default';
415+ $view = $this->searchViewPath($group, $motion);
416+ }
417+ }
418+
419+ // 見つかったなら、アクションパラメータもそれに合わせて修正しておく。
420+ if($view) {
421+ $this->attributes['action.group'] = $group;
422+ $this->attributes['action.motion'] = $motion;
423+ $this->attributes['action.full'] = sprintf('%s/%s.%s', $this->attributes['action.site'], $group, $motion);
424+ }
425+
426+ return $view;
427+ }
428+
429+ /**
430+ * 引数で指定されたグループ名・アクション名でアクションのないビューを探す。
431+ *
432+ * param グループ名
433+ * param モーション名
434+ * return ビューファイルのパス。見つからない場合はnull。
435+ */
436+ private function searchViewPath($group, $motion) {
437+
438+ $view = sprintf('%s/%s/%sView.html', $this->attributes['path.site'], $group, ucfirst($motion));
439+ return leaf_exists($view) ? $view : null;
440+ }
441+
442+ //-----------------------------------------------------------------------------------------------------
443+ protected function execute() {
444+
445+ $this->renderer['actionpars'] = $this->attributes;
446+
447+ return $this->viewPath;
448+ }
449+}
--- mojo/MojoRenderer.php (revision 164)
+++ mojo/MojoRenderer.php (revision 165)
@@ -3,6 +3,9 @@
33 /**
44 * アクションの出力処理を担当するクラス(レンダラ)。
55 * 普通、一つのレンダラインスタンスは一つのアクションインスタンスに紐づく。一つのインスタンスで複数のアクションを処理したりすることはない。
6+ *
7+ * 主なメソッドフローは次の通り。
8+ * render → output
69 */
710 class MojoRenderer implements ArrayAccess {
811
@@ -124,6 +127,10 @@
124127 //=========================================================================================================
125128 /**
126129 * ビューとしてテンプレートファイルを伴うレンダラ実装。
130+ *
131+ * 主なメソッドフローは次の通り。
132+ * render → output → getViewFile
133+ * outputFile
127134 */
128135 class TemplateRenderer extends MojoRenderer {
129136
@@ -144,8 +151,11 @@
144151
145152 // ビューファイルを取得。見付からない場合はエラー。
146153 $file = $this->getViewFile($view);
147- if(!$file)
148- throw new ErrorException(sprintf('ビューファイルが見付からない。(site:%s, action:%s, view:%s)");', $this->action->attributes['name.site'], get_class($this->action), $view));
154+ if(!$file) {
155+ throw new ErrorException(sprintf(
156+ 'ビューファイル "%s" が見付からない。action:%s(%s)");', $view, get_class($this->action), $this->action->attributes['action.summon']
157+ ));
158+ }
149159
150160 // ビューを出力。
151161 $this->outputFile($file);
@@ -189,13 +199,11 @@
189199 // アクションからテンプレート検索ディレクトリを取得、一つずつ見ていく。
190200 $dirs = $this->action->getViewDirectories();
191201 foreach($dirs as $dir) {
192- if($dir) {
193202
194- // テンプレートファイルを見つけたらリターン。
195- $path = $dir . '/' . $view;
196- if( file_exists($path) && is_file($path) )
197- return $path;
198- }
203+ // テンプレートファイルを見つけたらリターン。
204+ $path = $dir . '/' . $view;
205+ if( file_exists($path) && is_file($path) )
206+ return $path;
199207 }
200208
201209 // ここまで来ると分からない。
@@ -222,8 +230,12 @@
222230 //=========================================================================================================
223231 /**
224232 * レンダリングエンジンとしてPHPをそのまま使う実装。
225- * TemplateRenderer もそうなっているが、こちらはレイアウトファイルを指定できるなど、すこしだけリッチに
226- * なっている。
233+ * TemplateRenderer もそうなっているが、こちらはレイアウトファイルを指定できるなど、すこしだけリッチになっている。
234+ *
235+ * 主なメソッドフローは次の通り。
236+ * render → output → getViewFile
237+ * getLayoutFile
238+ * outputFile
227239 */
228240 class PhpRenderer extends TemplateRenderer {
229241
@@ -250,8 +262,11 @@
250262
251263 // レイアウトファイルを取得。見付からない場合はエラー。
252264 $file = $this->getLayoutFile();
253- if(!$file)
254- throw new ErrorException(sprintf('レイアウトファイルが見付からない。(site:%s, action:%s, layout:%s)");', $this->action->attributes['name.site'], get_class($this->action), $this->layout));
265+ if(!$file) {
266+ throw new ErrorException(sprintf(
267+ 'レイアウトファイルが "%s" が見付からない。action:%s(%s)");', $this->layout, get_class($this->action), $this->action->attributes['action.summon']
268+ ));
269+ }
255270
256271 // レイアウトを出力。
257272 $this->outputFile($file);
@@ -272,17 +287,14 @@
272287 // レイアウトファイルの名前を取得。
273288 $name = $this->layout . '.' . $this->templateExt;
274289
275- // アクションからテンプレート検索ディレクトリを取得、セカンダリから一つずつ見ていく。
290+ // アクションからテンプレート検索ディレクトリを取得、一つずつ見ていく。
276291 $dirs = $this->action->getViewDirectories();
277- array_shift($dirs);
278292 foreach($dirs as $dir) {
279- if($dir) {
280293
281- // レイアウトファイルを見つけたらリターン。
282- $path = $dir . '/' . $name;
283- if( file_exists($path) && is_file($path) )
284- return $path;
285- }
294+ // レイアウトファイルを見つけたらリターン。
295+ $path = $dir . '/' . $name;
296+ if( file_exists($path) && is_file($path) )
297+ return $path;
286298 }
287299
288300 // ここまで来ると分からない。
@@ -294,8 +306,8 @@
294306 //=========================================================================================================
295307 /**
296308 * 出力を行わないレンダラ。
297- * 一定数のアクションの共通処理として出力を行いたくない場合に使う。個別のアクションで出力したくないだけなら
298- * これを使わずとも execute() で "none" を返せばよい。
309+ * 一定数のアクションの共通処理として出力を行いたくない場合に使う。個別のアクションで出力したくないだけならこれを使わずとも
310+ * execute() で "none" を返せばよい。
299311 */
300312 class NullRenderer extends MojoRenderer {
301313
--- mojo/readme.txt (revision 164)
+++ mojo/readme.txt (revision 165)
@@ -1,5 +1,5 @@
11
2-●URLとサイト、グループ、モーション
2+●URLとアクション
33
44 ○ルーティング
55
@@ -13,20 +13,12 @@
1313
1414 クラス名は BazAction でも良いのだが、あまり凡庸な名前だと他と被ることになるかも。
1515
16- ○GETパラメータ
16+ ○用語
1717
18- $_GETで参照できるクエリパラメータを付けたい場合は、普通に次のように出来る。
18+ |-- アクション名 --|
19+ http://sample.com/index.php/[サイト名]/[グループ名].[モーション名]
20+ |-- ディレクション名 --|
1921
20- http://sample.com/index.php/site/group.action?key1=val1&key2=val2&key3=val3&key4=val4
21-
22- 次のようにもできる。
23-
24- http://sample.com/index.php/site/group.action/key1=val1/key2=val2?key3=val3&key4=val4
25-
26- "=" が含まれているフィールドはGET変数の指定と解釈するので、次のようにもできる。
27-
28- http://sample.com/index.php/key1=val1/site/group.action/key2=val2?key3=val3&key4=val4
29-
3022 ○省略
3123
3224 グループ名 default なら省略できる。
@@ -52,7 +44,7 @@
5244
5345 ○デフォルト
5446
55- 次のようにアクション部分を省略し、設定ファイルの routing - mojo.default_action の値を使うことが出来る。
47+ 次のようにディレクション部分を省略し、設定ファイルの routing - mojo.default_direction の値を使うことが出来る。
5648
5749 http://sample.com/index.php/user
5850
@@ -65,18 +57,20 @@
6557
6658 /-/foo.bar
6759
68- ○グループが存在しない場合
60+ ○GETパラメータ
6961
70- 基本的に
62+ $_GETで参照できるクエリパラメータを付けたい場合は、普通に次のように出来る。
7163
72- sites/サイト/グループ/モーションAction.php
64+ http://sample.com/index.php/site/group.motion?key1=val1&key2=val2&key3=val3&key4=val4
7365
74- となるが、見つからない場合は
66+ 次のようにもできる。
7567
76- sites/サイト/default/グループモーションAction.php
68+ http://sample.com/index.php/site/group.motion/key1=val1/key2=val2?key3=val3&key4=val4
7769
78- も探索される。
70+ "=" が含まれているフィールドはGET変数の指定と解釈するので、次のようにもできる。
7971
72+ http://sample.com/index.php/key1=val1/site/group.motion/key2=val2?key3=val3&key4=val4
73+
8074 ●フィルタ
8175
8276 各アクションに事前処理と事後処理を行う「フィルタ」を設定できる。
@@ -119,7 +113,7 @@
119113 ●コマンドラインでの実行
120114
121115 php コマンドに与えるスクリプトパスとしてフロントコントローラーを与える。普通はルートにある mojo.php が良いだろう。
122- サイトとアクションは第一引数として与える。example/test を実行したいなら次のようにする。
116+ アクション名は第一引数として与える。example/test を実行したいなら次のようにする。
123117
124118 php /foo/bar/mojo.php example/test
125119
@@ -159,7 +153,6 @@
159153 BasicAuthenticationFilter.php アクションにBASIC認証をかけるためのフィルタ。
160154 LogSlowFilter.php 実行に時間がかかるアクションをログするためのフィルタ。
161155 mojo/ フレームワーク拡張
162- ActionlessView.php Notfoundアクションなどにアクションのないビューを取り扱う能力を付与するためのトレイト。
163156 SmartyRenderer.php Smartyを使ったレンダラ。
164157 mojo/ フレームワークコア。
165158 resources/ ブラウザから直接参照される必要がないアセットの配置場所。
--- resources/initialize.php (revision 164)
+++ resources/initialize.php (revision 165)
@@ -582,7 +582,7 @@
582582 return ($value == $check) ? null : $value;
583583 }
584584
585-// 引数が正の数または負の数であるかに関わらずく、ただ小数点とそれ以降にある数字を切り捨てた値を返す。javascript の Math.trunc() に相当。
585+// 引数が正の数か負の数かに関わらず、ただ小数点とそれ以降にある数字を切り捨てた値を返す。javascript の Math.trunc() に相当。
586586 function trunc($x) {
587587 return $x < 0 ? ceil($x) : floor($x);
588588 }
@@ -673,7 +673,7 @@
673673 function string_cast($v) {
674674
675675 if(is_array($v)) {
676- return sprintf('array(%s)', implode(',', array_each($v, function($v, $k){
676+ return sprintf('array(%s)', implode(', ', array_each($v, function($v, $k){
677677 return "$k=>" . (is_array($v) ? 'array' : (string)$v);
678678 })));
679679 }
@@ -727,9 +727,9 @@
727727 }
728728
729729 // コマンドの出力が逐次出力される標準関数 system() を、コマンドの出力全体を戻り値とするように改造したもの。
730-// コマンド出力をモニタしながら実行するバッチなどで便利なことがある。
730+// コマンド出力をそのままモニタ出力して、しかしプログラムでも処理するバッチなどで便利なことがある。
731731 // コマンドの末尾に "| tee" を付けて動作するので、末尾でシェル操作をしているようなコマンドではうまく動かないかも。
732-function system_exec($command, &$retval=null) {
732+function exec_tee($command, &$retval=null) {
733733
734734 // 出力を得るための一時ファイルを作成。
735735 $tmpfile = tempnam(sys_get_temp_dir(), 'php-');
--- resources/settings.php (revision 164)
+++ resources/settings.php (revision 165)
@@ -20,7 +20,7 @@
2020 'mojo.mojo' => 'mojo', // Mojoのクラスファイルがあるディレクトリ。
2121 'mojo.resources' => 'resources', // リソース類を格納するディレクトリ。
2222 'mojo.libs' => 'lib', // "libs" ディレクトリ。サブディレクトリを含め、このディレクトリより下のファイルはオートロードの対象になる。
23- 'mojo.sites' => 'sites', // サイト・アクションファイルを格納しているディレクトリ。
23+ 'mojo.sites' => 'sites', // 各サイトを直接格納している親ディレクトリ。
2424 'mojo.var' => 'var', // サーバ処理で生成されるファイルの格納ディレクトリ。
2525 'smarty.cache' => 'var/smarty.cache',
2626 'smarty.compile' => 'var/smarty.compile',
@@ -32,12 +32,12 @@
3232
3333 // ルーティングに関する設定。
3434 'routing' => array(
35- 'mojo.default_site' => 'public', // URLでサイトが省略された場合のデフォルト。
36- 'mojo.default_site.cli' => 'command', // コマンドラインで実行された場合のデフォルトサイト。
37- 'mojo.default_action' => 'index', // URLでアクションが省略された場合のデフォルト。
38- 'mojo.default_front' => 'index.php', // 省略可能なフロントコントローラー。URL生成時に参照される。
39- 'mojo.notfound_action' => 'notfound', // 該当のアクションが見つからない場合のアクション。
40- 'mojo.error_action' => 'sorry.error', // エラーが発生した場合のエラー処理アクション。AppMojo クラスで探索されている。
35+ 'mojo.default_site' => 'public', // URLでサイトが省略された場合のデフォルト。
36+ 'mojo.default_site.cli' => 'command', // コマンドラインで実行された場合のデフォルトサイト。
37+ 'mojo.default_direction' => 'index', // URLでディレクションが省略された場合のデフォルト。
38+ 'mojo.default_front' => 'index.php', // 省略可能なフロントコントローラー。URL生成時に参照される。
39+ 'mojo.notfound_direction' => 'sorry.notfound', // 該当のアクションが見つからない場合のディレクション。
40+ 'mojo.error_direction' => 'sorry.error', // エラーが発生した場合のエラー処理アクション。AppMojo クラスで探索されている。
4141
4242 // ここの設定で、リクエストされているパスを先頭一致で検査するURLリライトのような挙動を設定できる。
4343 //
@@ -127,7 +127,7 @@
127127 // これらは実行時に設定される。ここには説明のためにキーのみが記述されている。
128128 'runtime' => array(
129129 'mojo.requested_site' => '', // リクエストされているサイト名。
130- 'mojo.requested_action' => '', // リクエストされているグループ・モーション名。
130+ 'mojo.requested_direction' => '', // リクエストされているディレクティブ名(グループ.モーション)。
131131 'mojo.requested_name' => '', // 上記二つを "/" で連結したもの。
132132 'mojo.front_dir' => '', // REQUEST_URI から PATH_INFO を除き、さらに実行ファイル部分を除いたもの。
133133 // たとえば、/some/path/index.php/abc/def であれば /some/path
--- resources/smarty.plugin/block.detail_link.php (revision 164)
+++ resources/smarty.plugin/block.detail_link.php (revision 165)
@@ -20,9 +20,6 @@
2020 // hrefを生成。
2121 $href = AppMojo::getUrl($params['action'], ['id'=>$params['id'], '_backto'=>true]);
2222
23- // リンク文字列に「(id)」を付ける。
24- $content .= sprintf('(%s)', htmlspecialchars($params['id']));
25-
2623 // <a>で囲んで出力。HtmlUtil::tag() を使ってないのは二重エンコードを避けるため。
2724 return sprintf('<a href="%s">%s</a>', htmlspecialchars($href), $content);
2825 }
--- resources/smarty.plugin/function.partial.php (revision 164)
+++ resources/smarty.plugin/function.partial.php (revision 165)
@@ -4,18 +4,17 @@
44 * 指定されたアクションの出力を取得してテンプレートに挿入する。
55 *
66 * パラメータ)
7- * action 指定のサイト・アクション名。サイト名とアクション名を "/" でつなげたものだが、
8- * "partial" サイトならばサイト部分は省略できる。
7+ * action 指定のアクション名。サイト名とディレクション名を "/" でつなげたものだが、"partial" サイトならばサイト部分は省略できる。
98 * その他 指定のアクションの attributes として渡される。
109 */
1110 function smarty_function_partial($params, $template) {
1211
13- // パラメータから "action" キーを取り出して、サイト名とアクション名に分ける。
14- @[$site, $action] = explode('/', ArrayUtil::eject($params, 'action'));
12+ // パラメータから "action" キーを取り出して、サイト名とディレクション名に分ける。
13+ @[$site, $direction] = explode('/', ArrayUtil::eject($params, 'action'));
1514
1615 // "/" がないのはサイト名の省略。
17- if( is_null($action) ) {
18- $action = $site;
16+ if( is_null($direction) ) {
17+ $direction = $site;
1918 $site = '';
2019 }
2120
@@ -27,5 +26,6 @@
2726 $params['smarty.parent'] = $template;
2827
2928 // 指定のアクションを実行。
30- Mojo::forward("$site/$action", $params);
29+ if( !Mojo::forward("$site/$direction", $params) )
30+ trigger_error("アクション $site/$direction を実行できません。", E_USER_WARNING);
3131 }
--- resources/smarty.plugin/function.url_for.php (revision 164)
+++ resources/smarty.plugin/function.url_for.php (revision 165)
@@ -4,7 +4,7 @@
44 * パラメータで指定されたアクションへのURLを返す。
55 *
66 * 以下のパラメータは特別に処理する。
7- * action サイト・アクション名。"site/action" の形式で指定する。現在のサイトと同一ならサイト名は省略できる。
7+ * action アクション名。"site/direction" の形式で指定する。現在のサイトと同一ならサイト名は省略できる。
88 * params パラメータを配列で指定したい場合はこのキーで指定する。
99 * encode 出力のエンコード方法。"html" か "json"。省略時は "html"
1010 * absolute 絶対URLにしたい場合は true を指定する。
--- resources/smarty.plugin/modifier.uncapsule.php (revision 164)
+++ resources/smarty.plugin/modifier.uncapsule.php (revision 165)
@@ -6,7 +6,7 @@
66 function smarty_modifier_uncapsule($value) {
77
88 if( is_array($value) )
9- return sprintf('array(%s)', implode(', ', $value));
9+ return string_cast($value);
1010 else
1111 return $value;
1212 }
--- sites/admin/AdminBaseAction.php (revision 164)
+++ sites/admin/AdminBaseAction.php (revision 165)
@@ -20,7 +20,7 @@
2020 // APIでないなら...
2121 if( !$this->filterByClass('ApiBehaviorFilter') ) {
2222
23- // 次の変数を全アクションで使えるようにする。
23+ // 次のビュー変数を全アクションで使えるようにする。
2424 $this->renderer['application'] = AppMojo::setting('application');
2525
2626 // backtoパラメータを解析してヘッダ部分のメニューバック階層を得る。
--- sites/admin/default/NotfoundView.html (revision 164)
+++ sites/admin/default/NotfoundView.html (revision 165)
@@ -27,7 +27,7 @@
2727
2828 404 Not Found.<br />
2929 site: {[$site]}<br />
30- action: {[$action]}<br />
30+ direction: {[$direction]}<br />
3131
3232 </body>
3333
--- sites/admin/populate/IndexAction.php (revision 164)
+++ sites/admin/populate/IndexAction.php (revision 165)
@@ -10,7 +10,7 @@
1010
1111 // 入力検証
1212 if( $error = MojoForm::execute($_POST['table'], ['type'=>'word']) )
13- throw new CrackingException('フォームに入力された変数tableの値が不正(%s)', $error);
13+ throw new ErrorException('フォームに入力された変数tableの値が不正(%s)', $error);
1414
1515 // 投入対象が指定されていないならすぐビューへ。
1616 if( !$_POST['table'] ) return;
--- sites/command/CommandBaseAction.php (revision 164)
+++ sites/command/CommandBaseAction.php (revision 165)
@@ -11,7 +11,7 @@
1111 // このバッチの実行期限。ワンショットなバッチなどで設定する。少しでも間違い実行を防ぐため。
1212 protected $batchExpiration = null; // 設定するなら '2021-03-09 15:21:23' など。
1313
14- // 出力先ログ名。nullの場合はアクション名から生成される。
14+ // 出力先ログ名。nullの場合はアクションクラス名から生成される。
1515 protected $logname = null;
1616
1717 // バッチの出力先となる Log インスタンス。ready() でセットされる。
--- sites/global_layout.html (revision 164)
+++ sites/global_layout.html (revision 165)
@@ -26,8 +26,8 @@
2626 // APIコール時の現在バージョンとしても使う。
2727 Runtime["requested_site"] = {[AppMojo::setting('runtime', 'mojo.requested_site')|json nofilter]};
2828 // リクエストされているサイト名。サービス名じゃないよ。
29- Runtime["requested_action"] = {[AppMojo::setting('runtime', 'mojo.requested_action')|json nofilter]};
30- // リクエストされているグループ・アクション名。
29+ Runtime["requested_direction"] = {[AppMojo::setting('runtime', 'mojo.requested_direction')|json nofilter]};
30+ // リクエストされているディレクション名。
3131 </script>
3232
3333 {[block name="site-head"]}{[/block]}
--- sites/partial/PartialBaseAction.php (revision 164)
+++ sites/partial/PartialBaseAction.php (revision 165)
@@ -12,7 +12,7 @@
1212 public function run() {
1313
1414 if(AppMojo::setting('runtime', 'mojo.requested_site') == 'partial')
15- throw new CrackingException('partialサイトを直接実行しようとした');
15+ throw new ErrorException('partialサイトを直接実行しようとした');
1616
1717 return parent::run();
1818 }
--- sites/partial/default/RecordAction.php (revision 164)
+++ sites/partial/default/RecordAction.php (revision 165)
@@ -14,7 +14,8 @@
1414 $attributes = $this->attributes;
1515 $attributes['resultset'] = array( ArrayUtil::eject($attributes, 'record') );
1616
17- AppMojo::forward('partial/resultset', $attributes);
17+ AppMojo::forward('partial/resultset', $attributes)
18+
1819 return 'none';
1920 }
2021 }
--- sites/public/sorry/ErrorView.html (nonexistent)
+++ sites/public/sorry/ErrorView.html (revision 165)
@@ -0,0 +1,14 @@
1+{[extends file='layout.html']}
2+{[$title="ERROR!"]}
3+
4+{[block name="content"]}
5+
6+ <p>
7+ <pre>{[$action->attributes['message']]}</pre>
8+ {[if $action->attributes['inquiry']]}
9+ <pre>問い合わせコード: {[$action->attributes['inquiry']]}</pre>
10+ {[/if]}
11+ <pre>{[$action->attributes['description']]}</pre>
12+ </p>
13+
14+{[/block]}
--- sites/public/sorry/NotfoundView.html (nonexistent)
+++ sites/public/sorry/NotfoundView.html (revision 165)
@@ -0,0 +1,10 @@
1+{[extends file='layout.html']}
2+{[$title="NOT FOUND"]}
3+
4+{[block name="content"]}
5+
6+ <p>
7+ アクション {[Mojo::setting('runtime', 'mojo.requested_name')]} が見つかりません。
8+ </p>
9+
10+{[/block]}
--- sites/test/nova/LogAction.php (revision 164)
+++ sites/test/nova/LogAction.php (revision 165)
@@ -61,8 +61,8 @@
6161
6262 $ret = AppLog::exception(new Exception('this is test'), 'test');
6363 $this->checkRegexp('AppLog::exception() による書き込みが正常に機能している。', file_get_contents($testfile), '/^\d{2}:\d{2}:\d{2} .+?\(\d+\) Exception\(0\): this is test/');
64- $this->checkRegexp('AppLog::exception() の戻り値 code が正常に返されている。', $ret['code'], '/-\d{10}$/');
65- $this->checkRegexp('AppLog::exception() の戻り値 text が正常に返されている。', $ret['text'], '#^/.+?Exception\(0\): this is test#');
64+ $this->checkRegexp('AppLog::exception() の戻り値 code が正常に返されている。', $ret[0], '/-\d{10}$/');
65+ $this->checkRegexp('AppLog::exception() の戻り値 text が正常に返されている。', $ret[1], '#^/.+?Exception\(0\): this is test#');
6666 unlink($testfile);
6767
6868 AppLog::lineAccess('test');