2008年7月17日木曜日

PHP-Barcodeを使ってみたサル

バーコードとか作ってみようと思いライブラリを探しました。で見つけたのがPHP-Barcode。
http://www.ashberg.de/php-barcode/

動かすまで15分ぐらいかかった。バージョンは0.3pl1。
使い方は適当なディレクトリに配置してimgタグのsrcでパラメータつけるだけ。

<img src="barcode.php?code=4901480070278&scale=2&mode=png&encoding=EAN">

しかしバーコードが出てこない。というかPHPスクリプトが実行されていない。ソースを見たらPHPの開始タグが「<?」になっていたので「<?php」に修正(barcode.php、encode_bars.php、php-barcode.php)。無事バーコードが出てきました。

しかしバーコードの下部に表示されるはずのEAN(JAN)の数字が出てこない。もう一度ソースを見てみたらフォントのパスを指定している部分があやしい(php-barcode.phpの59行目と69行目)。パスの指定に$_SERVER["PATH_TRANSLATED"]というのを使っているけど値を取得できていないようなので、フォントを同じディレクトリに配置して59行目と68~70行目をコメントアウトし、$font_loc="フォントファイル名"と一行追加。無事動きました。

ずいぶん昔に書かれたソースなのかなと思ったら2004年製だってさ。4年前って大昔だなと思ったら4年後が恐くなってきた。

※フォントって何気なく使ってるけどライセンス要確認。なんだか複雑なことになってるようです。調べたけど良くわからんかった。サルはフリーフォントを使いましょう。

2008年7月14日月曜日

JavaScriptでホイール操作(拡大縮小)を実現したいサル

Googleマップ等で実現されているマウスホイール操作をやってみたくなってトライ。クロスブラウザ対応が大変手間で、マウスホイールをどっちに回転させたかまでは取得できたが、ここから先がムリ。サルが考えていたのはユーザがドラッグ操作で自由に配置した要素たちを、位置、サイズ、フォント等全てホイール操作で拡大縮小したかったんだけど難しい。今までFirefoxを素敵なブラウザと思っていたけど、今回はやられた。style属性のwidthやheightの値を除算したあと乗算したらもとの値に戻らない。style属性の値として有効な値と端数処理がブラウザごとに違う様子。もう一度チャレンジする気になれなさそうなので、誰かが素敵なライブラリを作ってくれるのを待つ。

<div id="hoge" style="border: 1px solid; width: 300px;height: 300px;"></div>

<script language="javascript">

function wheel(event){

var zoom = 0;

if (!event) event = window.event;

if (event.detail)
zoom = -event.detail / 3;
else if (event.wheelDelta)
zoom = event.wheelDelta / 120;
else zoom = 0;

var elem = document.getElementById('hoge');
elem.innerHTML = "zoom = " + zoom;
}

var hoge = document.getElementById('hoge');
if (hoge.addEventListener) hoge.addEventListener('DOMMouseScroll', wheel, false);
hoge.onmousewheel = hoge.onmousewheel = wheel;

</script>

2008年7月10日木曜日

cometについて続きを話すサル

HTTPの同時接続数には制限があるらしい。ということはもちろんAjaxセッションも。cometはセッションを一つ占有し続けるのでこれは重要な問題です。チャットしながら別のAjaxセッションを使いたい時もあるだろうし、複数のチャットルームにログインする場合とか。で前回作ったチャットプログラム(7月9日の2-1、2-2)でブラウザ毎にテストしたところ下記のようになっておりました。数字が同時接続数。

FireFox 2
Opera 4
Safari 4
IE8beta 6

同時接続数の制限は一つのブラウザから一つのドメインに対してらしいので、これを回避するにはサブドメイン等を使うとよろしいということになっているようです。

そしてcometではAjaxセッションを保持し続けるわけですが、SafariではAjaxセッションは2分で切れてしまうようです。つまりチャットやってて2分発言がないと落ちてしまう!これを回避する方法は不明。他のブラウザは少なくとも30分は大丈夫でした。

さらにcometを実装したサーバがどれくらいのセッションを捌けるのかという問題もあります。なにしろ接続しっぱなしですから。手元のマシンで前述のチャットプログラムを使ってテストしたところ、同時接続数20で発言時にCPU使用率3~7%(発言の長さによる)、待機時は1%未満、メモリ使用量はほとんど影響受けず。100セッションぐらいだったらテキトーなサーバでもけっこういけるんじゃないでしょうか。1Kとか10Kとかなったら多分無理。

以上なんかよくわかってないことについて書いてしまったので突っ込みあったらお願いします。

2008年7月9日水曜日

cometなんて知らなかったサル

なんか閃いた(サル的に)のでブラウザでチャットする方法を考えてみた。XMLHttpRequest(いわゆるAjaxのこと、以下XHR)を使えば容易に実現できそうに思える。

