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

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

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

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

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

Q&A

解決済

3回答

3792閲覧

会員検索サイトのようなものを作成しております。うまく検索ができません。

退会済みユーザー

退会済みユーザー

総合スコア0

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

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

0グッド

1クリップ

投稿2016/06/19 16:43

編集2016/06/20 04:46

###前提・実現したいこと
勉強し始めてから、一ヶ月も立たない初心者です。会員検索サイトのようなものを作成しています。
環境はPHPとMySQLになります。ID、名前、登録日のこの3つの条件(1つでも3つでも条件に合うようなら検索できるようにしたいです。)から検索して、該当する会員の検索結果を表示するようにしたいと思っています。

###発生している問題
登録日だけで検索をすると、どの日付を選んでも、全会員のデータが表示されてしまいます。また、何も入力しないでも、全データが出力されてしまします。どこに問題があるのかを教えていただけますと、幸いです。

長いですが、以下にソースコードを記入させていただきます。
お手数をおかけいたしますが、もし可能であればアドバイスを頂きたいです。

###該当のソースコード

<form name="form1" method ="post"action ="member_search.php"> ID(半角)&nbsp; <input type="text" name="vid"value> 氏名&nbsp; <input type="text" name="nm"value> 入会日&nbsp; <input type="date" name="ndt"value> <input type="submit" value="検索"> </form> <?php try { $dsn ='mysql:dbname=kadai;host=localhost'; $user ='root'; $password=''; $dbh =new PDO($dsn,$user,$password); $dbh->query('SET NAMES utf8'); $sql ='SELECT id,name,address,tel1,tel2,email,ndate,remarks FROM mst_member WHERE 1'; $stmt =$dbh->prepare($sql); $stmt->execute(); print "<table border=1>" ; print "<tr><td>ID</td><td>名前</td><td>住所</td><td>電話番号(固定)</td><td>電話番号(携帯)</td><td>メールアドレス)</td><td>入会日</td><td>備考</td><tr/>"; while(true) { $rec =$stmt->fetch(PDO::FETCH_ASSOC); if($rec==false) { break; } print"<tr>" ; print"<td>".$rec["id"]."</td>"; print"<td>".$rec["name"]."</td>"; print"<td>".$rec["address"]."</td>"; print"<td>".$rec["tel1"]."</td>"; print"<td>".$rec["tel2"]."</td>"; print"<td>".$rec["email"]."</td>"; print"<td>".$rec["ndate"]."</td>"; print"<td>".$rec["remarks"]."</td>"; print"<br/>"; print"<tr/>" ; } print "</table>" ; } catch (Exception $e) { print'ただいま障害により大変ご迷惑をお掛けしております。'; exit(); } $dbh = null; ?>

member_search.php

<?php try { $vid=$_POST['vid']; $nm=$_POST['nm']; $ndt=$_POST['ndt']; $dsn ='mysql:dbname=kadai;host=localhost'; $user ='root'; $password=''; $dbh =new PDO($dsn,$user,$password); $dbh->query('SET NAMES utf8'); $sql = "SELECT * FROM mst_member WHERE 1 = 1 "; if ($vid !="") { $sql .= " and id = $vid "; } if ($nm != "" ) { $sql .= " and name like '%$nm%' "; } if ($ndt != "" ) { $sql .= " and ndate >= $ndt "; } else { $post[]=""; } $stmt =$dbh->prepare($sql); $data[]=$vid; $data[]=$nm; $data[]=$ndt; $stmt->execute($data); $count=$stmt->rowCount(); print"検索結果は".$count."件です。<br>"; } catch (Exception $e) { print'ただいま障害により大変ご迷惑をお掛けしております。'; exit(); } if ($count<1) { print"検索結果がありません。<br>"; } else { ?> <table border=1> <tbody> <tr><td>ID</td><td>名前</td><td>住所</td><td>電話番号(固定)</td><td>電話番号(携帯)</td><td>メールアドレス)</td><td>入会日</td><td>備考</td><tr/>" <?php while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { ?> <tr> <td><?=htmlspecialchars($row['id'])?></td> <td><?=htmlspecialchars($row['name'])?></td> <td><?=htmlspecialchars($row['address'])?></td> <td><?=htmlspecialchars($row['tel1'])?></td> <td><?=htmlspecialchars($row['tel2'])?></td> <td><?=htmlspecialchars($row['email'])?></td> <td><?=htmlspecialchars($row['ndate'])?></td> <td><?=htmlspecialchars($row['remarks'])?></td> </tr> <?php } ?> </tbody></table> <?php } ?>

