ドットインストールの動画レッスン「PHPで作る簡単クイズ」-PHP(5)

【この記事を読むのに必要な時間は約 25 分です。】

ドットインストールでPHPの勉強をしている。ドットインストールのPHP関連で用意されている動画レッスンを終えた後に、DEMOを見ただけでスクリプトが書けるようになるための練習。今回は、ドットインストールの動画レッスン「PHPで作るシンプルカレンダー」-PHP(4)に続き5回目。

最初にDEMOと考え方のヒントを提示し、それだけで実際にスクリプトを書いてみよう。スクリプトを書き終わったら、完成版と見比べて答え合わせができるようにした。

今回は、「PHPで作る簡単クイズ」(全9回)から。

Step1

今回は最初にDEMOを見るのではなく、4ステップで作っていこう。まずは、次のクイズリストのQ&Aを全て配列に入れてみよう。

Q.1 今学習している言語は? A:PHP,Java,C++,Ruby
Q.2 スタイルを設定しているのは? A:CSS,HTML,PHP,Document
Q.3 ウェブサイトを作る基本は? A:HTML,iOS,Windows,POST
Q.4 2ちゃんねるとは? A:掲示板,コッペパン,な板,番組

Step1の答え

// クイズリスト 正解はaの一番最初に記載する
    $quizList = array (
        array(
          'q' => '今学習している言語は?',
          'a' => array ('PHP', 'Java', 'C++', 'Ruby')
        ),
        array(
          'q' => 'スタイルを設定しているのは?',
          'a' => array ('CSS', 'HTML', 'PHP', 'Document')
        ),
        array(
          'q' => 'ウェブサイトを作る基本は?',
          'a' => array ('HTML', 'iOS', 'Windows', 'POST')
        ),
        array(
          'q' => '2ちゃんねるとは?',
          'a' => array ('掲示板', 'コッペパン', 'まな板', '番組')
        )
    );

Step2

それでは、Step1のクイズリストを利用して、html / CSS / PHP の知識で次の画像の通り作ってみよう。

正解だったらとりあえず正解と表示されるように、間違いだったらリダイレクトしてクイズを続行できるようにしてみよう。正答率はHTML / CSS で形だけ作ろう。

GIF

ポイントとヒント

  1. html5でひな形作成。
  2. クイズはランダムに選択され、その答えは毎回シャッフルされるようにしよう。 / mt_rand() / coount() / shuffle()
  3. foreach文で form/ input
  4. エスケープ処理もしよう。
  5. 正誤判定はどうやればいい?
    • 選択されたクイズのアンサーの選択肢のうち、一番最初が正解。
    • すなわち選択したアンサー($_POST[‘answer’])が配列の一番最初($_SESSION[‘qnum’][‘a’][0])と一致すれば正解。
    • 答えを選択してPOSTすると正解なら正解へ飛び、間違いなら再度クイズへ戻るので、どのクイズを選択したのかは、SESSIONを使ってページが遷移した後も保持しよう。
    • POST後にもセッションで保持が必要なのは’qnum’
    • クイズで選択した答えはPOSTされる。
  6. 間違った回答をした場合リダイレクトさせるには / header()

Step2の答え

<?php
    session_start();

    function h($s) {
        return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
    }

    $quizList = array (
        array(
          'q' => '今学習している言語は?',
          'a' => array ('PHP', 'Java', 'C++', 'Ruby')
        ),
        array(
          'q' => 'スタイルを設定しているのは?',
          'a' => array ('CSS', 'HTML', 'PHP', 'Document')
        ),
        array(
          'q' => 'ウェブサイトを作る基本は?',
          'a' => array ('HTML', 'iOS', 'Windows', 'POST')
        ),
        array(
          'q' => '2ちゃんねるとは?',
          'a' => array ('掲示板', 'コッペパン', 'まな板', '番組')
        )
    );

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if ($_POST['answer'] === $quizList[$_POST['qnum']]['a'][0]) {
           echo '正解です!!!';
           exit;
        }
            header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
            exit;
    }

    $qnum = mt_rand(0, count($quizList)-1);

    $quiz = $quizList[$qnum];

    $_SESSION['qnum'] = (string)$qnum;

    shuffle($quiz['a']);

?>
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charaset ="utf-8">
        <title>簡単クイズ</title>
        <style>
          #answer_row {
            padding: 7px;
            background: #eee;
            border: #ccc;
          }
        </style>
    </head>
    <body>
        <div id="answer_row">
             ○問中
             ○問正解!

        </div>
        <p>Q.<?php echo $quiz['q']; ?></p>
        <?php foreach($quiz['a'] as $answer) : ?>
        <form action="" method="post">
            <input type="submit" name="answer" value="<?php echo h($answer) ?>">
            <input type="hidden" name="qnum" value="<?php echo h($_SESSION['qnum']) ?>">
        </form>
        <?php endforeach; ?>

    </body>
