Rewish

PHPとjQueryでCometっぽいモノを実装する

クライアントからのリクエストに対してすぐに応答せず溜めておき、何らかのタイミングを見計らって応答する技術。

Comet - Wikipedia

例えばチャットなんかで使うと、相手が発言したタイミングでレスポンスが返ってくるので、リアルタイムに会話が出来たりする訳です。

※あくまでも実装の一例なので、このままでは利用シーンがかなり限定される事をご留意下さい。

注意事項

以下の実装方法は、知識的なコストがあまり必要ない替わりに、サーバー的なコストを多く消費します。

共有レンタルサーバーなんかで使うと、アカウント制限・停止などの処置がとられる場合があるので、共有で使うスペースなどでの使用は避け、専用サーバーや自前のサーバーなどで使用して下さい。

Comet動作の流れ

大まかな流れはこんな感じです。

  1. Ajax(XMLHttpRequest)を利用してサーバー(PHP)にリクエストを送る
  2. リクエストを受けたPHPはトリガーイベントが発生するまで待機(ループ)
  3. PHPはトリガーイベントが発生したらレスポンスを返し終了
  4. クライアント側はレスポンスを受け取り、必要ならば再度リクエスト(1へ)

2はwhileで無限ループして仮想待機状態にし、sleep()で若干負荷を軽減する感じ。

PHPのサンプルコード

とりあえず表示確認できればいいので簡単に。

以下のコードをcomet.phpという名前で保存する。

<?php
// データを書き込むファイル
define('DATA_FILE', 'data.log');

/**
 * データを取得
 */
function getData() {
	return file_get_contents(DATA_FILE);
}

/**
 * 更新チェック
 *
 * 対象データに変化が無ければループし続ける。
 * 変化が有れば新しいデータ追加した全てのデータを返す。
 */
function getUpdatedData() {
	$data = getData();
	$temp = $data;
	while ($temp === $data) {
		$temp = getData();
		sleep(1);
	}
	return $temp;
}

/**
 * データ追加
 *
 * 新しいデータを追加して全てのデータを返す。
 */
function pushData($data) {
	if (!empty($data)) {
		$data = str_replace(array("\n", "\r"), '', $data)
				. ' [' . date('c') . ']' . PHP_EOL;
		file_put_contents(DATA_FILE, $data, FILE_APPEND|LOCK_EX);
	}
	return getData();
}

if (isset($_GET['mode'])) {
	// モードの振り分け
	switch ($_GET['mode']) {
		// データを取得
		case 'view':
			$data = getData();
			break;

		// 更新チェック
		case 'check':
			$data = getUpdatedData();
			break;

		// データを保存
		case 'add':
			$data = pushData($_POST['data']);
			break;
	}

	// 結果を表示
	echo nl2br(htmlspecialchars($data, ENT_QUOTES));
}

jQueryのサンプルコード

これまた簡単に。

jQuery(function($) {
  var $view = $('#view'),
      $data = $('input[name="data"]');

  /**
   * データ取得
   */
  function getData() {
    $.post('comet.php?mode=view', {}, function(data) {
      $view.html(data);
      checkUpdate();
    });
  }

  /**
   * 更新チェック
   */
  function checkUpdate() {
    $.post('comet.php?mode=check', {}, function(data) {
      $view.html(data);
      checkUpdate();
    });
  }

  $('#add').submit(function(event) {
    event.preventDefault();
    $.post('comet.php?mode=add', {data: $data.val()}, function(data) {
      $data.val('');
    });
  });

  getData();
});

IEのキャッシュ対策のためにPOSTでリクエスト。

HTMLのサンプルコード

<dl>
  <dt>データ<dt>
  <dd id="view">ここにデータ</dd>
</dl>

<form id="add" action="comet.php?mode=add" method="post">
  <input type="text" name="data" value="" />
  <input type="submit" value="追加" />
</form>

利用シーン

社内システムや小規模会員制システムなど、リクエスト数が把握出来る時などにサクっと組み込むと幸せ。