今回はリアルタイムな同期チャットを作ってみました。
考え方的にはぐーぐるさんが使ったことで有名なコメットとよく似ています。
ただPHPを使ってやりたかったのでcometは用いません。
そもそもコメットの特徴はクライアントサイドからサーバーへの要求の投げかけを一回にしてるところなのでその一回を半無限ループ使って常時接続状態にすればいいわけです。
以下がソースです。
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="content-script-type" content="text/javascript" />
<link rel="stylesheet" href="css/main.css" charset="utf-8" type="text/css" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/chat.js"></script>
</head>
<body>
<div class="chat" id="chat"></div>
<div class="commentarea" id="commentarea">
<textarea class="comment" id="comment"></textarea>
<input type="button" class="submit" value="発言" onclick="remark();" />
</div>
</div><!-- end Chat Program -->
</body>
</html>
chat.js
$(function(){
$("#submit").click(remark);
$.post("get_chat_log.php",{},get_message,"text");
});
function remark(){
var txt = $("#comment").val();
$.post("write_chat_log.php",{name:"oppai星人",message:txt});
$("#comment").val("");
}
function get_message(text){
var recs = eval(text);
n = recs.length;
for(var i = 0; i<n; i++){
data = $("#chat").html();
$("#chat").html(data +"<span class=\"name\">" +recs[i]["name"]+"</span> さんの発言:<br />"+"<span class=\"message\">"+recs[i]["message"]+"</span><br />\n");
}
$.post("get_chat_log.php",{},get_message,"text");
}
get_chat_log.php
<?php
set_time_limit(0);
$file_name = "log/chatlog.txt";
//ファイルの最終更新時間を取得
$o_time = filemtime($file_name);
$o_filesize = filesize($file_name);
while(TRUE){
//キャッシュ削除
clearstatcache();
//今現在のファイルの最終更新時間を取得
$n_time = filemtime($file_name);
//アクセス時の最終更新時間と比較
if($o_time!==$n_time){
$n_filesize = filesize($file_name);
$fp = fopen($file_name,"r+");
$fp2 = fseek($fp,$o_filesize);
//通常ファイルサイズ文読み込むがEOFになると終了する。
$body = fread($fp,$n_filesize);
fclose($fp);
break;
}
//遅延
sleep(1);
}
$new_line = explode("\n",$body);
$n = count($new_line);
$res = array();
for($i=0; $i<$n; $i++){
$data = explode(">",$new_line[$i]);
$account = $data[0];
$message = trim($data[1]);
$message = str_replace(">",">",$message);
$message = str_replace("<","<",$message);
$arr = array("name"=>$account,"message"=>$message);
array_push($res,$arr);
}
$n = count($res);
$txt ="";
for($i=0; $i<$n-1; $i++){
if($i==0 && ($n-1)!=1){
$txt .= '\'[{"name":"'.$res[$i]["name"].'","message":"'.$res[$i]["message"].'"}';
}elseif($i==0 && ($n-1)==1){
$txt .= '[{"name":"'.$res[$i]["name"].'","message":"'.$res[$i]["message"].'"}]';
}elseif($i==$n-2){
$txt .= ',{"name":"'.$res[$i]["name"].'","message":"'.$res[$i]["message"].'"}]';
}else{
$txt .= ',{"name":"'.$res[$i]["name"].'","message":"'.$res[$i]["message"].'"}';
}
}
echo $txt;
?>
write_chat_log.php
<?php
$name = $_POST["name"];
$msg = str_replace("\n","<br />",$_POST["message"]);
$msg = htmlspecialchars($msg,ENT_QUOTES);
$body = $name.">".$msg."\r\n";
$file_name = "log/chatlog.txt";
$fp = fopen($file_name,'a');
fwrite($fp,$body);
fclose($fp);
echo $body;
?>
以上の4ファイルです。
index.htmlのページ読み込みと同時にchat.js内でget_chat_log.phpへのajax通信が始まります。
set_time_limit関数の引数に0を代入することでphpの実行時間の制約を無限大に伸ばしてます。
そしてwhile(true)を用いて無限ループ。
sleepで引数時間に一回ずつループします。
ログファイルに更新があればbreakして差分をjsのコールバック関数にjsonフォーマットで受け渡し、js側で整形して出力します。
出力後、またget_chat_log.phpにアクセスして次の更新を待ち続けます。
次に発言時、index.htmlのonclickイベントでchat.jsのremark関数が動きwrite_chat_log.phpにajax通信し、入力情報をログに書き出しします。
これでリアルタイムなチャットが更新されます。
まぁ結構サーバーに負荷かかりますが。笑

試してないけどグレート!
差分だけやり取りするのはかっこいいね。
いくつか気になった点
[chat.js : 2]
● $(“#submit”).click(remark);
→ #submit は無いヨ。
あと、submit時に関数実行したいなら、inputにonclickイベントを登録するより
フォームをformでくくって、それにonsubmitイベントを登録した方がいいよ。
もちろんボタンは input type=”submit” で。
[get_chat_log.php : 43]
● $message = str_replace(“>”,”>”,$message);
● $message = str_replace(“<”,”<",$message);
→ htmlspecialchars で変換してるのに、この復号だけでは不完全じゃないかな。
PHP5以降なら、 htmlspecialchars_decode() で、
それより前ならちと面倒な手順が必要。
[get_chat_log.php : 52]
ここ、PHPの配列を汎用的にJSONに変換できる関数組もうぜ。
添字配列でも連想配列でも対応できるように。
[write_chat_log.php : 4]
● $msg = str_replace("\n","”,$_POST["message"]);
→ 改行コードを \n に決め打ちしてる。
改行コードはクライアントの環境によって
「\n」「\r」「\r\n」の3種類あるから、ちゅうい。