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

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

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

6939閲覧

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

gorira6

総合スコア12

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2017/05/21 15:30

###前提・実現したいこと
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

1<?php 2 3$filename = './bbs.txt'; 4$name = 0; 5$comment = 0; 6$name_max = 20; 7$comment_max = 100; 8$log = date('-Y-m-d H:i:s'); 9 10if ($_SERVER['REQUEST_METHOD'] === 'POST'){ 11 12 $name = $_POST['name']; 13 14 if (($fp_name = fopen($filename , 'a')) !== FALSE){ 15 if ((strlen($name) <= 20) || (strlen($name) !== 0)){ 16 if (fwrite($fp_name , "$name:") === FALSE){ 17 print 'ファイル書き込み失敗' . $filename; 18 } 19 } 20 fclose($fp_name); 21 } 22 23 $comment = $_POST['comment']; 24 25 $text = "$comment $log\n"; 26 if (($fp_comment = fopen($filename , 'a')) !== FALSE){ 27 if ((strlen($comment) <= 20) || (strlen($comment)) !== 0){ 28 if (fwrite($fp_comment , " $text") === FALSE){ 29 print 'ファイル書き込み失敗' . $filename; 30 } 31 } 32 fclose($fp_comment); 33 } 34} 35 36$data = array(); 37 38if (is_readable($filename) === TRUE){ 39 if (($fp = fopen($filename , 'r')) !== FALSE){ 40 while (($tmp = fgets($fp)) !== FALSE){ 41 $data[] = htmlspecialchars($tmp); 42 } 43 fclose($fp); 44 } 45}else{ 46 $data[] = 'ファイルがありません'; 47} 48 49?> 50<!DOCTYPE html> 51<html lang = "ja"> 52<head> 53 <meta charset = "UTF-8"> 54 <title>ひとこと掲示板</title> 55</head> 56<body> 57 <h1>ひとこと掲示板</h1> 58 59<?php if (strlen($name) > $name_max){?> 60 <ul> 61 <li> 62<?php print '名前は20文字以内で入力してください';?> 63 </li> 64 </ul> 65<?php } ?> 66 67<?php if(strlen($name) === 0){?> 68 <ul> 69 <li> 70<?php print '名前を入力してください';?> 71 </li> 72 </ul> 73<?php } ?> 74 75 76<?php if (strlen($comment) > $comment_max){?> 77 <ul> 78 <li> 79<?php print 'ひとことは100文字以内で入力してください';?> 80 </li> 81 </ul> 82<?php } ?> 83 84<?php if(strlen($comment) === 0){?> 85 <ul> 86 <li> 87<?php print 'ひとことを入力してください';?> 88 </li> 89 </ul> 90<?php } ?> 91 92 <form method = "POST"> 93 名前:<input type = "text" name = "name" size = "20" value = ""> 94 ひとこと:<input type = "text" name = "comment" size = "55" value = ""> 95 <input type = "submit" name = "submit" value = "送信"> 96 </form> 97<?php 98foreach ($data as $read) { 99?> 100 <ul> 101 <li> 102<?php print $read; ?> 103 </li> 104 </ul> 105<?php 106} 107?> 108</body> 109</html>

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

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

判定式が誤っています。

php

1if ((strlen($name) <= 20) || (strlen($name) !== 0)){ 2 34 5if ((strlen($name) <= 20) && (strlen($name) !== 0)){

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

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

php

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

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

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

php

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

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

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

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

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

php

1htmlspecialchars($tmp); 2 34 5htmlspecialchars($tmp, ENT_QUOTES, 'UTF-8');

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

投稿2017/05/21 16:08

編集2017/05/21 16:56
miyahan

総合スコア3095

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gorira6

2017/05/22 03:23

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

0

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

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

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

そこで、

php

1 2 3if ($_SERVER['REQUEST_METHOD'] === 'POST'){ 4 5 $name = $_POST['name']; 6 $comment = $_POST['comment']; 7 8 if (($fp_name = fopen($filename , 'a')) !== FALSE){ 9 if (((mb_strlen($name) <= $name_max) && (mb_strlen($name) !== 0)) && ((mb_strlen($comment) <= $comment_max) && (mb_strlen($comment) !== 0))){ 10 if (fwrite($fp_name , "$name:") === FALSE){ 11 print 'ファイル書き込み失敗' . $filename; 12 } 13 } 14 fclose($fp_name); 15 } 16 17 $text = "$comment $log\n"; 18 if (($fp_comment = fopen($filename , 'a')) !== FALSE){ 19 if (((mb_strlen($comment) <= $comment_max) && (mb_strlen($comment) !== 0)) && ((mb_strlen($name) <= $name_max) && (mb_strlen($name) !== 0))){ 20 if (fwrite($fp_comment , " $text") === FALSE){ 21 print 'ファイル書き込み失敗' . $filename; 22 } 23 } 24 fclose($fp_comment); 25 } 26} 27

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

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

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

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

投稿2017/05/22 10:50

gorira6

総合スコア12

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問