Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

1行掲示板

Last updated at Posted at 2014-07-24

PHPファイルで一行掲示板を作成しているのですが、下記のテキストに何を入力すれば... - Yahoo!知恵袋

注意

このコードは、ファイルの文字コードがUTF-8 BOM無し(UTF-8N)であると想定したコードです。
Shift JISやEUC-JPで保存しないでください。
もしどうしてもUTF-8以外の文字コードを使いたい場合は、UTF-8 BOM無し(UTF-8N)以外の文字コードを使いたい場合を見てください。

コード

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>一行掲示板</title>
</head>
<body>
	<form id="x1" name="x1" method="post" action="">
		<input type="text" id="naiyou" name="naiyou" size="20">
		<input type="submit" value="書き込む">
	</form>
	<hr>
<?php
//データファイルのファイル名
$filename='data.txt';

if(!empty($_POST['naiyou'])){
	$data=file_get_contents($filename); //データファイル読み込み
	$body=$_POST['naiyou']; //入力データを取得
	$date=date('Y/m/d H:i:s'); //日付文字列を取得

	$body="$body ($date)\n".$data; //入力されたデータ、日付、改行、読み込んだテキスト

	$fp=@fopen($filename,'w'); //上書きモード
	flock($fp,LOCK_EX); //ファイルをロック
	fputs($fp,$body); //ファイルに書き込み
	fclose($fp); //ファイルを閉じる
}

$data=file($filename); //データファイルを読み込み、1行ずつ配列に格納
$line=count($data); //全件数を取得
for($i=0; $i<$line; $i++){
	$no=$line-$i-1; //全件数からカウント数を引き、逆順の番号を作る。0からカウントさせるため、更に1を引いている。
	echo "$no. $data[$i]<hr>\n";
}
?>
</body>
</html>

上記は、質問内容のコードに機能を追加しただけのものです。
ただし、以下の問題があるので、せめて追加するべきコードは追加してください。

追加するべきコード

ファイルの存在チェック

このコードでは、ファイルが存在するかどうかのチェックがされていません。
そのためファイルが無いまま稼働させると、以下のエラーがでてしまいます。

初期状態(ファイルなし)
初期状態

テスト書き込み後(ファイルなし)
テスト書き込み後

テスト書き込み後 2
テスト書き込み後 2

これを防ぐため、ファイルを読み込む場合にファイルが存在するかをチェックしてください。

18行目、書き込み処理のためのファイル読み込み
$data=file_get_contents($filename); //データファイル読み込み

ファイルがない場合、空の内容(空文字列)とする
if(is_readable($filename)){
	$data=file_get_contents($filename); //データファイル読み込み
}else{
	$data=''; //ファイルが存在しない場合、内容を空文字列として処理する
}
30行目、書き込み一覧の表示部分
$data=file($filename); //データファイルを読み込み、1行ずつ配列に格納

ファイルがない場合、空の配列を出力する
if(is_readable($filename)){
	$data=file($filename); //データファイルを読み込み、1行ずつ配列に格納
}else{
	$data=array(); //ファイルが存在しない場合、空の配列として処理する
}

なお、厳密にはis_readable関数はファイルの存在チェックではなく、
ファイルが存在し、なおかつ読み込み可能であるかを知る関数です。
が、ファイルが読み込めない場合もエラーは同じように出てしまうので、
ここではファイルは存在しないものとして一緒に扱ってしまいます。

XSS対策

この掲示板は、書き込まれた内容をそのままHTMLとして表示してしまいます。
これは、JavaScriptの書き込みも許すという非常に危険な脆弱性で、
巷ではXSS脆弱性と言われています。

これを回避するため、入力された文字列はHTMPエスケープを行い、ただの文字列としてHTMLに認識されるよう変換する必要があります。
具体的な解説はこちらをどうぞ→ 【PHP入門講座】 XSS攻撃への対策 - Qiita

34行目、書き込み一覧の表示部分
echo "$no. $data[$i]<br>\n<hr>";

表示する直前にHTML文字を無効化
$comment=$data[$i];
$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
echo "$no. $comment<br>\n<hr>";

この変換は二重に行うと表示がおかしくなってしまうので、原則として表示する直前で行うべきです。
また、直前の表示以外での変換は一切行わないでください。

書き込み時の改行削除

HTMLでは<input type="text">だからといって、改行が書き込まれないとは限りません。
フォームのHTMLはやろうとすれば簡単に改変できます。
(もちろん、PHPコードのHTMLが改変されるという意味ではなく、ブラウザにダウンロードされたHTMLを改変されるという意味ですが・・・)

それによって改行文字が送信されると、今のままではそのまま書き込まれてしまい、ファイルのデータがおかしくなってしまいます。
(この構造ですと、改行された各行が単一の書き込みとして表示されます。)

そこで、データファイルに保存する直前に改行文字を削除、
または、別の文字に変換する必要があります。

19行目、書き込み処理のための入力データ取得
$body=$_POST['naiyou']; //入力データを取得