</html>

Step3-だいたい完成させてみよう

GIF2

ポイント

  1. リセットボタンを使ってリセットできるようにしよう。/ $_SESSIONの活用。
  2. 回答数と正解率を表示できるようにしよう。/ $_SESSIONの活用。
    • 初期化に必要なのは、回答回数と正答回数。
    • 初期化は、どこで必要なのか考えよう。
    • リセットを関数化してみよう。
  3. リダイレクトも関数化してみよう。

Step3-だいたい完成させたスクリプト

<?php
// POSTした後にも$qnumを保持するためにセッションを利用
  // SESSIONの流れ①
    session_start();

// エスケープ処理
    function h($s) {
        return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
    }

// クイズリスト 正解はaの一番最初に記載する
    $quizList = array (
        array(
          'q' => '今学習している言語は?',
          'a' => array ('PHP', 'Java', 'C++', 'Ruby')
        ),
        array(
          'q' => 'スタイルを設定しているのは?',
          'a' => array ('CSS', 'HTML', 'PHP', ・・・'Document')
        ),
        array(
          'q' => 'ウェブサイトを作る基本は?',
          'a' => array ('HTML', 'iOS', 'Windows', 'POST')
        ),
        array(
          'q' => '2ちゃんねるとは?',
          'a' => array ('掲示板', 'コッペパン', 'まな板', '番組')
        )
    );

// リセットセッション処理
    function resetSession() {
        $_SESSION['correct_count'] = 0;
        $_SESSION['all_count'] = 0;
    }
// リダイレクト処理
    function redirect() {
        header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
        exit;
    }

// 正誤判定
  // POSTされたら
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
      // リセットボタンをおした時の処理
        if (isset($_POST['reset']) && $_POST['reset'] === '1') {
      /* この部分はリセットセッションとして関数化すっる
            $_SESSION['correct_count'] = 0;
            $_SESSION['all_count'] = 0;
      */
        resetSession();
      /* この部分はリダイレクトとして関数化する
            header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
            exit;
      */
        redirect();
        }
  // POSTされたAnswerとPOSTされたSESSIONで保持されたqnumのクイズのゼロ番目が一致すれば正解
    // SESSIONの流れ④
        if ($_POST['answer'] === $quizList[$_POST['qnum']]['a'][0]) {
        //  echo '正解です!';
        //  exit;
    // 正解だったら正答数を1増やす
            $_SESSION['correct_count']++;
        }
    // 正解でなくても答える(POSTされる)度に増えていくので
            $_SESSION['all_count']++;
  /* この部分はリダイレクト処理として関数化する
  //  POSTした後に元のページに戻ってきたい- リダイレクト処理  リダイレクトさせると再読み込みによる二十読み込みも防止できるので以下のように(URLが変わってもいいように)
      header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
  //リダイレクトしたら必ずexit;を付ける
      exit;
  */
        redirect();
    }

// 正答率
  // ①初期化処理
    if (empty($_SESSION)) {
  /* この部分はリセットセッションとして関数化する
        $_SESSION['correct_count'] = 0;
        $_SESSION['all_count'] = 0;
  */
        resetSession();
    }

// クイズの選択=クイズ番号の決定
    $qnum = mt_rand(0, count($quizList)-1);

// 選択したクイズ
    $quiz = $quizList[$qnum];

// SESSIONで$qnumをPOST後も保持
  // SESSIONの流れ②
// 上の正誤判定で厳密演算子を使用しているのでキャストしておく
    $_SESSION['qnum'] = (string)$qnum;

// クイズのアンサーオプションをシャッフル
    shuffle($quiz['a']);

//  var_dump($quiz['a']);
?>
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charaset ="utf-8">
        <title>簡単クイズ</title>
        <style>
          #answer_row {
            padding: 7px;
            background: #eee;
            border: #ccc;
          }
        </style>
    </head>
    <body>
        <div id="answer_row">
            <?php echo h($_SESSION['all_count']); ?> 問中
            <?php echo h($_SESSION['correct_count']); ?> 問正解!
        </div>
        <p>Q.<?php echo $quiz['q']; ?></p>
        <!-- foreach文で全ての回答オプションを表示する -->
        <!-- SESSIONに保存したqnumをhiddenでPOSTする SESSIONの流れ③-->
        <?php foreach($quiz['a'] as $answer) : ?>
        <form action="" method="post">
            <input type="submit" name="answer" value="<?php echo h($answer) ?>">
            <input type="hidden" name="qnum" value="<?php echo h($_SESSION['qnum']) ?>">
        </form>
        <?php endforeach; ?>
        <hr>
        <!-- リセットボタンの設置 -->
        <form action="" method="post">
            <input type="submit" value="リセット!">
            <!-- リセットが有効のとき1 -->
            <input type="hidden" name="reset" value="1">
        </form>
    </body>
