[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 のような仕組みを取り入れる必要があると思います。

[PHP]ライフゲームを作る

lifegame

ライフゲームとは数学的なシミュレーションで、次のルールに従ってセルの状態が変化します。

セル(一つのマス)が生きていて周囲に生きているセルが 1 以下(過疎)のときと 4 以上の時(過密)は消滅し、2 か 3 の時は生存します。

また、周囲に生きているセルが 3 つある時、そこに生きているセルが誕生します。

<?php
session_start();

function check_around($cell, $cell_x, $cell_y){
	$count = 0;
	for($y=-1;$y<=1;$y++){
		for($x=-1;$x<=1;$x++){
			if($x == 0 && $y == 0) continue;
			if(
				isset($cell[$cell_x + $x][$cell_y + $y]) &&
				$cell[$cell_x + $x][$cell_y + $y] == 1
			){
				$count++;
			}
		}
	}
	
	$result = 0;
	
	$current = $cell[$cell_x][$cell_y];
	
	if($current == 1 && $count <= 1) $result = 0;
	if($current == 1 && $count >= 4) $result = 0;
	if($current == 1 && ($count == 2 || $count == 3)) $result = 1;
	if($current == 0 && $count == 3) $result = 1;

	return $result;
}

$cell = array();

$size_x = 20;
$size_y = 20;

$reset = (!isset($_SESSION['cell'])) ? true : false;

if($reset === true || empty($_SESSION['cell'])){
	$_SESSION['cell'] = array();
	for($y=0;$y<$size_y;$y++){
		for($x=0;$x<$size_x;$x++){
			$cell[$x][$y] = mt_rand(0,1);
		}
	}
	
	
} else {
	$cell = $_SESSION['cell'];
}

if($reset !== true){
	$next_cell = array();
	for($y=0;$y<$size_y;$y++){
		for($x=0;$x<$size_x;$x++){
			$next_cell[$x][$y] = check_around($cell, $x, $y);
		}
	}
	$cell = $next_cell;
}

$_SESSION['cell'] = $cell;

header("Content-type:text/html;charset=utf-8");

for($y=0;$y<$size_y;$y++){
	for($x=0;$x<$size_x;$x++){
		if($cell[$x][$y] == 0){
			echo "□";
		} else {
			echo "■";
		}
	}
	echo "<br />\n";
}

あまり凝った組み方ではないですが、基本的なルールに従って進行します。
セッションを使っているのでブラウザを閉じるとリセットされ、ページを更新するごとに世代が変わります。
実験はローカルサーバーで行なって下さい。共用サーバーで更新ボタンを連打しないようにお願いします。

本来こういったものはタイマーで画面が書き換わるタイプの言語で組むのが一般的ですが、PHP で組むと、訪問者があるごとに世代を進行させたり、アクセス毎に餌のようなものを加えてみる等の処理ができて面白いのかもしれません。

Javascript と Canvas の組み合わせでリアルタイムで動作するタイプはこちらの記事で解説しています。

密集の度合いに応じて色を付けてみるのもなかなか綺麗です。

life

[PHP]生年月日(誕生日)から星座を調べる

誕生日を入力して占いなどで使われる12星座を割り出すプログラムです。
自動的に変換されるので生年月日(YYYY-MM-DD)で入力しても構いません。
厳密に日付の書式を定めるなら new DateTime() の代わりに DateTime::createFromFormat() を利用して下さい。

<?php
//誕生日
$birthday = "8/20";
$datetime = new DateTime($birthday);
echo getZodiacFromDateTime($datetime);

function getZodiacFromDateTime($datetime){
	$m = (int)$datetime->format("n");
	$d = (int)$datetime->format("j");
	 
	$zodiacs = array(
	// '名前', 月, 日 ~ 月, 日
		array('牡羊座',  3, 21,  4, 19),
		array('牡牛座',  4, 20,  5, 20),
		array('双子座',  5, 21,  6, 21),
		array('かに座',  6, 22,  7, 22),
		array('獅子座',  7, 23,  8, 22),
		array('乙女座',  8, 23,  9, 22),
		array('天秤座',  9, 23, 10, 23),
		array('蠍座',   10, 24, 11, 22),
		array('射手座', 11, 23, 12, 21),
		array('山羊座', 12, 22,  1, 19),
		array('水瓶座',  1, 20,  2, 18),
		array('魚座',    2, 19,  3, 20)
	);
	 
	foreach($zodiacs as $zodiac){
		list($name, $start_m, $start_d, $end_m, $end_d) = $zodiac;
		if(
			($m === $start_m && $d >= $start_d) ||
			($m === $end_m && $d <= $end_d)
		){
			return $name;
		}
	}
	return false;
}

結果:

獅子座

関数に DateTime オブジェクトを渡すと星座の名前を返します。
プログラム自体はシンプルに黄道十二星座の期間を格納した配列によって条件分岐させています。