PHP – Ajaxでリアルタイムチャットなど

PHPを使用し、チャットなどを作る時に必要となる、「ウェブのページを再ロードすることなく、バックグランドでデータを取得して、表示内容だけを更新する」ための基礎知識をまとめました。

Ajaxの基本的なコード

ヘッダ部分に追加する行その1 JQuery


<script src='https://code.jquery.com/jquery-3.3.1.js'></script>

ヘッダ部分に追加する 行その2 コード本体


function checkMessage() {
    $.ajax({
        type: 'post',
        url: 'https://<< URL >>'
    }
    .then(
        function (data) {
            dispElm = document.getElementById('display_area');
            dispElm.html=data;
        },
        function () {
            $('#display_area').html('ERROR');
        }
    );
}

上記の「<< URL >>」の部分に読み込んできたいページやファイルのURLを入れます。
下線の部分のfunctionが処理が成功したときに呼ばれる関数で、引数として読み込んだデータが渡されます。
下線の部分のFunctionのすぐ下の引数なしのFunctionは、処理中にエラーが発生したり、URLで指定されたデータが見つからない(読み込めない)などの異常が発生した時に呼び出されます。

別の方法 コード本体

 上記と違う書き方として以下があります。この場合は、下記の例で行くと「xreq.readyState」には、XMLHttpRequestクライアントの状態が、「xreq.status」には、HTTP レスポンスステータスコードが、それぞれ入ってきます。
 下記ですと、「xreq.readyState (4)」は「操作完了」、「xreq.status(200)」は「リクエスト成功」を意味します。


function checkMessage() {
    let disp = document.getElementById('display_area');
    let xreq = new XMLHttpRequest();
    xreq.open('GET', 'https://<< URL >>', true);
    xreq.send(null);
    xreq.onreadystatechange = function() {
      if (xreq.readyState === 4) {
        if (xreq.status === 200) {
           disp.innerHTML = xreq.responseText;
        }
      }
    };
}

HTML body内に記述する表示エリアの例


<span id='display_area'></span>

処理を定期的に繰り返す方法

チャットアプリなどでは、表示情報を数秒おきに更新する必要があります。ウェブ画面を再ロードすることなく表示項目だけを更新したい場合は(今回の場合は上記サンプルの「chekMessage()」を繰り返し呼び出す仕組みが必要です。

数秒間隔で関数を呼び出す処理(ヘッダ部分に追加)


$(document).ready(function() {
    // 最初に関数を1回呼び出しておく
    checkMessage();
    // タイマー起動(chekMessage関数を3秒間隔で呼び出す)
    setInterval('checkMessage()', 3000);
});

「$(document).ready」で、ページが準備できた際に呼び出される関数内で、setIntervalを呼び出します。第一引数には呼び出す関数名を記述し、第二引数には呼び出す間隔をミリ秒単位で指定します。

タイマー処理を止める方法

setIntervalを使うと、指定した関数を永遠に呼び出し続けます。これを停止したい場合は、以下のようにします。


// タイマー起動(checkMessage関数を3秒間隔で呼び出す)
intvlId = setInterval('checkMessage()', 3000);

// タイマー停止
clearInterval(intvlId);

指定時間後に1回だけ関数を呼び出す

指定したミリ秒後に関数を1回だけ呼び出したい場合は、以下のコードを使います。


// checkMessage関数を3000ミリ秒(=3秒)後に1回だけ呼び出す
var timeId = setTimeout( 'checkMessage()', 3000 ) ;

// 設定したタイマーを停止する
clearTimeout(timeId);

コーリングシーケンス


// 指定したミリ秒ごとに処理を実行する
var intervalId = setInterval('コード',間隔(ミリ秒));
var intervalId = setInterval('関数名()',間隔(ミリ秒),関数の第一引数,第二引数,…);

// 実行中のIntervalタイマーをキャンセルする
clearInterval(intervalId);

// 指定したミリ秒後に処理を1回だけ実行する
var intervalId = setTimeout('コード',間隔(ミリ秒));
var intervalId = setTimeout('関数名()',間隔(ミリ秒),関数の第一引数,第二引数,…);

// 実行中のTimeoutタイマーをキャンセルする
clearTimeout(intervalId);

チャットを作る場合のヒント

PHPを使ってチャットアプリを作る場合は、上記のAjaxコード、定間隔の関数の実行という2つに加えて考えなければならない点は、チャット用のデータをどう保持するかです。JSONなどでチャットデータを持っても良いですが、面倒な場合は、PHPプログラムでチャットの全行を表示するように「&lt;table>」を表示するHTML文をファイルに書き出して、Ajaxでそのファイルを読み込んで、HTMLのspanやdivなどの表示領域(innerHTML)に放り込んでしまう方法があります。

ファイルを書き出す場合は、以下のPHP関数を利用します

ファイルを書き出す場合は、以下のPHP関数を利用します。他の人が書き込むタイミングと競合しないように、LOCK_EXオプションを指定して、排他をかけられるのがこのapiの使い勝手の良い所以です。


// テキストファイルを排他ロックしながら書き出す
file_put_contents($fullPath, $textData, LOCK_EX);

$fullPath = 書き出すファイルのフルパス名
$textData = 書き出す内容

毎回表示エリアを更新しないようにする

 チャットの表示エリアを数秒おきに更新すると、チャットの内容をコピペしたい場合などに選択したエリアがすぐにもとにもどってしまい非常に不便です。そこで、誰かがテキストを送信して、表示内容が変わったときだけ更新するようにすると、このような煩わしさはなくなります。そのため、下記のような考えかたで処理を実装すると、良いと思います。

チャットのサンプルソース


<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
  <script src='https://code.jquery.com/jquery-3.3.1.js'></script>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
  <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
  <title>Chatシステム</title>

  <script type='text/javascript'>
  function checkServerStatus() {
    let check = document.getElementById('refreshSw');
    let xhr = new XMLHttpRequest();
    xhr.open('GET', 'チャットデータファイルチェック処理のURL', true);
    xhr.send(null);
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) { 
          check.value = xhr.responseText;
        }
      }
    };
  }
  function updateServerStatus() {
    let check = document.getElementById('refreshSw');
    let dispAreaDiv = document.getElementById('dispArea');
    let xhr = new XMLHttpRequest();
    xhr.open('GET', 'チャットデータファイルのURL', true);
    xhr.send(null);
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) { 
        if (xhr.status === 200) { 
          dispAreaDiv.innerHTML=xhr.responseText;
          check.value = 'NODISP';
        }
      }
    };
  }
  function ajaxFunction() {
    let refrshSW = document.getElementById('refreshSw');
	    
    if (refrshSW.value === 'NODISP' ) {
      checkServerStatus();
    } else {
      updateServerStatus();
    }
  }

  $(document).ready(function() {
    ajaxFunction();
    setInterval('ajaxFunction()', 3000);
  });

</script>
</head>

<body style='color: #2285b1; background-color: white;'>

<form name='frm1' method='post' action='チャットを送信するPHPスクリプトのURL'>
<div><center>
<table>
<tr>
 <th>テキスト送信</th>
 <td>
   <textarea name=inp01 rows=5 style='width:95%;'></textarea>
 </td>
</tr>
</table>
<td>input type=submit value='send'></td>
</div>
</form>

<input type=hidden style='width:100%;' id='refreshSw' value=''>
<div id='dispArea'></div>

</body>
</html>

タイトルとURLをコピーしました