###試したこと
課題に対してアプローチしたことを記載してください

###補足情報(言語/FW/ツール等のバージョンなど)
より詳細な情報

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

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

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

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

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

kei344

2016/06/19 16:46

コードはコードブロックで囲んでいただけませんか? ```(バッククオート3つ)で囲み、前後に改行をいれるか、コードを選択して「</>」ボタンを押すとコードブロックになります。
退会済みユーザー

退会済みユーザー

2016/06/20 02:29

お教えいただきありがとうございます。 今後質問をする際は、必ずそのようにして投稿をさせていただきます。 ご親切にありがとうございます。
kei344

2016/06/20 02:32

この質問も編集可能ですので、編集されるのが良いと思います。あなたと同じ疑問を抱えている方がこのページを見に来る可能性があるので、できればよろしくお願いします。
退会済みユーザー

退会済みユーザー

2016/06/20 02:48

kei344様 お教えいただいた通り、コードを選択して「</>」ボタンで対応をしてみました。うまく対応できているでしょうか。ありがとうございました!
kei344

2016/06/20 04:08

すいませんが、編集が保存されていないようです。もう一度ご確認いただけませんか?
退会済みユーザー

退会済みユーザー

2016/06/20 04:49

無事できました!開いたり閉じたりできるようになるのですね! 大変お手数をおかけいたしました!
kei344

2016/06/20 07:55

完璧です!編集ありがとうございました。
guest

回答3

0

ベストアンサー

今回の質問内容に関する直接的な問題点

原因が判明しましたので,内容を変更します.ttyp03さんのご指摘通りです.

ただ,依然としてこの部分はPHPの文字列として直接変数展開せずに,プリペアドステートメントのプレースホルダ「?」に当てはめるように書くべきなので,セキュリティ上まずい書き方であることには変わりません.

修正すべき脆弱性

  • ユーザから入力された変数をそのままSQL文の中に展開しているので,SQLインジェクション攻撃に対する致命的な脆弱性がある.この脆弱性は1つもあってはいけないレベルで危険.そもそもPDOの使い方を間違っているので,正しい用法を覚える.
  • htmlspecialcharsを通さずにHTML内にテキストデータを表示しようとしている場所がある.XSS攻撃に対する脆弱性になり得るので,常に通すように心がけるべき.

誤作動やエラーの防止

  • 変数未定義のエラーを防ぐために,$_POSTの存在確認をissetで行うべき.欲を言えば更にis_stringで文字列かどうかの確認を行うべき.これらをまとめて行ってくれる,filter_inputの利用が推奨される.
  • SET NAMES utf8はデータベース側の文字セットしか変更できないため,基本的には,PDOドライバ側も同時に変更できる,DSNでのcharset=utf8指定を利用すべき.
  • エラーモードがデフォルトのPDO::ERRMODE_SILENTのままなので,->prepare()のコールに失敗した場合にPDOExceptionが発生せず,単にfalseが返されるだけになってしまっている.エラーモードをPDO::ERRMODE_EXCEPTIONに変更しない限りは,いちいち返り値をチェックする必要がある.
  • LIKE検索のためのエスケープを行っていないため,% _ \ といった特殊文字を含むキーワードを正しく取り扱えない.
  • SELECTの結果に対して->rowCount()は使えず,常にゼロになってしまうため,結果を配列として取得したあと配列に対してcount()を実行する.

読みやすいコードにするためには必要

  • HTMLの表示内容はほとんど重複するにも関わらず,絞り込み検索を行うためだけにPHPファイルを複製している.このような場合は「絞り込み条件が送信されてきたか」で判定し,HTML表示部分は共通化するだけで十分.
  • PHPのテンプレートエンジンとしての特性を無視して,printでHTMLタグ全体を出力している部分がある.基本的には変数に依存する部分のみを <?= ~ ?> 記法で出力するだけで十分.
  • ロジックとHTMLを可能な限り分離すべき.**「あらかじめ必要な変数を作っておき,すべての変数が用意できたらHTMLを書き始める」**というやり方を徹底する.<body>まで書いてからデータベースに接続して…,というのは典型的にダメな書き方.また,HTMLを途中まで書いておいて最後まで書き終わる前にdieで強制終了するのNG.

改善済みのコード

html

1<?php 2 3// HTML中にテキストを埋め込むときに利用する関数 4function h($str) 5{ 6 return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); 7} 8 9// $_POST['vid'], $_POST['nm'], $_POST['ndt'] を安全に文字列として受け取る 10// もし存在していない場合,空文字列になる 11$vid = (string)filter_input(INPUT_POST, 'vid'); 12$nm = (string)filter_input(INPUT_POST, 'nm'); 13$ndt = (string)filter_input(INPUT_POST, 'ndt'); 14 15try { 16 17 // localhostのkadaiデータベースに文字コードutf8で接続 18 // SQL実行時のエラーもPDOExceptionに変換してスローさせる 19 $pdo = new PDO('mysql:dbname=kadai;host=localhost;charset=utf8', 'root', '', [ 20 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 21 ]); 22 23 // 共通のSQL文と空のパラメータ用配列を準備する 24 $sql = ' 25 SELECT id, name, address, tel1, tel2, email, ndate, remarks 26 FROM mst_member 27 WHERE 1 = 1 28 '; 29 $params = []; 30 31 // $vid が入力されていたら,AND条件としてプレースホルダ「?」を追加し. 32 // そこに文字列として当てはめるための値を $params に追加する 33 if ($vid !== '') { 34 $sql .= ' AND id = ? '; 35 $params[] = $vid; 36 } 37 38 // $nm が入力されていたら,AND条件としてプレースホルダ「?」を追加し. 39 // そこに文字列として当てはめるための値を $params に追加する 40 // 名前に関してはLIKE検索を行うので,LIKE検索で使われる特殊文字を 41 // addcslashes関数を使ってエスケープしたあと,両端を%で囲んだ値を使用する 42 if ($nm !== '') { 43 $sql .= ' AND name like ? '; 44 $params[] = '%' . addcslashes($nm, '\_%') . '%'; 45 } 46 47 // $hdt が入力されていたら,AND条件としてプレースホルダ「?」を追加し. 48 // そこに文字列として当てはめるための値を $params に追加する 49 if ($ndt !== '') { 50 $sql .= ' AND ndate >= ? '; 51 $params[] = $ndt; 52 } 53 54 // SQLを実行し,すべての結果を2次元の配列として一気に取得 55 $stmt = $pdo->prepare($sql); 56 $stmt->execute($params); 57 $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); 58 59} catch (PDOException $e) { 60 61 // もしどこかでエラーが発生した場合,「500 Internal Server Error」として 62 // テキスト形式でエラーメッセージを表示して終了する 63 header('Content-Type: text/plain; charset=UTF-8', true, 500); 64 echo "ただいま障害により大変ご迷惑をお掛けしております。\n"; 65 echo "Error: {$e->getMessage()}\n"; 66 exit; 67 68} 69 70// エラーが起こらなければHTMLとして表示を開始する 71header('Content-Type: text/html; charset=UTF-8'); 72 73?> 74<!DOCTYPE html> 75<title>名簿一覧</title> 76<h1>名簿一覧</h1> 77<form method="post" action=""> 78 ID(半角) <input type="text" name="vid" value="<?=h($vid)?>"><br> 79 氏名 <input type="text" name="nm" value="<?=h($nm)?>"><br> 80 入会日 <input type="date" name="ndt" value="<?=($ndt)?>"><br> 81 <input type="submit" value="検索"> 82</form> 83<?php if ($_SERVER['REQUEST_METHOD'] === 'POST'): /* POSTされたときだけ結果件数を示す */ ?> 84<p>検索結果は<?=h(count($rows))?>件です</p> 85<?php endif; ?> 86<table border="1"> 87 <tr> 88 <th>ID</th> 89 <th>名前</th> 90 <th>住所</th> 91 <th>固定電話番号</th> 92 <th>携帯電話番号</th> 93 <th>Eメールアドレス</th> 94 <th>入会日</th> 95 <th>備考</th> 96 </tr> 97<?php foreach ($rows as $row): ?> 98 <tr> 99 <td><?=h($row['id'])?></td> 100 <td><?=h($row['name'])?></td> 101 <td><?=h($row['address'])?></td> 102 <td><?=h($row['tel1'])?></td> 103 <td><?=h($row['tel2'])?></td> 104 <td><?=h($row['email'])?></td> 105 <td><?=h($row['ndate'])?></td> 106 <td><?=h($row['remarks'])?></td> 107 </tr> 108<?php endforeach; ?> 109</table>

参考リンク

投稿2016/06/19 19:05

編集2016/06/20 02:20
mpyw

総合スコア5223

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

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

退会済みユーザー

退会済みユーザー

2016/06/20 02:06

ご回答いただき、誠にありがとうございます。 ここまで細かくしっかりと教えていただけるとは思っていませんでした。想像をはるかに超えられてしまってすごく驚いております。 と同時に、とてもうれしく思います。 まだまだ分からないことだらけで、悩みながら進んでいるような状態ですが、もし機会がありましたら、またたくさんのことを教えていただきたいです。 自分の問題点についてたくさん気が付くことができました。 ご親切に、ご丁寧に本当にありがとうございました。
guest

0

input type="date" で送信すると、サーバー側には2012-01-01のような形式で送信されるようです。
なので、日付部分のクエリでは''で囲むなどしないと、引き算された結果(上記の例だと2012-1-1で2010)で検索されているのではと思われます。

PHP

1$sql .= " and ndate >= $ndt "; 23$sql .= " and ndate >= '$ndt' ";

またテーブルのフォーマットがわからないのですが、ndateカラムの型と前述の2012-01-01の形式がマッチしているのかも確認が必要です。

投稿2016/06/20 01:05

編集2016/06/20 01:05
ttyp03

総合スコア16996

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

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

退会済みユーザー

退会済みユーザー

2016/06/20 02:02

ご回答いただきまして、誠にありがとうございます。 教えていただいた通りにしたところ、自分が求めているように動くようになりました。 分からないことだらけで申し訳ないのですが、私のために、ご親切に教えていただいたこと、深く感謝しております。
guest

0

member_search.php

$stmt =$dbh->prepare($sql);
の直前で
echo $sql;
として、実際にどのようなSQLが生成されているかを確認し、
実行したいSQLと同じであるかどうかを確認するのがいいかと思います。

どんなSQLを実行したいのかが分からない
ということであれば、それはPHPというよりはSQLの問題なので
その部分を先に解決されるのがいいでしょう。

また、プリペアードステートメントの使用方法もあいまいな状態で組まれているように見えるので、

PHPマニュアル
を参考にしてSQLに変数を文字列として連結してしまっている部分を修正してみてください。
*現状のままだと正しく動かないだけではなく、致命的なSQLインジェクション脆弱性を抱えています。

投稿2016/06/19 18:53

tanat

総合スコア18706

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

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

退会済みユーザー

退会済みユーザー

2016/06/20 02:23

ご回答いただきまして、まことにありがとうございます。 おっしていただいた通りでいろいろと自分でもあいまいな点がたくさんあります。 一歩ずつしっかりと分からない点は調べていく必要があるなと感じました。 一人だとどうしてもいろんな点で凝り固まってしまうので、お聞きすることができてとてもうれしいです。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問