元
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>
上記は、質問内容のコードに機能を追加しただけのものです。
ただし、以下の問題があるので、せめて追加するべきコードは追加してください。
追加するべきコード
ファイルの存在チェック
このコードでは、ファイルが存在するかどうかのチェックがされていません。
そのためファイルが無いまま稼働させると、以下のエラーがでてしまいます。
これを防ぐため、ファイルを読み込む場合にファイルが存在するかをチェックしてください。
$data=file_get_contents($filename); //データファイル読み込み
↓
if(is_readable($filename)){
$data=file_get_contents($filename); //データファイル読み込み
}else{
$data=''; //ファイルが存在しない場合、内容を空文字列として処理する
}
$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
echo "$no. $data[$i]<br>\n<hr>";
↓
$comment=$data[$i];
$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
echo "$no. $comment<br>\n<hr>";
この変換は二重に行うと表示がおかしくなってしまうので、原則として表示する直前で行うべきです。
また、直前の表示以外での変換は一切行わないでください。
書き込み時の改行削除
HTMLでは<input type="text">
だからといって、改行が書き込まれないとは限りません。
フォームのHTMLはやろうとすれば簡単に改変できます。
(もちろん、PHPコードのHTMLが改変されるという意味ではなく、ブラウザにダウンロードされたHTMLを改変されるという意味ですが・・・)
それによって改行文字が送信されると、今のままではそのまま書き込まれてしまい、ファイルのデータがおかしくなってしまいます。
(この構造ですと、改行された各行が単一の書き込みとして表示されます。)
そこで、データファイルに保存する直前に改行文字を削除、
または、別の文字に変換する必要があります。
$body=$_POST['naiyou']; //入力データを取得
↓
$body=$_POST['naiyou']; //入力データを取得
//入力データに改行文字が混ざっていた場合、半角スペースに置換
$body=strtr($body,array(
"\r\n"=>' ',
"\r" =>' ',
"\n" =>' ',
));
または
$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に変換します。
$body=$_POST['naiyou']; //入力データを取得
↓
$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重に変換処理を行うのは好ましいものではありません。
つまりは、データファイルの文字コードが違うなどどいう事態そのものを起こすべきではありません。)
echo "$no. $data[$i]<br>\n<hr>";
↓
$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>";
$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
<meta charset="utf-8">
↓
<meta charset="Shift_JIS">
XSS対策コードを組み込んでいる場合、以下の箇所も変更してください。
$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
↓
$comment=htmlspecialchars($comment, ENT_QUOTES, 'SJIS');
EUC-JP
<meta charset="utf-8">
↓
<meta charset="EUC-JP">
XSS対策コードを組み込んでいる場合、以下の箇所も変更してください。
$comment=htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
↓
$comment=htmlspecialchars($comment, ENT_QUOTES, 'EUCJP');
UTF-8 BOMあり
この掲示板において、変更する箇所はありません。