Table of Contents

The Quiz App

A basic quiz app in PHP that teaches beginners the basics of HTML forms, database access, arrays and JSON.

Questions

First, the questions. Create a file called questions.php and enter something like this:

<?php
 
$questions[0]['question'] = "What was originally considered to be the ninth planet from the Sun but after 1992 had its status as a large planet changed?";
$questions[0]['answer'] = "Pluto";
 
$questions[1]['question'] = "How many days are there in February in a leap year?";
$questions[1]['answer'] = "29";
 
$questions[2]['question'] = "Give the four initials of the organisation responsible for maintaining a database of drivers in Great Britain?";
$questions[2]['answer'] = "DVLA";
 
$questions[3]['question'] = "Which dance move, in which the dancer moves backwards, was made popular by Michael Jackson?";
$questions[3]['answer'] = "Moonwalk";
 
?>

This is a basic array of questions and answers.

A random question

Now, a basic page to choose a question at random, and wait for the answer, let's call it quiz.php:

<?php
 
require("questions.php");
 
$size_of_question_list = sizeof($questions);
 
// check if we are in the middle of answering a question (if we are, then
// the random_number will already be set), otherwise, choose a number
 
if(empty($_REQUEST['random_number'])) {
   $random_number = rand(0, $size_of_question_list);  // there is a bug here
} else {
   $random_number = $_REQUEST['random_number'];
}
 
 
?>
 
<h1>Quiz</h1>
 
<?php
 
if(!empty($_REQUEST['user_answer'])) {
   $user_answer = $_REQUEST['user_answer'];
   $question_id = $_REQUEST['random_number'];
   echo "<p>You answered '$user_answer' for Question #$question_id</p>";
 
   if($user_answer == $questions[$question_id]['answer']) {
      echo "Correct :)";
   } else {
      echo "Wrong :(";
   }
 
}
 
?>
 
<p><strong>Question: </strong><?= $questions[$random_number]['question']; ?></p>
<form method='post' action='quiz.php'>
<input type='hidden' name='random_number' value='<?= $random_number; ?>' />
<input type='text' name='user_answer' />
<input type='submit' name='answer_button' value='Answer'/>
</form>
 
<p><a href='quiz.php'>Another random question</a></p>

This includes the questions.php file, selects a question at random, then outputs the question and an HTML form to receive the answer.

