6
\$\begingroup\$

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.

\$\endgroup\$
2
\$\begingroup\$

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');}
}

?>
\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Matt Herbstritt Jan 21 '14 at 12:43
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Matt Herbstritt Jan 21 '14 at 14:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.