この間の続きを面倒臭いながら実装中。
Accept-Languageヘッダフィールドの書式はRFC2616のSection14で定義されてるようです。
[RFC 2616] Hypertext Transfer Protocol—HTTP/1.1
Each language-range MAY be given an associated quality value which represents an estimate of the user’s preference for the languages specified by that range. The quality value defaults to ””q=1””. For example,
Accept-Language: da, en-gb;q=0.8, en;q=0.7
なるほど。これにそってgetLocales, getLocaleを実装してみました。
<?php
class RequestUtil {
function getLocale() {
return array_shift(RequestUtil::getLocales());
}
function getLocales() {
foreach (split(",", $_SERVER["HTTP_ACCEPT_LANGUAGE"]) as $value) {
list($name, $pri) = split(";", trim($value));
$num = preg_replace("/^q=/", "", $pri);
$langs[($num ? $num : 1)] = $name;
}
arsort($langs);
foreach ($langs as $value) {
$locales[] = $value;
}
return $locales;
}
}
?>
Firefoxでは仕様通りこんなのが来るのでちゃんと取れます。
ja,en-us;q=0.7,en;q=0.3
IEではこんな感じ。
ja
quality valueを省略した場合は1ということになっているのでこれも問題なさそう。
でもOperaのこれは間違ってないのかな
ja, en
両方1だとどちらを選んでいいのかわからない!
現在の実装だと上記の場合、getLocale()はenが返って来てしまいます。かといって左側優先なんてルールを勝手に実装したくない。
・・・と書いていたらyoshukiさんから情報が。
[RFC 3282] Content Language Headers
The syntax and semantics of language-range is defined in [TAGS]. The Accept-Language header may list several language-ranges in a comma- separated list, and each may include a quality value Q. If no Q values are given, the language-ranges are given in priority order, with the leftmost language-range being the most preferred language; this is an extension to the HTTP/1.1 rules, but matches current practice.
If Q values are given, refer to HTTP/1.1 [RFC 2616] for the details on how to evaluate it.
Accept-LanguageヘッダはRFC3282で再定義されていてquality valueが無い場合は左優先となっているそうです!
さっそくさっきのヤツを左優先に修正。
<?php
class RequestUtil {
function getLocale() {
return array_shift(RequestUtil::getLocales());
}
function getLocales() {
foreach (<strong>array_reverse(</strong>split(",", $_SERVER["HTTP_ACCEPT_LANGUAGE"])<strong>)</strong> as $value) {
list($name, $pri) = split(";", trim($value));
$num = preg_replace("/^q=/", "", $pri);
$langs[($num ? $num : 1)] = $name;
}
arsort($langs);
foreach ($langs as $value) {
$locales[] = $value;
}
return $locales;
}
}
?>
これで安心です。
「世界政府っていうものが仮にあるとして、そこで開発しなければならないはずのシステムは全部グーグルで作ろう。それがグーグル開発陣に与えられているミッションなんだよね」
グーグルに勤める友人は私にこう言った。
それでGoogle Earthか!
SourceForge.net: Project Info – AwesomeFramework
This is PHP application framework of an extremely simple Plain Old PHP File base. It is possible to use it for not only a browser but also the command line application.
1行のフレームワークAwesomeFrameworkをsourceforge.netにプロジェクト登録しました。
POPFとか勝手に言っていいのかよ!
プロジェクト申請を通すために大層なこと書いてるけどソースは1行。嘘は言ってないからよしとします!
AwesomeFramework-0.4のソース:
<?php include $_SERVER["argv"][1] ? preg_replace('/^--action=/', "", $_SERVER["argv"][1]) : ($_GET["action"] ? $_GET["action"] : 'default.php'); ?>
しかし、この1行から恐るべき機能の数々が(実装したじゃなくて)発見されたので次の勉強会のネタはこれで行こうかと。
AwesomeFrameworkをコマンドラインに対応させました。
<?php
forward($_SERVER["argv"][1] ? preg_replace("/^--action=/", "", $_SERVER["argv"][1]) : ($_GET["action"] ? $_GET["action"] : "default"));
function forward($action) { include $action.".php"; }
?>
php index.php --action=action
なんて高機能なんだ。
コマンドラインから実行できるということは・・・Mojavi3超えたっっっ!?
AwesomeFrameworkにForwardを実装しました。
<?php
include $_GET["action"] ? $_GET["action"].".php" : "default.php";
function forward($action) { include $action.".php"; }
?>
なんて高機能なんだ。
これでViewにforwardすることでModelとViewとControllerがそろってると言えるのではないだろうか!!
AwesomeTemplateEngineと同様、PHPファイル自体をテンプレートとして使用しますがAwesomeFrameworkはあくまで、$GLOBALS経由でテンプレートに値を渡します!
Actionのファイル:
<?php
$GLOBALS["message"] = "World";
forward("view");
?>
Viewのファイル:
Hello <?=$GLOBALS["message"]?>!
あと、今思いついたんですが、
module/ action.php index.php
index.php?action=module/action
ってやってモジュールによって管理する機能搭載!とか言えないでしょうか・・・。
個別処理に関数やクラスを使っても良いが、各caseで関数などのコールが入るだけで、やることは変わらない。
PHPのフレームワークは、個別処理をどう別けて記述するかを決めるところが肝であって、mojaviとか見たいに、コントローラクラスなんて作らなくてもいいやんと言う気もする。柔軟性高いし速いし。
Harukiさんのフレームワークについての考察。
振り分け処理(あえてコントローラーとは言わない)の最小限サンプルを書かれています。
確かにFront Controller Patternといってもやりたいことはこれだけスね。
それに比べた場合、他のフレームワークの便利な点は何か?
- 名前空間が分かれている
- エントリポイントに制約(仕様)がある
名前付けとか開放・閉鎖原則とか単にオブジェクト指向の便利な点と変わらなくなってしまいました。
クラスになると名前空間を分けることができるけれど、(クラスに対してコンパイラがやっているように)関数名と変数名にPrefixを付ければ同じことになる。
JavaやPHP5のように仕様を強制できる仕組み(インターフェースとか)があると個別処理が交換可能なことが保障されるので便利。だけど仕様を覚えるのが面倒。
これを全部とっぱらって柔軟性が高くて習得コストが低いのを売りにしたフレームワーク(的なもの)っていう方向性も面白いなと思いました。
そこで、昔面白いなと思った「7行のテンプレートエンジンAwesomeTemplateEngine」に対抗して「1行のフレームワークAwesomeFramework」を作ってみました。
AwesomeFramework 0.1(HelloWorldサンプル付属)
そのソース:
<?php include $_GET["action"] ? $_GET["action"].".php" : "default.php"; ?>
これを使ってある程度のアプリを作ろうとすると、逆に名前空間のありがたさとか(グローバル変数地獄)を身をもってわかって良いかも知れない!
自分の記事へのリンクがあればまあ良いかなと思っています。
リンクのありなしでしょんぼり度がかなり違いますからね。
これはありますね。
大量のトラックバックスパムに紛れて本物のTBキター!
↓
自記事への言及無し
↓
ガッカリ・・・
↓
少しでも関連のあるサイトに片っ端からTB打ったのかも?
↓
むしろ小腹すら立つ
Mojavi2、こんなFilterはどうだろうか。
<?php
class MessageFilter extends Filter {
function execute(&$filterChain, &$controller, &$request, &$user) {
if ($request->hasAttribute("messages") === false) {
// global messages
$globalMessagesPath = BASE_DIR."configs/messages_".DEFAULT_LANG.".ini";
if (is_readable($globalMessagesPath)) {
$messages = parse_ini_file($globalMessagesPath);
}
}
// module messages
$messagesPath = $controller->getModuleDir()."config/messages_".DEFAULT_LANG.".ini";
if (is_readable($messagesPath)) {
$messages = array_merge($messages, array($controller->getCurrentModule() => parse_ini_file($messagesPath)));
}
$filterChain->execute($controller, $request, $user);
}
}
}
?>
messages_ja.ini, messages_en.iniとか置いといて読み込むことで多言語化の枠組みを提供するFilter。
アプリ共通のiniとモジュール毎のiniを両方読み込む。
strutsのように環境変数から勝手に切り替えるようにしたいんだけどHTTP_ACCEPT_LANGUAGEのパースが面倒臭い。MojaviのRequestにもServletRequest.getLocale()があれば・・・。
RubyForge is a home for open source Ruby projects; thanks to the folks who make it possible!
RubyForgeて、
Ruby界隈はどんだけアツいんだよ!