At this point your basic quiz app should be working (but it doesn't use a database, just an array of manually entered questions).

Getting the questions from a database

First, lets design a very simple database that can hold both the questions and answers. We'd normally have a table for questions and a table for answers (especially if some questions had multiple answers) but for now we'll stick to one table.

We need:

  1. A question ID (so we can match questions to answers) this'll be a number so we can use numbered arrays
  2. A question (a text field to record the question in) lets say 2000 characters long at most
  3. An answer (so we know if the user got it right) lets say 500 characters long at most
  4. A time/date of when the question was last asked

Why the time and date? This'll let the 'random experience' be a bit less repetitive. More on that later.

You can create this manually using the mysql client, or in Adminer (instructions on installing it are here).

A quick way to create the table in Adminer is to copy and paste this SQL into the SQL Command section:

CREATE TABLE `quiz_questions` (
  `question_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `question` VARCHAR(2000) NOT NULL,
  `answer` VARCHAR(500) NOT NULL,
  `last_asked` datetime NOT NULL
);

That'll create the table for you.

And now, this SQL will add some test questions to it (or you can enter some yourself by clicking on the newly created “quiz_questions” table and clicking 'New Item'):

INSERT INTO `quiz_questions` (`question`, `answer`, `last_asked`)
VALUES ('What was originally considered to be the ninth planet from the Sun but after 1992 had its status as a large planet changed?', 'Pluto', now());
INSERT INTO `quiz_questions` (`question`, `answer`, `last_asked`)
VALUES ('How many days are there in February in a leap year?', '29', now());
INSERT INTO `quiz_questions` (`question`, `answer`, `last_asked`)
VALUES ('Give the four initials of the organisation responsible for maintaining a database of drivers in Great Britain?', 'DVLA', now());
INSERT INTO `quiz_questions` (`question`, `answer`, `last_asked`)
VALUES ('Which dance move, in which the dancer moves backwards, was made popular by Michael Jackson?', 'Moonwalk', now());

Let's create a new file, this one called questions_from_db.php - we'll be using some code from the connecting to your DB sample:

<?php 
 
$host = "localhost";
$username = "coding_username";
$password = "cheese";
$db_name = "coding"; 
 
$mysql_string = "mysql:host=$host;dbname=$db_name";
 
$db_connection = new PDO($mysql_string, $username, $password);
 
$sql = "select * from quiz_questions";
$rs = $db_connection->query($sql);
$rows = $rs->fetchAll(PDO::FETCH_ASSOC);
 
foreach($rows as $row) {
  $questions[$row['question_id']]['question'] = $row['question'];
  $questions[$row['question_id']]['answer'] = $row['answer'];
 
}
 
?>

Putting it all together

Now, if everything has went to plan, you can replace the:

require("questions.php")

line from your quiz.php file with:

require("questions_from_db.php")

and now, when you enter new questions to the database, they'll be available in your quiz.

Extending the quiz app

JSON (sharing your questions)

You can make your questions, in your database, available to anyone else's quiz program with this bit of code, call it export.php:

<?php
header("Content-Type: application/json");
require("questions_from_db.php");
echo json_encode($questions);
?>

Now, you can share your quiz URL with others. The full URL will be http://YOUR_IP_ADDRESS/export.php

So, back to the quiz.php file (actually anyone else's quiz.php) instead of the line that reads

require("questions.php")

This code will connect to a fellow coder's IP address, download their quiz questions and format them in a way your program can understand them:

$json_data = file_get_contents("http://YOUR_IP_ADDRESS/export.php");
$questions = json_decode($json_data, true);

Fixing problems: The Moonwalk and Case Sensitivity

Prep

Before we begin, let's get your quiz to focus just on this question while we test it (so it does't choose them at random).

Add the question if you don't have it already, then find the ID of the question by looking at your database. Then add a new line after the if statement that assigns the random number (around line 14 depending if you've made changes, just make sure it's not inside the if statement) that reads (replace 4 with your Micheal Jackson question ID):

$random_number = 4;

Remember to take this line out when you want to go back to random selection.

Now your quiz will continually ask this question while we test some changes.

Case sensitive

One of my example question/answers is “Which dance move, in which the dancer moves backwards, was made popular by Michael Jackson?”, if someone enters all lower case 'moonwalk' it'll by wrong. Why? Because the “==” comparison is exact (i.e. case sensitive).

We could fix that by changing the

if($user_answer == $questions[$question_id]['answer']) {

line to use the strcasecmp() function. You can read about that function here:

https://www.php.net/manual/en/function.strcasecmp.php

It compares one string to another, ignoring case and returns 0 if they match. So, this would work:

if(strcasecmp($user_answer,$questions[$question_id]['answer']) == 0) {

This 'equals 0' thing might be a little confusing, especially if we go back to this code after a couple weeks. So maybe we could try an alternative approach that is a little easier to read. We could also use the strtolower() function and convert the user's answer (and our answer) to lower case, then use the old comparision, something like:

if(strtolower($user_answer) == strtolower($questions[$question_id]['answer'])) {

That should also work. Whichever you use is up to what you prefer (or how you might use the answers later).

The 'the' problem

For example, if I give “The Moonwalk” as an answer, it'll be wrong. Why?

Now, let's look at the line that checks if it matches an answer:

if($user_answer == $questions[$question_id]['answer']) {

Using “==” we asked it “Does the user's answer exactly match the answer in the database?”, because “The Moonwalk” isn't “Moonwalk” it fails (incorrect).

Instead we could ask “Does the user's answer contain the same word we have as the answer in the database?”, in which case “The Moonwalk” would contain it.

So how do we do that? PHP doesn't have a function that does exactly that, but it does have a function that tells us which character a certain string starts at. strpos(). You can read it's manual entry here:

https://www.php.net/manual/en/function.strpos.php

So, if we use that function, we should flip the question to be: “Does the questions's answer appear in the text the user has submitted?”

Can you get it working? We'd have to do something like this:

if(strpos($user_answer, $questions[$question_id]['answer']) !== false) {

This is a bit more complex, but it's quite a common use of strpos. It's saying “Does the database answer appear in the user's answer?” and because of the way strpos works (it returns false if not found) we check to make sure it's not false.

We use a special operator “!==” here rather than the “!=” operator because it's possible strpos will return “0” i.e. “Yes, I found the string you are searching for and it's at the 0th character” - so we want to compare the type of variable that is return (i.e. true/false) rather than assume 0 is false and 1 is true in this particular case. Don't worry about this too much, it's not too common outside of this particular use case.

Lastly, we'll also make it not case-sensitive, so the final line you should use is:

if(strpos(strotolower($user_answer), strtolower($questions[$question_id]['answer'])) !== false) {

You might want to break that into a few lines to make it easier to read/understand later:

$AnswerUser = strtolower($user_answer);
$AnswerDb = strtolower($questions[$question_id]['answer']);
 
if(strpos($AnswerUser, $AnswerDb) !== false) {

Next Steps