質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.10%

一言掲示板 ファイル操作 文字数オーバーを表示しない方法

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 5,069

score 12

前提・実現したいこと

phpで一言掲示板を作成しています。

以下が要件です
1,利用者が名前とコメントを入力し、発言できる。
2,利用者の過去の発言内容をテキストファイルで管理する。
全ての利用者の過去の発言内容を一覧で表示する。一覧には「名前」「コメント」「発言日時」の3つを1行ずつ表示する。
3,利用者の名前は最大20文字以内まで発言できる。もし20文字より多くの文字を入力して発言した場合はエラーメッセージを表示し、発言できないようにする。
4,利用者のコメントは最大100文字以内まで発言できる。もし100文字より多くの文字を入力して発言した場合はエラーメッセージを表示し、発言できないようにする。
5,利用者の名前とコメントは必ず文字が入力される。もし名前あるいはコメントが未入力で発言した場合はエラーメッセージを表示し、発言できないようにする。

発生している問題・エラーメッセージ

3と4の要件に関して
エラーメッセージは表示出来るのですが
「発言できないようにする」方法が分かりません。
現状では、エラーメッセージと、文字数制限を無視した発言が、同時に表示されてしまいます。
例:
・名前を入力してください(エラーメッセージ)
・ :こんにちは! -2017-05-22 23:24:34
(本来は、田中太郎:こんにちは!-2017-05-22 23:24:34 というような形で表示される)

該当のソースコード

<?php

$filename = './bbs.txt';
$name = 0;
$comment = 0;
$name_max = 20;
$comment_max = 100;
$log = date('-Y-m-d H:i:s');

if ($_SERVER['REQUEST_METHOD'] === 'POST'){

    $name = $_POST['name'];

    if (($fp_name = fopen($filename , 'a')) !== FALSE){
        if ((strlen($name) <= 20) || (strlen($name) !== 0)){
            if (fwrite($fp_name , "$name:") === FALSE){
                print 'ファイル書き込み失敗' . $filename;
            }
        }
        fclose($fp_name);
    }

    $comment = $_POST['comment'];

    $text = "$comment $log\n";
    if (($fp_comment = fopen($filename , 'a')) !== FALSE){
        if ((strlen($comment) <= 20) || (strlen($comment)) !== 0){
            if (fwrite($fp_comment , " $text") === FALSE){
                print 'ファイル書き込み失敗' . $filename;
            }
        }
        fclose($fp_comment);
    }
}

$data = array();

if (is_readable($filename) === TRUE){
    if (($fp = fopen($filename , 'r')) !== FALSE){
        while (($tmp = fgets($fp)) !== FALSE){
        $data[] = htmlspecialchars($tmp);
        }
        fclose($fp);
    }
}else{
        $data[] = 'ファイルがありません';
}

?>
<!DOCTYPE html>
<html lang = "ja">
<head>
    <meta charset = "UTF-8">
    <title>ひとこと掲示板</title>
</head>
<body>
    <h1>ひとこと掲示板</h1>

<?php if (strlen($name) > $name_max){?>
    <ul>
        <li>
<?php print '名前は20文字以内で入力してください';?>
        </li>
    </ul>
<?php } ?>

<?php if(strlen($name) === 0){?>
    <ul>
        <li>
<?php print '名前を入力してください';?>
        </li>
    </ul>
<?php } ?>


<?php if (strlen($comment) > $comment_max){?>
    <ul>
        <li>
<?php print 'ひとことは100文字以内で入力してください';?>
        </li>
    </ul>
<?php } ?>

<?php if(strlen($comment) === 0){?>
    <ul>
        <li>
<?php print 'ひとことを入力してください';?>
        </li>
    </ul>
<?php } ?>

    <form method = "POST">
        名前:<input type = "text" name = "name" size = "20" value = "">
        ひとこと:<input type = "text" name = "comment"  size = "55" value = "">
        <input type = "submit" name = "submit" value = "送信">
    </form>
<?php
foreach ($data as $read) { 
?>
    <ul>
        <li>
<?php print $read; ?>
        </li>
    </ul>
<?php 
}
?>
</body>
</html>

試したこと

・上記のコード(11行目,21行目辺り)でも試してあるように、ファイルへ書き込みを行う際に
指定の文字数以内もしくは、フォームへ入力した文字が0文字でない場合
のみ fwrite()の処理を行えるように、if文で条件分けした。

・同様に、指定の文字数以内もしくは、フォームへ入力した文字が0文字でない場合のみ、fgets()の処理を行えるように、while文をif文で条件分けした

・同様に、指定の条件の場合のみ、配列dataに$tmp(読み込まれたファイル)が代入されるように、$data[]をif文で条件分けした。

以上3つ共試した結果、どの方法でも「発生している問題・エラーメッセージ」に記載したような結果になってしまいました。

大変お手数かと思いますが、ご教授お願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+5

判定式が誤っています。