</html>

Step4- CSRF対策 / エラーチェック

CSRF対策とは

CSRFとはCross-site Request Forgeryの略で、別のサイトに用意したコンテンツ上の罠のリンクを踏ませること等をきっかけとして、インターネットショッピングの最終決済や退会等Webアプリケーションの重要な処理を呼び出すようユーザを誘導する攻撃のこと。
基本乱数を発生させたtokenを利用しよう。
セッションにtokenを保存するには ⇒ $_SESSION[‘token’] = sha1(uniqid(mt_rand(), true));

エラーチェック

POSTしたものは必ずエラーチェックする癖をつけよう。
エラーチェックの基本はisset()関数。 / empty()関数

完成したスクリプト

<?php
    session_start();

    function h($s) {
        return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
    }

    $quizList = array (
        array(
          'q' => '今学習している言語は?',
          'a' => array ('PHP', 'Java', 'C++', 'Ruby')
        ),
        array(
          'q' => 'スタイルを設定しているのは?',
          'a' => array ('CSS', 'HTML', 'PHP', 'Document')
        ),
        array(
          'q' => 'ウェブサイトを作る基本は?',
          'a' => array ('HTML', 'iOS', 'Windows', 'POST')
        ),
        array(
          'q' => '2ちゃんねるとは?',
          'a' => array ('掲示板', 'コッペパン', 'まな板', '番組')
        )
    );

    function resetSession() {
        $_SESSION['correct_count'] = 0;
        $_SESSION['all_count'] = 0;
        // CSRF対策 tokenの設定
        $_SESSION['token'] = sha1(uniqid(mt_rand(), true));
    }

    function redirect() {
        header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
        exit;
    }

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
      // CSRF対策
        if ($_POST['token'] !== $_SESSION['token']) {
            echo '不正な投稿です!(1)';
            exit;
        }
        if (isset($_POST['reset']) && $_POST['reset'] === '1') {
            resetSession();
            redirect();
        }
      // エラーチェック-入力でqnumが渡されるのでこれのエラーチェック
      // リセットの方が優先されるのでその後に
        if (!isset($_POST['qnum']) || $_SESSION['qnum'] !== $_POST['qnum']) {
            echo '不正な投稿です!(2)';
            exit;
        }
      // エラーチェック-そもそもqnumがquizListに含まれているか
        if (!isset($quizList[$_POST['qnum']])) {
            echo '不正な投稿です!(3)';
            exit;
        }

        if ($_POST['answer'] === $quizList[$_POST['qnum']]['a'][0]) {
            $_SESSION['correct_count']++;
        }
            $_SESSION['all_count']++;
            redirect();
    }

    if (empty($_SESSION)) {
        resetSession();
    }

    $qnum = mt_rand(0, count($quizList)-1);

    $quiz = $quizList[$qnum];

    $_SESSION['qnum'] = (string)$qnum;

    shuffle($quiz['a']);

?>
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charaset ="utf-8">
        <title>簡単クイズ</title>
        <style>
          #answer_row {
            padding: 7px;
            background: #eee;
            border: #ccc;
          }
        </style>
    </head>
    <body>
        <div id="answer_row">
            <?php echo h($_SESSION['all_count']); ?> 問中
            <?php echo h($_SESSION['correct_count']); ?> 問正解!
<!-- 正答率の表示 -->
            <?php if($_SESSION['all_count'] > 0) : ?>
            正答率は <?php echo h( sprintf("%.2f", $_SESSION['correct_count'] / $_SESSION['all_count'] * 100)); ?> % です!
            <?php endif; ?>
        </div>
        <p>Q.<?php echo $quiz['q']; ?></p>
        <?php foreach($quiz['a'] as $answer) : ?>
        <form action="" method="post">
            <input type="submit" name="answer" value="<?php echo h($answer) ?>">
            <input type="hidden" name="qnum" value="<?php echo h($_SESSION['qnum']) ?>">
<!-- CSRF対策 -->
            <input type="hidden" name="token" value="<?php echo h($_SESSION['token']) ?>">
        </form>
        <?php endforeach; ?>
        <hr>
        <form action="" method="post">
            <input type="submit" value="リセット!">
            <input type="hidden" name="reset" value="1">
<!-- CSRF対策 -->
            <input type="hidden" name="token" value="<?php echo h($_SESSION['token']) ?>">
        </form>
    </body>
</html>

PHPマニュアルを見よう

最初は何を書いているかわからないけど、とにかく疑問点があったらPHPマニュアルを見る癖をつけよう。

セッション

セッションの基本的な使用法

以上

Related Posts Plugin for WordPress, Blogger...

もし良ければお願いします。

  • このエントリーをはてなブックマークに追加

良かったらフォローお願いします。