【Ajax】チャットログを自動更新して表示させる方法!phpとjavascriptで実装
この記事からわかること
- 完全自作で作るチャット機能
- チャットログの自動更新方法
- 相手のチャットが送信されるたびに取得して表示させるコード
- Ajaxの使い方
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
PHP学習のためにPHPでチャット機能を自作する時に立ちはだかる壁がチャットログの自動更新です。
相手がチャットを送信したタイミングでこちらの履歴も更新させないとチャットになりませんよね
今回はチャット機能の根幹でもあるリアルタイムでの更新処理を解説していきます。
前提:チャット機能の作り方
PHPを主に使って完全自作でチャット機能を作る方法は⇩こちらの記事でも紹介しています。
今回はこの記事の続きでも、そしてこの記事とは別で作成していても使えるように解説していきたいと思います。とはいえ以下の条件だけは満たしていないと実装できないので注意してください。
チャットログ機能の必須前提機能
チャットログをファイルで管理
チャットログ機能の必須ファイル
- chat.php:チャット機能のトップページ
- chatlog.json:チャットログ格納用
- main.js:javascriptファイル
- chatlog.php:Ajaxで通信するログとファイルサイズ出力ファイル
チャットログ自動更新機能の仕様と流れ
まずは今回作成するチャットログ自動更新機能の仕様とプログラムの流れをまとめていきます。
チャットログ自動更新機能の仕様
- ログが増えたかどうかを0.1秒ごとに確認する
- 確認方法はファイルサイズを比較する
- チャットログが増えればログを都度表示
- Ajaxを使って実装
- Ajaxを使っても画面が少しチラつくので必要な時だけ非同期通信
チャットログ自動更新機能の流れ
- チャットページに通常アクセス
- ログファイルサイズをinput要素1に出力
- 0.1秒ごとに新しいログファイルサイズをinput要素2に出力
- input要素1の値とinput要素2の値が同じなら"3"を繰り返し
- 表示中のチャットログを削除し新しいチャットログを取得し再表示
- "2"に戻る
ざっくり説明するとこのような仕様と流れで作成していきます。肝となるのはファイルサイズの取得です。
今回のチャット機能はチャットログをファイル(JSONファイル)で蓄積していくのでファイルサイズが増える=チャットログが増えていると識別することができます。
JSONファイルとはjavascriptの記法を用いたテキストファイルのことです。データの追加、削除、操作がしやすくテキストファイルなので容量も軽く利便性も高い有用なファイル形式です。
チャットログ自動更新機能の作成方法
それではここから実際にコーディングしていきたいと思います。
まずはPHPでファイルサイズを取得する方法です。ファイルサイズは旧と新の2つを比較するので2つの格納場所を用意します。旧は変数$filesize
新はスーパグローバル変数$_SESSION['filesize']
に格納していきます。
$J_file = "chatlog.json"; // チャットログパス(相対パス)を変数に格納
session_start(); // セッションを使うので明示的にスタート
$filesize = filesize($J_file); // 旧⇦ファイルサイズを取得
if(empty($_SESSION['filesize'])){
// 新が空なら(1番最初のアクセスなら)新にファイルサイズを格納
$_SESSION['filesize'] = $filesize ;
}
最初にチャットログファイルの相対パスを格納します。セッションを使うのでセッションをスタートさせfilesize関数
でファイルサイズを取得します。引数に取得したいファイルパスを指定するだけでファイルサイズが返ってきます。
empty関数
は引数に指定した変数が空であればtrue
を返す関数です。
ログがあれば表示させる
次にチャットログがあればログを表示させかつ2種類のファイルサイズをinput要素として構築します。なければ、2種類のファイルサイズのみをinput要素として構築しておきます。
input要素はtype="hidden"
とすることでページには表示させないようにしておきます。
構築したHTML構造は変数$result
に格納しておきます。
// チャットログがあればチャットログとファイルサイズ(新旧)を表示
if($file = file_get_contents($J_file)){
$file = json_decode($file);
$array = $file->chatlog;
// チャットログを全て取り出しHTMLツリーを構築
foreach($array as $object){
if(isset($result)){
// 第二回目以降
$result = $result.'<div class="'.$object->person.'"><p class="chat">'.str_replace("\r\n","<br>",$object->text).'<span class="chat-time">'.$object->time.'</span></p><img src="'.$object->imgPath.'"></div>';
}else{
// 第一回目
$result = '<div class="'.$object->person.'"><p class="chat">'.str_replace("\r\n","<br>",$object->text).'<span class="chat-time">'.$object->time.'</span></p><img src="'.$object->imgPath.'"></div>';
}
}
// 現在のファイルサイズと旧ファイルサイズを表示
$result = $result .'<input id="preFilesize" type="hidden" value="'.$_SESSION['filesize'].'"><input id="aftFilesize" type="hidden" value="'.$filesize.'">';
}else{
// チャット履歴がない場合はチャットが増えたときに備える
$result = '<input id="preFilesize" type="hidden" value="'.$_SESSION['filesize'].'"><input id="aftFilesize" type="hidden" value="'.$filesize.'">';
}
あとはログを表示させたい箇所に$result
を持ってくるだけです。今回は"chat-area"
の中に表示させておきます。
<div class="chat-area" id="chat-area">
<?php echo $result;?>
</div>
チャットログの構造
チャットログは以下のような構造で管理しています。8行目の$result
に格納しているのはただデータを取り出しているだけですのでそれぞれのログにあったデータを取り出してあげてください。
⇩{"連想配列1"}
【1階層】⇩連想配列1→{"chatlog":"[配列1]"}
【2階層】⇩[配列]1→["連想配列2","連想配列2","連想配列2"....]
【3階層】連想配列2(チャットログ)→{"person":"person1","imgPath":"image/person1.png","time":"16:20","text":"チャットの本文"}
{"chatlog":
[
{"person":"person2","imgPath":"..\/image\/person2.png","time":"22:26","text":"\u30c1\u30e3\u30c3\u30c8\u6a5f\u80fd\u3092\u81ea\u4f5c\u3057\u3066\u3044\u307e\u3059\u3002"},
{"person":"person1","imgPath":"..\/image\/person1.png","time":"22:27","text":"\u30c1\u30e3\u30c3\u30c8\u30ed\u30b0\u306fJSON\u30d5\u30a1\u30a4\u30eb\u3067\u683c\u7d0d\u3057\u3066\u3044\u307e\u3059\u3002"}
]
}
3階層目にチャットログを蓄積しています。画像や送信時間などチャットログに表示させたい情報を蓄積していきます。
Ajaxを使う場所
ここまででHTML上に以下のようにinput要素が表示されるようになります。
しかしここではまだファイルサイズが更新されないのでAjaxを使って常に最新のファイルサイズを取得してaftFileSize
に格納します。
Ajax(エイジャックス/アジャックス)とはページをリロードしなくてもwebページの一部分だけを更新する仕組みのことです。詳しくは⇩こちらの記事をご覧ください!
【javascript】Ajaxの使い方とは?phpのPOSTへの受け渡し方とコードを徹底解説!
preFileSize
とaftFileSize
の値が同じ時は常に現在のファイルサイズを取得し続け、値が違う時はチャットログ表示を更新するという流れです。
Ajaxでチャットログ自体を0.1秒ごとに更新し続けても良いですが、チャットログ全体を更新し続けるため画面がどうしてもチラついてしまいます。なのでチャットログ全体を更新した方が良いか(新しいログがあるか)をファイルサイズを用いて識別しているのです。
Ajaxでファイルサイズを更新処理
では実際にAjaxを使った更新処理のjavascriptコードを解説していきます。今回はチャットログなのでいつログが増えるか分かりません。なのでsetIntervalメソッド
で発火のタイミングを0.1秒ごとにコントロールしておきます。
Ajaxの処理は関数resultLog
にまとめて呼び出しやすくしておきます。
document.addEventListener('DOMContentLoaded', function() {
// ファイルサイズ更新Ajaxを0.1秒ごとに実行
setInterval(resultLog, 1000);
function resultLog() {
let preFS = document.getElementById('preFilesize');
let aftFS = document.getElementById('aftFilesize');
if (preFS.value === aftFS.value) {
// ファイルサイズが同じ場合
// XMLHttpRequestオブジェクトを生成
let xhr = new XMLHttpRequest();
// 非同期通信を開始
xhr.open('GET', 'chatlog.php?ajax=' + "OFF", true);
xhr.send(null);
// onreadystatechange→通信の状態が変化したタイミングで呼び出されるイベントハンドラー
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) { //通信が完了
// readyState→HTTP通信の状態を取得
if (xhr.status === 200) { //通信が成功
// 現在のファイルサイズを取得し新しいファイルサイズのみ更新
aftFS.value = xhr.responseText;
}
}
}
} else {
// ファイルサイズが違う場合
let chatArea = document.getElementById('chat-area');
// XMLHttpRequestオブジェクトを生成
let xhr = new XMLHttpRequest();
// 非同期通信を開始
xhr.open('GET', 'chatlog.php?ajax=' + "ON", true);
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) { //通信が完了
if (xhr.status === 200) { //通信が成功
// チャットログを更新+FSも更新
chatArea.insertAdjacentHTML('afterbegin', xhr.responseText);
// チャット履歴の1番下にフォーカスを持ってくる
let chatAreaHeight = chatArea.scrollHeight;
chatArea.scrollTop = chatAreaHeight;
// チャット履歴の1番下にフォーカスを持ってくる
}
} else { //通信が完了する前
// 通信完了前に最初のチャットログとFSをリセット
chatArea.textContent = '';
}
}
};
};
}, false);
ここでのキーポイントはファイルサイズ値の差異によって分岐させているところです。
- 値が同じ→【chatlog.php?ajax=OFF】→aftFSのみ更新(9〜25行目)
- 値が違う→【chatlog.php?ajax=ON】→チャットログとpre+aftFS更新(27〜51行目)
Ajaxで通信する際にGETの値を変えることで処理を分岐させます。
ちなみに41行目で更新したチャットログは1番下に追加され表示されますがそのままではスクロールしないと見えない位置になってしまうため、44,45行目でスクロールできる範囲の1番下に表示を持ってくるようにしています。
overflow:scrollさせた要素を1番下にスクロール!位置座標を調整してチャットに使えるUIにしよう!
Ajax通信先のPHPファイルでの分岐処理
続いてAjax通信するPHPファイル(chatlog.php)の中身を記述していきます。
ここでは特別なことはありません。先程の流れをコードで表現するとこのようになります。
<?php
$J_file = "chatlog.json";
$filesize = filesize($J_file); // 最新のファイルサイズ
if(isset($_GET['ajax']) && $_GET['ajax'] === "ON"){
// ファイルサイズが違った時
if($file = file_get_contents($J_file)){
// 新しいチャットログのHTMLを構築
$file = json_decode($file);
$array = $file->chatlog;
foreach($array as $object){
if(isset($result)){
$result = $result.'<div class="'.$object->person.'"><p class="chat">'.str_replace("\r\n","<br>",$object->text).'<span class="chat-time">'.$object->time.'</span></p><img src="'.$object->imgPath.'"></div>';
}else{
$result = '<div class="'.$object->person.'"><p class="chat">'.str_replace("\r\n","<br>",$object->text).'<span class="chat-time">'.$object->time.'</span></p><img src="'.$object->imgPath.'"></div>';
}
}
}
// チャットリセットされた時もファイルサイズが一瞬違うため9行目にfalseが返ってもinputを表示させる
$result = $result .'<input id="preFilesize" type="hidden" value="'.$filesize.'"><input id="aftFilesize" type="hidden" value="'.$filesize.'">';
echo $result;
exit;
}elseif(isset($_GET['ajax']) && $_GET['ajax'] === "OFF"){
// ファイルサイズが同じ時
echo $filesize;
exit;
}
Ajaxでは非同期通信したPHPファイルに出力されている値(ページとして見た時に表示されている部分)をxhr.responseText
で取得することができます。
なので更新したい値をecho
などで出力すればOKです!
最後に
これで自動更新されるチャット機能が完成しました!
とはいえまだ完璧とはいえないので参考にしてもらえると嬉しいです。
今回自作したチャット機能(←クリック)まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。