[PHP, JS]jQueryのAjaxによるPHP、Javascript間の送受信(JSON)

Ajax(非同期通信)を用いれば画面遷移なしでデータのやり取りができるため、最近では定番の技術として頻繁に用いられています。

今回は Javascript を使ってホストにデータを送信し、処理結果を受け取るというプログラムを JSON、JSONP を使うケース、同一ドメイン、クロスドメイン環境のケースに分けて説明します。

同一ドメイン間で JSON を使ったサンプル

クライアント

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Ajax</title>
        <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
        <script>
        $(function(){
            $("#button1").on('click', function(event){
                event.preventDefault();
                var param = { "text": "Hello" };

                $.ajax({
                    type: "GET",
                    url: "get.php",
                    data: param,
                    crossDomain: false,
                    dataType : "json",
                    scriptCharset: 'utf-8'
                }).done(function(data){
                    alert(data.text);
                }).fail(function(XMLHttpRequest, textStatus, errorThrown){
                    alert(errorThrown);
                });
            });
        });
        </script>
    </head>
    <body>
        <button id="button1">submit</button>
    </body>
</html>

ホスト

<?php
// Ajax以外からのアクセスを遮断
$request = isset($_SERVER['HTTP_X_REQUESTED_WITH'])
     ? strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) : '';
if($request !== 'xmlhttprequest') exit;

$text = filter_input(INPUT_GET, 'text');
header('Content-type: application/json; charset=utf-8');
echo json_encode(['text' => $text . ', World!']);

出力結果

Hello, World!

「Hello」と書かれたボタンを押すと、JavaScript から PHP に「Hello」という文字列が送信されます。
PHP はそれに「, World!」という文字列を加えて返します。
Javascript は結果を受け取り「Hello, World!」と表示する流れです。

非クロスドメイン環境で ajax を使うと HTTP_X_REQUESTED_WITH というヘッダー情報が送信されます。これをもとに Ajax 以外の方法をブロックしています。

「type: "POST"」とした場合は POST メソッドで送信されるのでホスト側も「$text = filter_input(INPUT_POST, "text");」のようにして受け取ることになります。

JSONP を使ったクロスドメイン Ajax

JSONP(JSON with padding)を使うとクロスドメイン環境での Ajax が可能です。
クライアントの dataType に jsonp を指定し、type を GET に変更します。

クライアント

<script>
$(function(){
    $("#button1").on('click', function(event){
        event.preventDefault();
        var param = { "text": "Hello" };

        $.ajax({
            type: "GET",
            url: "get-jsonp.php",
            data: param,
            crossDomain: true,
            dataType : "jsonp",
            scriptCharset: 'utf-8'
        }).done(function(data){
            alert(data.text);
        }).fail(function(XMLHttpRequest, textStatus, errorThrown){
            alert(errorThrown);
        });
    });
});
</script>

ホスト

<?php
text = filter_input(INPUT_GET, 'text');
$callback = filter_input(INPUT_GET, 'callback');
$callback = htmlspecialchars(strip_tags($callback));
 
$param = ['text' => $text . ", World!"];

header('Content-type: text/javascript; charset=utf-8');
printf("{$callback}(%s)", json_encode( $param ));

JSONP ではクライアントから callback というデータが送信されます。これを javascript の関数を呼び出すような形にして出力するとデータの受け渡しができるようになります。

JSON を使ったクロスドメイン Ajax

JSON を使ってクロスドメイン通信を行う場合は次のヘッダーをサーバー側で出力しておく必要があります。

header("Access-Control-Allow-Origin: *");

「*」はすべてのサイトからの通信を許可するということを意味します。特定のサイトからのみリソースの利用を許可する場合は次のようにして URL を指定します。

header("Access-Control-Allow-Origin: http://foo.com http://bar.com");

不特定多数のクライアントにリソースシェアリングを行う場合は認証機能をつけることについても検討して下さい。

参考:
http://www.learningjquery.com/2010/03/detecting-ajax-events-on-the-server/

[PHP][jQuery]Ajax(非同期通信)を使ったチャット

あまり JavaScript の Ajax に関して経験がないので、
ちょっとした練習としてチャットのようなものを作ってみました。
練習用の単純なプログラムですので脆弱性等の問題があります。実際のサーバーでは運用しないで下さい。
また、Google Chrome ではローカルファイルの読み込みに制限があります。

非同期通信を利用するために jQuery を利用しています。

・表示部分(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" />
<title>Chat</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<style type="text/css">
#log {
	border: 1px solid #AAA;
	height:400px;
	width:600px;
	overflow: scroll;
}
</style>

</head>

<body>

<script type="text/javascript">

function load_log()
{
	$.ajax({
		type: 'post',
		url: './log.txt',
		success: function( data ){
			log = data.replace(/[\n\r]/g, "<br />");
			$('#log').html(log);
		}
	});
}

function write_message()
{
	$.ajax({
		type: 'post',
		url: 'write.php',
		data: {
			'message' : $("#message").val()
		},
		success: function(){
			load_log();
			$("#message").val('');
		}
	});
}

$(document).ready(function()
{
	load_log();
	setInterval('load_log()', 5000);
});

</script>

<form method="post" onsubmit="write_message();return false;">
  <input id="message" name="message" type="text" value="" />
  <input type="button" value="送信" onclick="write_message()">
</form>

<div id="log"></div>

</body>
</html>

・write.php(書き込み部分)

<?php
// Ajax以外からのアクセスを遮断
$request = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ?
	strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) : '';
if($request !== 'xmlhttprequest') exit;

$message = (isset($_POST['message']) && is_string($_POST['message'])) ?
	htmlspecialchars($_POST['message'], ENT_QUOTES) : "";
if($message == "") exit;

$max   = 30;		// 行の上限
$count = 0;
$log   = '';

$fp = fopen('log.txt', 'r');
if(flock($fp, LOCK_SH)){
	while(!feof($fp)){
		if($count >= $max - 1) break;
		$log .= fgets($fp);
		$count++;
	}
	flock($fp, LOCK_UN);
}
fclose($fp);

$log = date("Y-m-d H:i:s") . ' - ' . $message . "\n" . $log;
file_put_contents('log.txt', $log, LOCK_EX);

5 秒間隔で log.txt を読み込み、表示しているだけなので負荷は高めです。
本来であれば JSON で日付や発言者等の細かいパラメータを含めて渡したほうがよいでしょう。

実用的なチャットにするためには、ログイン機能の実装と、負荷対策として Comet のような仕組みを取り入れる必要があると思います。