strtr関数による置換(最速)
$body=$_POST['naiyou']; //入力データを取得
//入力データに改行文字が混ざっていた場合、半角スペースに置換
$body=strtr($body,array(
	"\r\n"=>' ',
	"\r"  =>' ',
	"\n"  =>' ',
));

または

str_replace関数による置換
$body=$_POST['naiyou']; //入力データを取得
//入力データに改行文字が混ざっていた場合、半角スペースに置換
$body=str_replace(array("\r\n", "\n", "\r"), ' ', $body);

または

正規表現による置換(低速)
$body=$_POST['naiyou']; //入力データを取得
//入力データに改行文字が混ざっていた場合、半角スペースに置換
$body=preg_replace('/\r\n|\r|\n/', ' ', $body);

strtr関数による文字列置換が最速で、私はこの方法を利用しています。
が、これはそれぞれの改行文字に対応した変換結果を指定しなくてはならず、記述が長いので他の記述を併記しました。

文字コードの自動変換

HTMLで文字コードがUTF-8であると指定していますが、
ブラウザによっては読み込めずに別の文字コードでHTMLを開く場合があります。
その場合、送信されてくる書き込み内容もUTF-8ではなく別の文字コードになってしまいます。

これを防ぐため、データファイルへの書き込み時に文字コードをUTF-8に変換します。

19行目、書き込み処理のための入力データ取得
$body=$_POST['naiyou']; //入力データを取得

文字コードをUTF-8に変換する
$body=$_POST['naiyou']; //入力データを取得
//文字コードをUTF-8に変換
$body=mb_convert_encoding($body, 'UTF-8', 'ASCII,JIS,UTF-8,CP51932,SJIS-win');
書き込み時の改行削除と組み合わせるとこうなる
$body=$_POST['naiyou']; //入力データを取得
//入力データに改行文字が混ざっていた場合、半角スペースに置換
$body=strtr($body,array(
	"\r\n"=>' ',
	"\r"  =>' ',
	"\n"  =>' ',
));
//文字コードをUTF-8に変換
$body=mb_convert_encoding($body, 'UTF-8', 'ASCII,JIS,UTF-8,CP51932,SJIS-win');

また、データファイルの内容が別の文字コードだった場合に備えて、表示側でも文字コードを変換してしまいます。
(この処理は本来、書き込み時に行われているはずのものであり、2重に変換処理を行うのは好ましいものではありません。
 つまりは、データファイルの文字コードが違うなどどいう事態そのものを起こすべきではありません。)

34行目、書き込み一覧の表示部分
echo "$no. $data[$i]<br>\n<hr>";

文字コードをUTF-8に変換する
$comment=$data[$i];
//文字コードをUTF-8に変換
$comment=mb_convert_encoding($comment, 'UTF-8', 'ASCII,JIS,UTF-8,CP51932,SJIS-win');
echo "$no. $comment<br>\n<hr>";
XSS対策と合わせるとこうなる
$comment=$data[$i];
//文字コードをUTF-8に変換
$comment=mb_convert_encoding($comment, 'UTF-8', 'ASCII,JIS,UTF-8,CP51932,SJIS-win');
$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
echo "$no. $comment<br>\n<hr>";

楽になるので知っておいて欲しい

ファイルの書き込み処理

提示されたコードでは、ファイルをロックしつつ書きこむためfopen関数が使われていますが、これはfile_put_contents関数で代用できます。
1行で書けるので、私はこちらをよく利用します。
PHPの環境がバージョン 5.1.0 以降の場合、ファイルをロックするLOCK_EXフラグがfile_put_contents関数で使えるので、こちらを使うほうが楽です。

$fp=@fopen($filename,'w'); //上書きモード
flock($fp,LOCK_EX); //ファイルをロック
fputs($fp,$body); //ファイルに書き込み
fclose($fp); //ファイルを閉じる

file_put_contents($filename, $body, LOCK_EX);

UTF-8 BOM無し(UTF-8N)以外の文字コードを使いたい場合

この掲示板に限らず、現在のPHPやHTML、その他のCGIプログラムなどでもUTF-8による記述が推奨されており、
UTF-8 BOM無し以外の記述はオススメできません。
が、どうしても他の文字コードでなければならない場合、以下を参考に該当箇所を変更してください。

Shift JIS

4行目のHTML記述部分
	<meta charset="utf-8">

4行目のHTML記述部分
	<meta charset="Shift_JIS">

XSS対策コードを組み込んでいる場合、以下の箇所も変更してください。

$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');

$comment=htmlspecialchars($comment, ENT_QUOTES, 'SJIS');

EUC-JP

4行目のHTML記述部分
	<meta charset="utf-8">

4行目のHTML記述部分
	<meta charset="EUC-JP">

XSS対策コードを組み込んでいる場合、以下の箇所も変更してください。

$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');

$comment=htmlspecialchars($comment, ENT_QUOTES, 'EUCJP');

UTF-8 BOMあり

この掲示板において、変更する箇所はありません。

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?