Creating an Online Quiz with PHP

Damn! A Dream.In.Code quiz can be tough I tell you. So many questions, so many answers, so little time! Like the question here “How many times have you been told you are a liar, you must die, or have been put in context of being some freak of nature by Supersloth?” Uhhh… Maybe there is a choice for how many times I have NOT been called one of those things. I am going to have to say D) Too many to count!. Now only if there was a way to bring the horror of this (or any other) quizes online. Oh wait! We can! I will show you how to setup a basic online quiz using PHP, a couple of text files, and grade it all right from the comfort of your web browser right here on this entry of the Programming Underground!

Woo hoo! I got my answer right! It was D! Yay! Now for the next question… If Amadeus told you that your homework assignment, that you posted on the board, looked like a bunch of garbage and to redo it, do you…. A.) Tell him to mind his own business, B.) Say “I shall redo it as you command oh wise one”… C.) Complain that you are just a newbie or D.) Ignore him and just post it again in another forum? Gosh what to do!

Well, let me put that aside for now and get to hammering out how we can create an online multiple choice quiz. The below solution is, as always, only one method of creating such a quiz using two external txt files. One to hold the questions and choices and the other to hold the answers to those questions. Now for simplicity sake, and not to drop a bunch of explode() function calls everywhere, I have opted to keep the answer key separate. That way you can actually store the key somewhere else or in a place perhaps that is non-navigable by the public (like out of the web document tree).

Lets first take a look at how we might structure the questions.txt file that will hold our questions.

1. What is your name? : A) George, B) Tom, C) Martyr, D) None of the Above
2. How old are you? : A) 15, B) 21, C) 30, D) None of the Above
3. If I could be like any DIC staff member I would be... : A) Skyhawk133, B) Supersloth, C) PsychoCoder, D) Martyr2, E) None of them weirdos 

The first part of this file is the question we want to ask. Simple enough huh? Following that is each of the multiple choices we want to use for that question separated by commas. The idea here is that we are going to read in the question, break this into another array with the question as the first element and the choices as the second element, then later split the choices for display.

So how do we do the answer key? Simple, the right letter choice for each corresponding question is on its own line. So if the right answers to the three questions above are A, D, B then we would have our answerkey.txt looking like this…

A
D
B

The idea here is that we have the answer for question 1 on line 1 which is the same as the line number in the questions.txt. With this setup we can easily go to the questions file, change the question, choices etc and then change the letter answer in the answerkey.txt on the same line for that question. We could even have a system automatically generate both the questions and answerkey text files for each quiz. Maybe we pull out the questions from a legacy database, write it to file to then be consumed by our PHP script. Theoretically we could load the questions into an array, the answers into an array, then run a scramble function on both arrays at the same time (to keep them parallel) and generate random quizes per viewing. I will leave that part up to you (it is really a lot simpler than you think.)

So lets get to the code and show how we process these files, read in the student’s answers, grade it and present their percentage along with a letter grade.

<html>
<head>
<title>DIC - Online Quiz Example Using PHP</title>
</head>

<body>


<?php

	
// Read answerkey.txt file for the answers to each of the questions.
function readAnswerKey($filename) {
	$answerKey = array();
	
	// If the answer key exists and is readable, read it into an array.
	if (file_exists($filename) && is_readable($filename)) {
		$answerKey = file($filename);
	}
	
	return $answerKey;
}


// Read the questions file and return an array of arrays (questions and choices)
// Each element of $displayQuestions is an array where first element is the question 
// and second element is the choices.

function readQuestions($filename) {
	$displayQuestions = array();
	
	if (file_exists($filename) && is_readable($filename)) {
		$questions = file($filename);
	
		// Loop through each line and explode it into question and choices
		foreach ($questions as $key => $value) {
			$displayQuestions[] = explode(":",$value);
		}				
	}
	else { echo "Error finding or reading questions file."; }
	
	return $displayQuestions;
}


// Take our array of exploded questions and choices, show the question and loop through the choices.
function displayTheQuestions($questions) {
	if (count($questions) > 0) {
		foreach ($questions as $key => $value) {
			echo "<b>$value[0]</b><br/><br/>";
			
			// Break the choices appart into a choice array
			$choices = explode(",",$value[1]);
			
			// For each choice, create a radio button as part of that questions radio button group
			// Each radio will be the same group name (in this case the question number) and have
			// a value that is the first letter of the choice.
			
			foreach($choices as $value) {
				$letter = substr(trim($value),0,1);
				echo "<input type=\"radio\" name=\"$key\" value=\"$letter\">$value<br/>";
			}
			
			echo "<br/>";
		}
	}
	else { echo "No questions to display."; }
}


// Translates a precentage grade into a letter grade based on our customized scale.
function translateToGrade($percentage) {

	if ($percentage >= 90.0) { return "A"; }
	else if ($percentage >= 80.0) { return "B"; }
	else if ($percentage >= 70.0) { return "C"; }
	else if ($percentage >= 60.0) { return "D"; }
	else { return "F"; }
}