if ((strlen($name) <= 20) || (strlen($name) !== 0)){

↓

if ((strlen($name) <= 20) && (strlen($name) !== 0)){

1文字以上 かつ 20文字以下 なので && が正解です。

あと $name_max をせっかく定義しているので、それに書き換えます。

if ((strlen($name) <= $name_max) && (strlen($name) !== 0)){

さらに、strlen() 関数は日本語のようなマルチバイト文字に対応していません。たとえば名前欄に "勘解由小路左衛門三郎" (10文字) を入力しても、"名前は20文字以内で入力してください" と怒られてしまいます。

ひらがな・漢字などのマルチバイト文字列を扱うmbstring というライブラリがあります。strlen のマルチバイト対応版として mb_strlen() が用意されているのでこれを使います。

if ((mb_strlen($name) <= $name_max) && (mb_strlen($name) !== 0)){

ちなみに本題ではありませんが、いくつかセキュリティ上の問題があります

  • 悪意のあるユーザーが「ひとこと」に改行を含んだ文字を入力して送信すると、簡単にデータファイル(bbs.txt)の構造が壊れてしまいます
  • 厳密にアクセス権を設定しない限り、ユーザーからデータファイル(bbs.txt)が覗かれてしまいます

今回のプログラムでは表示が崩れたり、投稿内容が書かれた生テキストが見えるだけで問題になりませんが、ユーザーのIPアドレスや非公開なメールアドレス等を記録するようになると問題がでてきます。

また htmlspecialchars() はシングルクオートをエスケープしません。今回の使いどころでは問題は起きませんが、場合によってはXSS攻撃を許してしまうため、オプションを指定してより強力なエスケープを行うことをおすすめします。(文字コードの指定は最近のPHPだと最初からUTF-8なので問題ないのですが念のため)

htmlspecialchars($tmp);

↓

htmlspecialchars($tmp, ENT_QUOTES, 'UTF-8');

実践的なアプリケーションを作っていきたいのではあれば、セキュリティについても併せて学習をすすめてください。PHP界隈では「体系的に学ぶ 安全なWebアプリケーションの作り方」いわゆる徳丸本という大変有名な名著があるので興味があれば読んでみてください。(少し内容が古いですが、現在でも基礎知識は十分通じます)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/22 12:23

    ご返信が送れてしまい、申し訳ありません。具体的で丁寧なご解説、本当に有り難うございます!本題だけでなく、セキュリティの面でも、アドバイスを頂き、とても感謝しています。

    キャンセル

0

上記の回答に沿って、修正した後
①名前OK-コメント空白
②名前OK-コメント100文字オーバー

の2つの条件で送信ボタンを押した場合に、エラーメッセージと共に
名前だけが出力されてしまうという問題が発生してしまいました。
例: 
・ひとことは100文字以内で入力してください。(エラーメッセージ) 
・田中太郎:

※(この状態で、次に、名前・ひとこと共に正常な文字数で入力した場合
・田中太郎:田中太郎: こんにちは!-2017-05-22 23:24:34 と投稿されてしまう)

そこで、

if ($_SERVER['REQUEST_METHOD'] === 'POST'){

    $name = $_POST['name'];
    $comment = $_POST['comment'];

    if (($fp_name = fopen($filename , 'a')) !== FALSE){
        if (((mb_strlen($name) <= $name_max) && (mb_strlen($name) !== 0)) && ((mb_strlen($comment) <= $comment_max) && (mb_strlen($comment) !== 0))){
            if (fwrite($fp_name , "$name:") === FALSE){
                print 'ファイル書き込み失敗' . $filename;
            }
        }
        fclose($fp_name);
    }

    $text = "$comment $log\n";
    if (($fp_comment = fopen($filename , 'a')) !== FALSE){
        if (((mb_strlen($comment) <= $comment_max) && (mb_strlen($comment) !== 0)) && ((mb_strlen($name) <= $name_max) && (mb_strlen($name) !== 0))){
            if (fwrite($fp_comment , " $text") === FALSE){
                print 'ファイル書き込み失敗' . $filename;
            }
        }
        fclose($fp_comment);
    }
}


というコードで試したところ、問題なく修正されました。
修正前のコードでは、
$comment = $_POST['comment']; を
$fp_nameへ書き込みを行うコードの後($fp_commentの直前)に
書いていたため、
$fp_nameへ書き込みを行う際の条件分岐の時点で、$commentに、あらかじめ宣言しておいた
「0」が代入されていて、文字数が「1」になり
このコードでいう5行目の、条件式が通ってしまう
状態になっていたところが問題でした。

結論としましては、
$comment = $_POST['comment']; を
$name = $_POST['name'];と同じ位置に移動させてあげることによって
問題が解決できました。

・プログラムは上から順番に処理をする
・変数は、使いたいところより前(上)で宣言、代入する

といった基本的なところが抜けていたので、以後気をつけたいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る