You can download the entire source code for this series here PHP Quiz (10.88 kB)
In order to store usernames and scores we’re going to use an xml file. We could also do this with a database but for the sake of simplicity, we’ll use xml. Copy and paste the following in to a text editor and save it as ‘leaders.xml’
<?xml version="1.0" encoding="UTF-8"?> <users> <user> <name>Bobby</name> <score>10</score> </user> <user> <name>Billy</name> <score>1</score> </user> </users>
XML is really easy to read. Looking at our example, we have a root element (<users>). Inside that there are <user> elements, each with their own nested <name> and <score> elements. PHP provides us with an easy way to work with xml files via the SimpleXML extension. It’s highly likely you’ll have this available to use.
The first thing we need is a way to display the ‘leaderboard’. We can write a function that utilises SimpleXML to achieve this.
showLeaders()
The function takes 3 parameters. $file is the xml file we want it to read. $limit is how many results we want to show. The last parameter, $group, is optional and enables us to group the results into smaller lists if we require it. This is null as default.
function showLeaders($file,$limit,$group = null) {}
Next we create an empty array called $leaders. We use SimpleXML to load our file and loop through the <user> elements extracting the name and score for each one. Each name/score pair is placed into the $leaders array as $key=>$value.
function showLeaders($file,$limit,$group = null) { $leaders = array(); $xml = simplexml_load_file($file); foreach($xml->user as $user) { $name = (string)$user->name; $score = (string)$user->score; $leaders[$name] = $score; } }
We sort the $leaders array numerically using arsort() so that the highest scorers are first in the array. We also set a $counter variable to 1.
function showLeaders($file,$limit,$group = null) { $leaders = array(); $xml = simplexml_load_file($file); foreach($xml->user as $user) { $name = (string)$user->name; $score = (string)$user->score; $leaders[$name] = $score; } arsort($leaders,SORT_NUMERIC); $counter = 1; }
We create a variable called $output. This will hold all of the html created by the function that we want to display on the page. First, we start a html list:
function showLeaders($file,$limit,$group = null) { $leaders = array(); $xml = simplexml_load_file($file); foreach($xml->user as $user) { $name = (string)$user->name; $score = (string)$user->score; $leaders[$name] = $score; } arsort($leaders,SORT_NUMERIC); $counter = 1; $output = "<ul class=\"leaders\">\n"; }
The next part is the main body of the function. We loop through the $leaders array:
foreach ($leaders as $key => $value) { }
We check to see if $counter is less than or equal to the $limit parameter:
foreach ($leaders as $key => $value) { if ($counter <= $limit) { } }
We check if $key is the same username as the current user. If it is, surround the username with <strong> tags making it stand out more. If this isn’t the current user, just create a standard list item. In either case, the html is appended on to the $output variable. Notice the (.=) operator.
foreach ($leaders as $key => $value) { if ($counter <= $limit) { if ($key == $_SESSION['user']) { $output .= "<li><strong>$key:</strong> $value/20</li>\n"; } else { $output .= "<li>$key: $value/20</li>\n"; } } }
Next, we check if the $group parameter was passed. If it was, we compare it to the $counter variable using the modulus operator (%). In a nutshell, this checks to see if $counter is cleanly divisible by $group (remainder of 0). What that means in layman’s terms is if $group was ’5′, every 5th list item would match. At that point, we end the current list and start another. Remember, this only happens if the $group parameter was passed into the showLeaders() function.
foreach ($leaders as $key => $value) { if ($counter <= $limit) { if ($key == $_SESSION['user']) { $output .= "<li><strong>$key:</strong> $value/20</li>\n"; } else { $output .= "<li>$key: $value/20</li>\n"; } if ($group) { if ($counter % $group == 0) { $output .= "</ul>\n<ul class=\"leaders\">\n"; } } } }
Finally, we increment $counter by 1, close the foreach loop, add a closing </ul> tag to $output and echo $output back to the browser.
foreach ($leaders as $key => $value) { if ($counter <= $limit) { if ($key == $_SESSION['user']) { $output .= "<li><strong>$key:</strong> $value/20</li>\n"; } else { $output .= "<li>$key: $value/20</li>\n"; } if ($group) { if ($counter % $group == 0) { $output .= "</ul>\n<ul class=\"leaders\">\n"; } } $counter++; } $output .= "</ul>\n"; echo $output; }
After putting all of that together, your final showLeaders function should look like this:
Complete showLeaders()
function showLeaders($file,$limit,$group = null) { $leaders = array(); $xml = simplexml_load_file($file); foreach($xml->user as $user) { $name = (string)$user->name; $score = (string)$user->score; $leaders[$name] = $score; } arsort($leaders,SORT_NUMERIC); $counter = 1; $output = "<ul class=\"leaders\">\n"; foreach ($leaders as $key => $value) { if ($counter <= $limit) { if ($key == $_SESSION['user']) { $output .= "<li><strong>$key:</strong> $value/20</li>\n"; } else { $output .= "<li>$key: $value/20</li>\n"; } if ($group) { if($counter % $group == 0) { $output .= "</ul>\n<ul class=\"leaders\">\n"; } } } $counter++; } $output .= "</ul>\n"; echo $output; }
Place the above in ‘functions.php’ just after the shuffle_assoc() function. Now, to use the showLeaders() function, we just call it wherever we want to display the leaderboard. For example, on ‘index.php’, just before the closing </div> tag of the #leaderboard div, place the following:
<?php showLeaders('leaders.xml',10,5); ?>
That will display the top 10 scorers grouped into lists of 5 i.e two lists.
Similarly, on ‘results.php’, just before the end of the #intro div, place this line (don’t forget to remove any dummy content you may have in that div already!)
<?php showLeaders('leaders.xml',20); ?>
That displays the top 20 scorers in one long list.
In part 5 I said that this would be the last post in this series. Alas, I was being a bit optimistic! As it stands, there are still 4 main things left to do.
- Display the correct/incorrect answers on ‘results.php’.
- Stop people from viewing ‘results.php’ unless they’ve completed the quiz.
- Check for existing usernames to avoid duplicates.
- Add latest username/score to ‘leaders.xml’
I hope you stick around for part 7. I promise it really will be the last post of this series:)
What You Said