Recording user response times for a quiz

I am working on a simple quiz-type game in which the user basically is asked a series of questions and needs to provide an answer.

I'm trying to find a solid/robust way of recording how long it takes for the user to answer each question. This is a web-based game so I am using JavaScript and PHP. I am very inexperienced, so I suspect this is not the best way to do it; at least I hope it's not because it seems very weak. I would appreciate any suggestions as to how to go about making a more full-proof solution. It's not critical that the time be accurate to the millisecond I'm just looking to build something that will be consistent and accurate to within a second or two.

The reason I'm doing it like this rather than just recording the time in the browser is because, as I understand it, the data could theoretically be altered by the user. This is also why I don't send that correct and answers to the browser until the user provides an answer.

Here is my current approach:

<?php
/* get_game_data.php
*
* user clicks button to start game
* and triggers ajax request sent to this file
*/
session_start();

if($_SERVER['REQUEST_METHOD'] === 'GET') { if(isset($_GET['gamesettings'])){///check certian user defined game options are set

require 'classes/Database.php';

date_default_timezone_set("UTC");

$_SESSION['app_start'] = new DateTime('NOW');////store time at which game/first question starts$_SESSION['app_index'] = 0;

if($_GET['app'] === 'aural') {$db = new Database();

$gameData = getGameData($db);///just an example
//etc...
//query database for game data, i.e. the questions,
echo json_encode($gameData['questions]);//then send back to browser$_SESSION['right_answers'] = $gameData['answers'];///store the right answers to varify against user answers later } } } ?> <?php /* post_game_data.php * * user answers question in a textbox etc. * then clicks a submit button which posts the answer to * this file via ajax again */ session_start(); function getTimeTaken($startDTO) {

$endDTO = new DateTime('NOW');$timeDiff   = $startDTO->diff($endDTO);
$timeTaken =$timeDiff->format('%s');

return $timeTaken; } date_default_timezone_set("UTC"); if($_SERVER['REQUEST_METHOD'] === 'POST')
{
if(
isset($_POST['user_answer']) && isset($_SESSION['app_index'])
)
{
$index =$_SESSION['app_index'];///current question in the game
$len = count($_SESSION['words']);///number of questions in the game
$userAns =$_POST['user_answer'];
$rightAns =$_SESSION['right_answers'];

if(isset($_SESSION['app_start']))////if this is the first question {$responseTime = getTimeTaken($_SESSION['app_start']); unset($_SESSION['app_start']);
$_SESSION['question_start'] = new DateTime('NOW'); } else if(isset($_SESSION['question_start']))///not the first question
{
$responseTime = getTimeTaken($_SESSION['question_start']);
$_SESSION['question_start'] = new DateTime('NOW'); } if($index < $len) {$crtAns = $rightAns[$index];
$mark = ($userAns == $crtAns)?1:0;///check user answer against correct answer$_SESSION['app_index']++;
echo json_encode(array($crtAns,$mark, $responseTime));////send right answer to broswer with mark and response time } } } ?>  This works pretty much as I would like it to right now but I don't have a live server to test it on, only Apache local host. 2 Answers Just some simple stuff, a common.php file for common code used by both files. DRY don't repeat yourself. Writing a script like you have is fine, the points i have noted will help you if you come back to maintain it in a year or so, and have forgotten how it works. The bulk of programming jobs are about maintaining a code base, not creating new stuff, so if you can learn how to make maintenance easier, it will be beneficial for you in the long run. (I wish i had learnt this stuff much earlier on) I have added inline comments explaining the changes i made <?php /* common.php */ require 'classes/Database.php'; session_start(); date_default_timezone_set("UTC"); ?> <?php /* get_game_data.php * * user clicks button to start game * and triggers ajax request sent to this file */ require 'common.php'; // moved into common.php // session_start(); if($_SERVER['REQUEST_METHOD'] === 'GET')
{
if(isset($_GET['gamesettings'])){///check certian user defined game options are set // moved into common.php // require 'classes/Database.php'; // moved into common.php // date_default_timezone_set("UTC");$_SESSION['app_start'] = new DateTime('NOW');////store time at which game/first question starts
$_SESSION['app_index'] = 0; // warning if$_GET['app'] not set this will get a warning notice, we can never trust GET/POST data
// if($_GET['app'] === 'aural') { if(isset($_GET['app']) && $_GET['app'] === 'aural') {$db = new Database();

$gameData = getGameData($db);///just an example
//etc...
//query database for game data, i.e. the questions,

// i prefer to do my internal logic before i output data. this way if something fails i can handle it prior to responding to the user
$_SESSION['right_answers'] =$gameData['answers'];///store the right answers to varify against user answers later

echo json_encode($gameData['questions']);//then send back to browser } } } ?> <?php /* post_game_data.php * * user answers question in a textbox etc. * then clicks a submit button which posts the answer to * this file via ajax again */ require 'common.php'; // moved into common.php //session_start(); // moved into common.php // date_default_timezone_set("UTC"); if($_SERVER['REQUEST_METHOD'] === 'POST')
{
// what you have done is not bad, i just prefer to have all user supplied data handled in one place, POST/GET
// so i know that everything beyond here has been sanitized
//    if(
//       isset($_POST['user_answer']) && // isset($_SESSION['app_index'])
//    )

$userAns = isset($_POST['user_answer']) ? $_POST['user_answer'] : null; // good variable names can also help reduce the need for comments // which of these two following variable names are easier to understand //$index = $_SESSION['app_index'];///current question in the game$current_question_index = isset($_SESSION['app_index']) ?$_SESSION['app_index'] : null;

if($userAns != null &&$current_question_index != null)
{

// same here with variable naming
// if you are going to maintain these scripts over time, you need to make it easier to remember what is going on

// $len = count($_SESSION['words']);///number of questions in the game
$num_questions = count($_SESSION['words']);

// handled above, no need to repeat
//      $userAns =$_POST['user_answer'];

$rightAns =$_SESSION['right_answers'];

// why use a comment here that could change over time when you can make an easily understandable function name instead
// if(isset($_SESSION['app_start']))////if this is the first question if (is_first_question()) {$responseTime = getTimeTaken($_SESSION['app_start']); unset($_SESSION['app_start']);

// $_SESSION['question_start'] = new DateTime('NOW'); start_question_timer(); } // else if(isset($_SESSION['question_start']))///not the first question
else if(is_question_started())///not the first question
{
$responseTime = getTimeTaken($_SESSION['question_start']);

// $_SESSION['question_start'] = new DateTime('NOW'); start_question_timer(); } // which of these two is going to be easier to understand next year when you come back to maintain your script if ($current_question_index < $num_questions) { // if($index < $len) {$crtAns = $rightAns[$current_question_index];

// this is probably overkill in this case, but again why write a comment when you can write a nice function name
// $mark = ($userAns == $crtAns)?1:0;///check user answer against correct answer$mark = mark_user_answer($userAns,$crtAns);

// easy function names to make the code more understandable
// $_SESSION['app_index']++; move_to_next_question(); // given your if {} elseif {} statement above,$responseTime may not have been set, as there is no default case
// this should be handled properly
echo json_encode(array($crtAns,$mark, $responseTime));////send right answer to broswer with mark and response time } } } // move functions below main code as what is most important should come first // perhaps this function could be called elapsedTimeInSeconds, // get infers you are getting a value, and there is no indication what the return type might be function getTimeTaken($startDTO) {

// good variable naming, easy to understand what is happening
$endDTO = new DateTime('NOW');$timeDiff   = $startDTO->diff($endDTO);
$timeTaken =$timeDiff->format('%s');

return $timeTaken; } function is_first_question() { return isset($_SESSION['app_start']);
}

function is_question_started() {
return isset($_SESSION['question_start']; } // function mark_user_answer($user_answer, $correct_answer) { return ($user_answer == $correct_answer) ? 1 : 0; } function move_to_next_question() {$_SESSION['app_index']++;
}

function start_question_timer() {
\$_SESSION['question_start'] = new DateTime('NOW');}
}

?>

• Thank you for this. I will study this carefully. I posted this mainly just as a proof of concept to see if people thought using sessions like this was a reliable/acceptable way to record user response times. However, there's plenty of great general programming stuff in here that I need to learn too. Thanks again. – Matt Herbstritt Jan 21 '14 at 12:43

Matt.

Basically, the idea is correct - start the timer on the server side before the question is send to the user and calculate the duration on the response received. Using DateTime for that is also a great idea, in my opinion.

An alternative solution for you would be storing your gamers response time in the database, but this is a bit more complex. Please feel free to contact me in case you need an advise on that.

The only advise I have for you - try using OOP concepts in your code. All the rest is fine.

• Thanks, I will eventually be storing the response times in a database. I just wanted to see if using PHP like this was a suitable mechanism for accurately recording the time. – Matt Herbstritt Jan 21 '14 at 14:10