【Ajax】チャットログを自動更新して表示させる方法!phpとjavascriptで実装

この記事からわかること

  • 完全自作で作るチャット機能
  • チャットログ自動更新方法
  • 相手のチャット送信されるたびに取得して表示させるコード
  • Ajaxの使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

PHP学習のためにPHPでチャット機能を自作する時に立ちはだかる壁がチャットログの自動更新です。

相手がチャットを送信したタイミングでこちらの履歴も更新させないとチャットになりませんよね

今回はチャット機能の根幹でもあるリアルタイムでの更新処理を解説していきます。

前提:チャット機能の作り方

PHPを主に使って完全自作でチャット機能を作る方法は⇩こちらの記事でも紹介しています。

今回はこの記事の続きでも、そしてこの記事とは別で作成していても使えるように解説していきたいと思います。とはいえ以下の条件だけは満たしていないと実装できないので注意してください。

チャットログ機能の必須前提機能

チャットログをファイルで管理

チャットログ機能の必須ファイル

チャットログ自動更新機能の仕様と流れ

まずは今回作成するチャットログ自動更新機能の仕様とプログラムの流れをまとめていきます。

チャットログ自動更新機能の仕様

チャットログ自動更新機能の流れ

  1. チャットページに通常アクセス
  2. ログファイルサイズをinput要素1に出力
  3. 0.1秒ごとに新しいログファイルサイズをinput要素2に出力
  4. input要素1の値とinput要素2の値が同じなら"3"を繰り返し
  5. 表示中のチャットログを削除し新しいチャットログを取得し再表示
  6. "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要素が表示されるようになります。

チャットページのHTML構造

しかしここではまだファイルサイズが更新されないのでAjaxを使って常に最新のファイルサイズを取得してaftFileSizeに格納します。

Ajax(エイジャックス/アジャックス)とはページをリロードしなくてもwebページの一部分だけを更新する仕組みのことです。詳しくは⇩こちらの記事をご覧ください!

【javascript】Ajaxの使い方とは?phpのPOSTへの受け渡し方とコードを徹底解説!

preFileSizeaftFileSize値が同じ時は常に現在のファイルサイズを取得し続け値が違う時はチャットログ表示を更新するという流れです。

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);

ここでのキーポイントはファイルサイズ値の差異によって分岐させているところです。

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です!

最後に

これで自動更新されるチャット機能が完成しました!

とはいえまだ完璧とはいえないので参考にしてもらえると嬉しいです。

自動更新されるチャット履歴

今回自作したチャット機能(←クリック)

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index