まず思いつくのがクライアントから定期的(1秒毎とか)にリクエストさせる方法(ポーリングというらしい)。この方法だと例えば5人が10分チャットすると5(人)×600(秒)=3000リクエスト。そしてユーザの発言が反映されるまで最大で1秒の遅延が発生する。リクエスト間隔を短くすれば遅延を短く出来るがリクエストが増える。リクエストを減らすと遅延が増える。

そこで閃いた(サル的に)のがブラウザからのリクエストに対してサーバサイドでセッションを保持し、ブラウザではXHR.readyState=3で受信する方法。リクエスト数=ユーザ数となり、リクエスト数という観点からは最良の方法と思われたが、実際にやってみたらFirefoxでしか動かない。IEもSafariもreadyState=3に対応していなかった。ソースは下記。サーバ側の処理(1-2)がミソ。

1-1
function broadcast(elem){
XHR.open('POST', 'broadcast.php',true);
XHR.onreadystatechange = function() {
if (XHR.readyState == 3 && XHR.status == 200) {
elem.innerHTML = decodeURIComponent(XHR.responseText);
}
}
XHR.send(null);
}

1-2
<?php
//broadcast.php
$stat_a = stat('chat.txt');
while($stat = stat('chat.txt')){
if($stat[9] != $stat_a[9]){
$str = file_get_contents('chat.txt');
echo "<div>".date('H:i:s')." ".urlencode(strip_tags($str))."</div>";
ob_flush();
flush();
$stat_a[9] = $stat[9];
}
usleep(0.1 * 1000 * 1000);
set_time_limit(10);
clearstatcache();
}
?>

IEでも動くいい方法がないか考えてみた。発言があるまでセッションを保持。発言があったらレスポンスしいったんセッションは終了。そしてすぐにまたXHRリクエスト。というのを繰り返す。これだと5人でチャットして全体で100の発言があったら5(人)×100(発言)=500リクエスト。ソースは下記。再帰的呼び出しでかっこいい出来(サル的に)。

2-1
function broadcast(elem){
XHR.open('POST', 'broadcast.php',true);
XHR.onreadystatechange = function() {
if (XHR.readyState == 4 && XHR.status == 200) {
elem.innerHTML = elem.innerHTML + decodeURIComponent(XHR.responseText);
broadcast(elem); //←再帰的呼び出し
}
}
XHR.send(null);
}

2-2
<?php
//broadcast.php
$stat_a = stat('chat.txt');
while($stat = stat('chat.txt')){
if($stat[9] != $stat_a[9]){
$str = file_get_contents('chat.txt');
echo "<div>".date('H:i:s')." ".urlencode(strip_tags($str))."</div>";
$stat_a[9] = $stat[9];
exit;
}
usleep(0.1 * 1000 * 1000);
set_time_limit(10);
clearstatcache();
}
?>

そして調べてみたらAjaxセッションを保持するという実装スタイルはcometという名前が付いていて別に新しくなかった。閃いたつもりだったのに(サルが)。XHR.readyState=3を使ったcometは今のところFirefoxでしか出来ないが、とても可能性を感じた。

以下てきとーにソースの説明

XHR.openでPOSTメソッドを使っているのはブラウザのキャッシュを回避するため。IEではGETメソッドのAjaxは強力にキャッシュされる。XHRは生成済みのXMLHttpRequestオブジェクトと思ってちょうだい。

1-2のob_flush()とflush()
PHPではスクリプトの実行が終わってから出力結果をブラウザに送信するが、ob_flush()とflush()を使うことでスクリプトの途中までの出力結果をブラウザに送信できる。

1-2のclearstatcache()
stat関数で取得したファイル情報はキャッシュされるので一度取得したファイル情報が更新された可能性がある場合はclearstatcache()でキャッシュをクリアしてからファイル情報を取得する必要がある。

1-2、2-2のset_time_limit(10)
スクリプトの実行でphp.iniで設定した秒数を超えるとFatal errorになるので、これを回避するためにset_time_limit(10)で期限を更新。set_time_limit()を呼び出すたびに秒数はリセットされる。数字は秒数。短すぎなければ何でも良い。

2008年7月2日水曜日

ユーザ操作の結果出来上がったDOMの状態を保存するサル

ユーザ操作の結果出来上がったDOMの状態を保存する方法を考えてみる。POSTでもGETでもいいからDOMの状態をXMLなりJSONなりの文字列に変換してサーバに送りつければいいんだと思うんだけど、その変換方法がさっぱり思いつかない。30分ぐらい悩んだあと気付いた。$(document).html()で取得できるじゃないかっ!アホですいません。いや違うサルだった。

ということで下記jQueryでAjaxに書いてみた(jQuery使うと楽すぎて脳が退化しそうだ)。要素も属性もそのままばっちり保存出来ます。それを読み込むとそのままそっくり復元される。当たり前だし簡単だったけど何故か感動した。

//指定した要素内のDOMを取得しdom_catch.phpにPOSTで渡す
function dom_up(elm){
$("dummy").load("dom_catch.php",{
dom: $(elm).html()
});
}