?>

<h2>Welcome to the Dream In Code Example Online Quiz!</h2>
<h4>Please complete the following questions as accurately as possible.</h4>

<form method="POST" action="<?php echo $_SERVER["PHP_SELF"]; ?>">

<?php
	// Load the questions from the questions file
	$loadedQuestions = readQuestions("questions.txt");
	
	// Display the questions
	displayTheQuestions($loadedQuestions);
?>

<input type="submit" name="submitquiz" value="Submit Quiz"/>


<?php

// This grades the quiz once they have clicked the submit button
if (isset($_POST['submitquiz'])) {

	// Read in the answers from the answer key and get the count of all answers.
	$answerKey = readAnswerKey("answerkey.txt");
	$answerCount = count($answerKey);
	$correctCount = 0;


	// For each answer in the answer key, see if the user has a matching question submitted
	foreach ($answerKey as $key => $keyanswer) {
		if (isset($_POST[$key])) {
			// If the answerkey and the user submitted answer are the same, increment the 
			// correct answer counter for the user
			if (strtoupper(rtrim($keyanswer)) == strtoupper($_POST[$key])) {
				$correctCount++;
			}
		}
	}


	// Now we know the total number of questions and the number they got right. So lets spit out the totals.
	echo "<br/><br/>Total Questions: $answerCount<br/>";
	echo "Number Correct: $correctCount<br/><br/>";

	if ($answerCount > 0) {

		// If we had answers in the answer key, translate their score to percentage
		// then pass that percentage to our translateGrade function to return a letter grade.
		$percentage = round((($correctCount / $answerCount) * 100),1);
		echo "Total Score: $percentage% (Grade: " . translateToGrade($percentage) . ")<br/>";
	}
	else {
		// If something went wrong or they failed to answer any questions, we have a score of 0 and an "F"
		echo "Total Score: 0 (Grade: F)";
	}
}

?>

</form>

</body>
</html>

The steps are written as in-code comments to show you how things are going down, but I will give you a brief summary of it here as well. As you can see this is a standard HTML page with PHP. We have a set of functions in it that implement various features and loading procedures. We have a function called readAnswerKey to load in our answerkey.txt file answers and return an array of those answers for comparison. We also have a function to read in the questions from questions.txt. We read in each line into an array called “questions”. Each element of this array is then the question and choices together. So we loop through each question and break it apart into another array and store that array into our final array called “displayQuestions”. So displayQuestions[0] contains an array of two elements, the first is the question and the second is the choices for that question.

So our first array element might look like this…

displayQuestions[0] contains  Array(0=>"1. What is your name?", 1 => A) George, B) Tom, C) Martyr, D) None of the Above)

Hopefully that makes sense. It is an array of arrays. Our next function called “displayQuestions” takes the array displayQuestions and loops through it, pulling out each array from it and showing the question, breaks the choices apart, then loops through the choices and creating a radio button for each one. Each choice is going to be part of the same question and thus named with the same name. All the choices for question 1 are named “1” and their letter is stored as the value for each radio button.

If the user chose option D for question 1, the result would be $_POST[“0”] = “D”. Now do you see where I am going with this? Answer key array index 0 is the answer for the first question. So you just have to compare the answerkey[“0”] to $_POST[“0”] and see if they have the same letter in it. If it does, we increment the count.

Next in the script we present the form and display the questions to the user along with the standard submit button. The form will submit to itself for grading. When the user does submit it, the code towards the bottom of this script is going to execute and load up the answers, find out how many answers there are total (and saves that value) and goes through the answer key comparing it against the user’s submitted answers. For each correct answer we increment a counter.

At the end of the loop we have the total number of questions, the total they got right so we can just spit out the percentage (numbercorrect / numbertotal) and translate it to a percentage by multiplying by 100.

Last thing we do is pass this percentage to the function for finding a letter grade. In that function (called translateToGrade) we have setup a custom letter grade scale where we test the value for certain ranges and return a letter representing the grade.

That is all there is to it! The explanation here is a bit lengthy, but the code is not that overall complicated. We load two files, compare their submitted answers against an answer key and return the results in percentage/letter grade. This system assumes that if a question is not answered, it is wrong. You can change the quiz just by changing the text files.

Hopefully that makes sense. Feel free to take out parts of this script for your own uses, edit the code I have written etc. I put it out there in the public domain for anyone to use and alter.

I am now back to my DIC quiz where hopefully I can get at least a 75% but with questions like “How many times has axel said pwned?” I am not too sure if I am even going to pass! Let’s only hope!

Thanks for reading! 🙂

About The Author

Martyr2 is the founder of the Coders Lexicon and author of the new ebooks "The Programmers Idea Book" and "Diagnosing the Problem" . He has been a programmer for over 25 years. He works for a hot application development company in Vancouver Canada which service some of the biggest tech companies in the world. He has won numerous awards for his mentoring in software development and contributes regularly to several communities around the web. He is an expert in numerous languages including .NET, PHP, C/C++, Java and more.