クイズアプリ

予め正しい答えを全て配列の0に入れておき、click functionで正しいか正誤判定しています。選択肢はシャッフルして表示し、最後に、正解率を表示させています。

reference
Autoloading Classes:The spl_autoload_register() function registers any number of autoloaders, enabling for classes and interfaces to be automatically loaded if they are currently not defined. By registering autoloaders, PHP is given a last chance to load the class or interface before it fails with an error.

index.php

<?php

require_once(__DIR__ . '/config.php');

$quiz = new MyApp\Quiz();

if(!$quiz->isFinished()) {
  $data = $quiz->getCurrentQuiz();
  shuffle($data['a']);
}

?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Intractive Art</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="stylesheet" href="styles.css">
</head>
<body>
  <?php if ($quiz->isFinished()) : ?>
    <div id="container">
      <div id="result">
        your score ...
        <div><?= h($quiz->getScore()); ?>%</div>
      </div>
     <a href=""><div id="btn">Replay</div></a>
    </div>
  <?php $quiz->reset(); ?>
  <?php else : ?>
    <div id="container">
      <h1>Q. <?= h($data&#91;'q'&#93;); ?></h1>
      <ul>
        <?php foreach ($data&#91;'a'&#93; as $a) : ?>
        <li class="answer"><?= h($a); ?></li>
        <!-- <li class="answer">A1</li>
        <li class="answer">A2</li>
        <li class="answer">A3</li> -->
        <?php endforeach; ?>
      </ul>
        <div id="btn" class="disabled"><?= $quiz->isLast() ? 'Show Result' : 'Next Question'; ?></div>
        <input type="hidden" id="token" value="<?= h($_SESSION&#91;'token'&#93;); ?>">
      </div>
  	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="quiz.js"></script>
  <?php endif; ?>
</body>
</html>

autoload.php

<?php

spl_autoload_register(function($class){
  $prefix = 'MyApp\\';

  if (strpos($class, $prefix) === 0){
    $className = substr($class, strlen($prefix));
    $classFilePath = __DIR__ . '/' . $className . '.php';

    if (file_exists($classFilePath)){
      require $classFilePath;
    } else {
        echo 'No such class' . $className;
        exit;
    }
  }

});
&#91;/php&#93;

_answer.php
&#91;php&#93;
<?php

require_once(__DIR__ . '/config.php');

$quiz = new MyApp\Quiz();

try{
  $correctAnswer = $quiz->checkAnswer();
} catch (Exception $e) {
  header($_SERVER['SERVER_PROTOCOL']. ' 403 Forbidden', true, 403);
  echo $e->getMessage();
  exit;
}

header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
  'correct_answer' => $correctAnswer
]);

Token.php

<?php

require_once(__DIR__ . '/config.php');

$quiz = new MyApp\Quiz();

try{
  $correctAnswer = $quiz->checkAnswer();
} catch (Exception $e) {
  header($_SERVER['SERVER_PROTOCOL']. ' 403 Forbidden', true, 403);
  echo $e->getMessage();
  exit;
}

header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
  'correct_answer' => $correctAnswer
]);

Quiz.php

<?php

 namespace MyApp;

 class Quiz{
   private $_quizSet = &#91;&#93;;

   public function __construct(){
     $this->_setup();
     Token::create();

     if(!isset($_SESSION['current_num'])){
       $this->_initSession();
     }
   }

   public function checkAnswer(){
     Token::validate('token');
     $correctAnswer = $this->_quizSet[$_SESSION['current_num']]['a'][0];
     if (!isset($_POST['answer'])){
       throw new \Exception('answer not set!');
     }
     if($correctAnswer === $_POST['answer']){
       $_SESSION['correct_count']++;
     }
     $_SESSION['current_num']++;
     return $correctAnswer;
   }

   public function isFinished(){
     return count($this->_quizSet) === $_SESSION['current_num'];
   }

   public function getScore() {
     return round($_SESSION['correct_count'] / count($this->_quizSet) * 100);
   }

   public function isLast(){
     return count($this->_quizSet) === $_SESSION['current_num'] + 1;
   }

   public function reset(){
     $this->_initSession();
    //  $_SESSION['current_num'] = 0;
    //  $_SESSION['correct_count'] = 0;
   }

   private function _initSession(){
     $_SESSION['current_num'] = 0;
     $_SESSION['correct_count'] = 0;
   }

   public function getCurrentQuiz() {
     return $this->_quizSet[$_SESSION['current_num']];
   }

   private function _setup(){
     $this->_quizSet[] = [
       'q' => '1543年は、ある物が日本に伝えられた年でもある。それは何か?',
       'a' => ['鉄砲', 'キリスト教', '貿易', '忍法']
     ];
     $this->_quizSet[] = [
       'q' => '海上交通の要地に作られた港町で有名なのはどこ?',
       'a' => ['堺', '伊丹', '京都', '江戸']
     ];
     $this->_quizSet[] = [
       'q' => '1639年に鎖国は完成する。この年から、来航を禁止された国はどこ?',
       'a' => ['ポルトガル', 'イギリス', '清', 'オランダ']
     ];
   }
 }

config.php

quiz.js

$(function() {
  'use strict';

  $('.answer').on('click', function() {
    var $selected = $(this);
    if ($selected.hasClass('correct') || $selected.hasClass('wrong')) {
      return;
    }
    $selected.addClass('selected');
    var answer = $selected.text();

    $.post('/_answer.php', {
      answer: answer,
      token: $('#token').val()
    }).done(function(res) {
      $('.answer').each(function() {
        if ($(this).text() === res.correct_answer) {
          $(this).addClass('correct');
        } else {
          $(this).addClass('wrong');
        }
      });
      // alert(res.correct_answer);
      if (answer === res.correct_answer) {
        // correct!
        $selected.text(answer + ' ... CORRECT!');
      } else {
        // wrong!
        $selected.text(answer + ' ... WRONG!');
      }
      $('#btn').removeClass('disabled');
    });
  });

  $('#btn').on('click', function() {
    if (!$(this).hasClass('disabled')) {
      location.reload();
    }
  });

});
body {
  font-size: 16px;
  font-family: Arial, sans-serif;
}
#container {
  width: 500px;
  margin: 15px auto;
}
h1, ul > li {
  border: 1px solid #ddd;
  border-radius: 5px;
}
h1 {
  padding: 10px;
  height: 50px;
  font-size: 18px;
  margin: 0 0 10px;
}
ul {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
}
ul > li {
  margin-bottom: 7px;
  padding: 7px 10px;
  cursor: pointer;
}
#btn {
  text-align: center;
  float: right;
  width: 100px;
  padding: 7px;
  color: #fff;
  border-radius: 5px;
  background: #00aaff;
  box-shadow: 0 4px 0 #0088cc;
  cursor: pointer;
  font-size: 14px;
}
#btn.disabled {
  opacity: 0.5;
}
.correct{
  color: limegreen;
  font-weight: bold;
}
.wrong {
  color: #ddd;
}
.selected {
  font-weight: bold;
}
#result {
  border: 1px solid #ddd;
  border-radius: 5px;
  margin-bottom: 15px;
  text-align: center;
  padding: 30px 0 50px;
}
#result > div {
  font-size: 64px;
  font-weight: bold;
  margin-top: 30px;
}