2002-06-06
_ phpのバージョン
使い始めた頃はまだphp3だったこのサーバー。二ヶ月くらい前にふと気づいたらphp4にバージョンアップされていて、ラッキーと思っていたんだけど、ただちょっと古いバージョンだったのが気になっていた。が、今日見てみたらいつの間にか最新バージョン(追記:よく見たら全然最新じゃなかった。4.0系から4.1系にあがっていただけ)にあがっていた。良かった良かった。phpは3と4はずいぶん違うけど、4の中でも初期バージョンと4.1以降のバージョンは結構違うからな。最新バージョンにあわせてもらえると、家サーバーとまったく同じコードが動かせるんで楽。Apacheとかもちゃんとバージョンアップされているようだ。使い始めた当初は、いかにも再販業者がノーメンテナンスで運営している(から安い)っぽかったけど、結構ちゃんとメンテナンスするんだね。
_ はてなアンテナtips
URLはできるだけ正確な表記で登録しましょう。たとえばアンテナ系リンク集なんかで、URL末尾に機能的な意味のない更新日時付加情報(?0206061424とか)がついている場合は、それをちゃんと省きましょう。また、URLがディレクトリで終わっている場合は、URL末尾は「/」で閉じておきましょう。あと個人的にはURLの末尾がインデックスファイル(index.htmlとか、ファイル名を省略しても同じ内容が開かれるもの)になっている場合は、それも省く方に統一する方が好みです。ただし、index.htmになっているけど、サーバーでindex.htmがインデックスファイルとして登録されていない場合があるので、要注意。
数字カウンター付きページで、カウンター部の更新を無視したい場合は、更新無視文字列を「\d+」にしておきましょう。あと、tDiary系の「本日のリンク元」を無視したい場合は「\d+\|」にしておきましょう(\d+だけでもいけている気がするが)。それ以上複雑なのは、まあそれなりにがんばってください。
* 追記訂正
上記は嘘です。「更新無視文字列」は、行内に一部でも含まれていれば有効なんで、上記のように設定したら、数値が入っている行はみんな無視されちゃいます。マッチした部分だけ無視するんだと思ってたよ。
2006-06-06
_ Zendネームスペースの下に入れるのはやめた
Zend_Search_HyperEstraierとして作りかけていたライブラリは、ひとまず単体のライブラリとしての完成度を高める方に集中するために、Zendネームスペース以下で構築するのは(他にやらなきゃならないことが増えるんで)いったんやめて、単にHyperEstraierネームスペース以下に構築することにした。いまいち使う気にならないLucene対抗にしようかと思ってたんだけど。
で、検索条件周りをいろいろいじっているうちに、HyperEstraier_SearchCondition::toQueryStringでノードAPIのパラメータになり、HyperEstraier_SearchCondition::to***(未定)Stringでestcmdのコマンドラインパラメータとなるのが妥当だよなーと思いつつ、そうなるとescmdのラッパーもないと意味がないよなーってことになり、そうなるといったいどこまで作ると一段落ってことになるのか先が見えなくなってきたのでした。
そういや検索条件をもっと設定しやすくしよう計画に関しては、一番わかりにくいのは属性検索の条件式表現だと思うんで、属性検索条件をセットする場合は、
$condition->addAttribute('@title', '==', 'foo');
$condition->addAttribute('@mtime', '>=', '2006/6/6');
とかやると、左辺値(既知のシステム属性の場合はそれにあわせる)や右辺値(左辺値から推測できなかった場合、右辺値が数値や日付として解釈できる場合はそれを優先する)から属性の種類を推測して、
$condition->attributes[] = '@title STREQ foo'; $condition->attributes[] = '@mtime NUMLE 2006/6/6';
とかに展開するようにしてみたんだけど、なんかいまいちすっきりしなくて中断中。どうせなら、
$condition->attributes[] = '@title == foo'; $condition->attributes[] = '@mtime >= 2006/6/6';
にしちゃった方がいいかなー。最悪でもセットされたパラメータをそのままHyper Estraierに渡せるようにしておけば、悪影響は出ないかな?
_ spycのパースって重いんだな
8kバイト+1kバイトのYAMLで書かれた設定ファイルをspycを使って 読み込むWebアプリ。当初の速度はこんな感じ。
Requests per second: 3.01 [#/sec] (mean) Time per request: 1662.891 [ms] (mean) Time per request: 332.578 [ms] (mean, across all concurrent requests)
ページ(データ取得+レンダリング)キャッシュをかましてみたんだけど、大して速くならなかった。
Requests per second: 3.67 [#/sec] (mean) Time per request: 1361.458 [ms] (mean) Time per request: 272.292 [ms] (mean, across all concurrent requests)
で、YAMLファイルの読み込みにキャッシュ(serializeして保存)をかましたら、劇的に速くなった。
Requests per second: 10.59 [#/sec] (mean) Time per request: 472.179 [ms] (mean) Time per request: 94.436 [ms] (mean, across all concurrent requests)
spycのパースってこんなに重い処理だったのか。
_ 配列が絡んだ__setの挙動
class Foo
{
protected $_array = array();
public function __get($name)
{
echo "__get called by $name\n";
switch ($name) {
case 'array': return $this->_array;
default:
throw new Exception();
}
}
public function __set($name, $value)
{
echo "__set called by $name, $value\n";
switch ($name) {
case 'array':
$this->_array = $value;
break;
default:
throw new Exception();
}
}
}
$foo = new Foo();
var_dump($foo->array);
$foo->array = array(1);
var_dump($foo->array);
$foo->array[] = 2;
var_dump($foo->array);
が、
__get called by array
array(0) {
}
__set called by array, Array
__get called by array
array(1) {
[0]=>
int(1)
}
__get called by array
__get called by array
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
ってなる(PHP 5.1.4/Windows XP Professional)のは正しいのか?
$foo->array[] = 2;
のところで__setが呼ばれずに、直接Foo::$_arrayに追加されているんだけど。
っつーか、
$foo->array[] = 2;
は
$tmp = $foo->array; array_push($tmp, 2); $foo->array = $tmp;
相当の処理だと信じていたんだけど。
_ serializeとvar_exportどちらが速いのか
Spycでパースしたデータを、serializeしてキャッシュするよりも、var_export($data, true)でPHPコードにしてキャッシュした方が速いんじゃね。と思ってベンチを取ってみた。
using Spyc::YAMLLoad start: 1149596540.0295 end: 1149596555.4353 elapsed: 15.405829191208 using serialize start: 1149596555.4358 end: 1149596555.64 elapsed: 0.2041699886322 using php var_export start: 1149596555.642 end: 1149596556.2202 elapsed: 0.57813310623169
あれー、PHPコードにするよりもserializeの方が速いのか。まあ確かにserializeの方が圧倒的にロジックが単純で済むしな。ただPHPコードとしてファイルに落としておけば、PHP Accelerator系のキャッシュが使えるだろうから、そういうのと組み合わせると速くなるかもしれない。けどまあ、ふつうはserializeを使おう。
一応コード
require_once 'spyc.php';
$yamlFile= 'spyc.yml';
echo "using Spyc::YAMLLoad\n";
$starttime = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$config = Spyc::YAMLLoad($yamlFile);
}
$endtime = microtime(true);
$elapsedtime = $endtime - $starttime;
echo "start: $starttime\n";
echo "end: $endtime\n";
echo "elapsed: $elapsedtime\n";
echo "\n";
echo "using serialize\n";
$serializedFile = 'spyc.yml.txt';
file_put_contents($serializedFile, serialize($config));
$starttime = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$config = unserialize(file_get_contents($serializedFile));
}
$endtime = microtime(true);
$elapsedtime = $endtime - $starttime;
echo "start: $starttime\n";
echo "end: $endtime\n";
echo "elapsed: $elapsedtime\n";
echo "\n";
echo "using php var_export\n";
$phpFile = 'spyc.yml.php';
file_put_contents($phpFile, '<?php return ' . var_export($config, true) . '?>');
$starttime = microtime(true);
for ($i = 0; $i < 1000; $i ++) {
$config = include $phpFile;
}
$endtime = microtime(true);
$elapsedtime = $endtime - $starttime;
echo "start: $starttime\n";
echo "end: $endtime\n";
echo "elapsed: $elapsedtime\n";
2007-06-06
_ 起動ファイルのコードのスコープ
どうでもいいけど、ちょっと気になること。
Zend Frameworkでは、起動ファイルにいろいろ初期化コードを書いていくけど、そこで使った変数はグローバル変数になってしまうことになる。たとえば、
$config = new Zend_Config_Xml('/path/to/config.xml');
Zend_Registry::set('config', $config);
とかして読み込んだconfigオブジェクトは、普通はZend_Registryにセットしておいて、必要に応じてZend_Registry::get('config')して使うけれども、実際はアクションコントローラの中とかで、
global $config;
とかすればアクセスできてしまう。かといって、わざわざ、
$config = new Zend_Config_Xml('/path/to/config.xml');
Zend_Registry::set('config', $config);
unset($config);
とまでは書かないよね。
じゃあグローバルスコープじゃないようにしようかと、起動ファイルの中身を、
function main()
{
// 各種初期化処理
}
main();
とかmain()関数でくくって書けばいいかなーと思ったりしたんだけど、それもいまいちぱっとしない気がする。どうせなら、
class Application
{
public function __construct()
{
$this->_init();
$this->_run();
}
protected function _init()
{
//初期化コード
}
protected function _run()
{
//実行
}
}
new Application();
までやっちゃった方が融通が利くか(staticメソッドのみで書いて、インスタンス化させずに動かした方がきれいか)。
っつーか、起動ファイルの中身をグローバルスコープでフラットに書いていると、可読性が悪くて嫌になってこない?
_ Zend_Aclのresourceの解決
Zend_Aclで、roleの方はスキーマ定義の段階でだいたい決まることが多いだろうし、それを解決するタイミングもユーザー識別の段階でほぼ確定だろうけど、resourceの方はどうやって解決するのが妥当だろう?
ドキュメントにある例では、resourceを指定せず(すべてのresourceに対して)roleごとに操作権限を設定するようになっているけど、よく使われるのは(すべてのresourceに対する)操作権限を管理するパターンではなく、どのresourceにアクセスできるかを管理するパターンだろう。たとえば、管理ツールには管理者のみがアクセスできる、とか。
そう言う場合、やっぱりmodule=resourceと考えて、Zend_Auth_Plugin_Abstract::preDispatch()あたりで、認証結果から確定したrole+リクエストから抜き出したmodule=resourceとして、$acl->isAllowed()するのが常道かな。module単位よりも細かい単位でアクセス制御したい場合は、function getResourceName($request)みたいなメソッドを用意して、requestから対応するresourceを解決するようにするとか。
ただ、SNSみたいにresourceとroleの関係を動的に解決しなければならないことが多いアプリケーションの場合、Zend_Aclをどう使うのがいいのかはまだ未解決。プラグインとかの共通層では対応が難しそうだから、roleとresourceの関係は抽象化した形で登録しておいて、アクションコントローラの末端でgetResource()とgetRole()を実装して突き合わせるしかないかな。それとも末端でローカルなZend_Aclオブジェクトを生成(=resourceもroleもローカルな定義を作り直す)した方がいいんだろうか。
ちなみに、ようやくZend_Session、Zend_Auth、Zend_Aclを実践的に使ってみているけど、思ったよりもだいぶ使いやすくなっていた。Zend_AuthとZend_Auth_Adapterの関係がちょっとわかりにくかったけど。
_ しょうもないバグを見つけた
Zend_Logでmaskとorしているのを見つけたとき以来のしょーもないバグ。まだこんなのが残っているのか。
ちなみになんでこれを見つけたのかというと、ErrorControllerの中で、
$this->getResponse->setHttpResponseCode(403);
をした場合に、なぜかviewRendererでデフォルトのテンプレートがレンダリングされないという症状にはまったから。「Zend_Controller_Response_Abstract::setHttpResponseCode(403)でisRedirect()がtrueにセットされる」→「Zend_Controller_Action_Helper_ViewRenderer::postDispatch()で、render()するかどうかの条件に!$this->getResponse()->isRedirect()を見ている」という原因ね。



Before...
_ ishinao [どうもバグっぽいですね。 $foo = new Foo(); $foo->array[] = 1; var_dum..]
_ ishinao [どうやら http://bugs.php.net/bug.php?id=36214 と同じ話っぽい。]
_ ishinao [ありゃ、(自分の書いた)ツッコミが一個消えた。tDiaryのバグか? 管理画面上は出てくるんだけど、勝手に非表示にな..]