PHP Trivia Game - Database Edition

Here is an updated version of the trivia game code, which has multiple screens (Welcome, Question) and fully-working sessions. All files not listed are unchanged from the previous iteration of the trivia game.

/web/src/trivia/TriviaController.php

Our updated controller.

<?php

class TriviaController {

  private $db;

  private $errorMessage = "";

  /**
   * Constructor
   */
  public function __construct($input) {
    // Start the session! (new!)
    session_start();

    $this->db = new Database();

    $this->input = $input;
  }

  /**
   * Run the server
   * 
   * Given the input (usually $_GET), then it will determine
   * which command to execute based on the given "command"
   * parameter.  Default is the welcome page.
   */
  public function run() {
    // Get the command
    $command = "welcome";
    if (isset($this->input["command"]) && (
      $this->input["command"] == "login" || isset($_SESSION["name"])))
      $command = $this->input["command"];

    switch($command) {
      case "login": // new!
        // TODO: maybe we should check name/email?
        $this->login();
        break;
      case "answer":
        $this->answerQuestion();
        break;
      case "question":
        $this->showQuestion();
        break;
      case "logout": // new!
        $this->logout(); // notice no break (new!)
      case "welcome":
      default:
        $this->showWelcome();
        break;
    }
  }

  public function login() {
    if (isset($_POST["fullname"]) && isset($_POST["email"]) &&
      isset($_POST["password"]) && !empty($_POST["password"]) &&
      !empty($_POST["fullname"]) && !empty($_POST["email"])) {
      // TODO: check that email looks right!

      $results = $this->db->query("select * from users where email = $1;", $_POST["email"]);

      if (empty($results)) {
        // create the user account
        $result = $this->db->query("insert into users (name, email, password) values ($1, $2, $3);",
          $_POST["fullname"], $_POST["email"], 
          password_hash($_POST["password"], PASSWORD_DEFAULT));
        
        $_SESSION["name"] = $_POST["fullname"];
        $_SESSION["email"] = $_POST["email"];
        $_SESSION["score"] = 0;
        
        // call showQuestion OR ...
        // redirect with a header to the question screen
        header("Location: ?command=question");
        return;
      } else {
        // check that the user's password is correct
        $hashed_password = $results[0]["password"];
        $correct = password_verify($_POST["password"], $hashed_password);
        if ($correct) {
          // Success!
          $_SESSION["name"] = $_POST["fullname"];
          $_SESSION["email"] = $_POST["email"];
          $_SESSION["score"] = $results[0]["score"];

          // call showQuestion OR ...
          // redirect with a header to the question screen
          header("Location: ?command=question");
          return;
        } else {
         $message = "<p class='alert alert-danger'>Incorrect password!</p>"; 
        }
      }
      $this->showWelcome($message);
      return;
    }

    $this->showWelcome("Name or email missing");
  }

  /**
   * Logout function.  We **need** to clear the session somehow.
   * When the user wants to start over, we should allow them to
   * reset the game.  I'll call it logout, so this function destroys
   * the session and immediately starts a new one.  (new!)
   */
  public function logout() {
    // Destroy the session
    session_destroy();

    // Start a new session.  Why?  We want to show the next question.
    session_start();
    $_SESSION["score"] = 0;
  }

  /**
   * Our getQuestion function, now as a method!
   */
  function getQuestion($id = null) {
    // Todo: Think about how to change this function to get
    // the qid from the session rather than needing the id
    // passed in. Would this be better?

    if ($id == null) {
      $results = $this->db->query("select id, question, answer 
                      from questions order by random() limit 1;");
    } else {
      $results = $this->db->query("select * from questions where id = $1;", $id);
    }
    
    if ($results !== false && isset($results[0])) {
      $_SESSION["qid"] = $results[0]["id"];
      return $results[0];
    }
    
    // Look! We can throw exceptions like in Java!
    throw new Exception("No questions in the database");

  }

  /**
   * Show a question to the user.  This function loads a
   * template PHP file and displays it to the user based on
   * properties of this object.
   */
  public function showQuestion($message = "") {
    $question = $this->getQuestion();
    $score = $_SESSION["score"]; // score variable, make the template easier to read (new!)
    $name = $_SESSION["name"]; // name variable, make the template easier to read (new!)
    include("/opt/src/trivia/templates/question.php");
  }
  
  public function showWelcome($message = "") {
    include("/opt/src/trivia/templates/welcome.php");
  }

  /**
   * Check the user's answer to a question.
   */
  public function answerQuestion() {
    $message = "";
    if (isset($_POST["answer"]) && isset($_POST["qid"]) && is_numeric($_POST["qid"])) {

      $question = $this->getQuestion($_POST["qid"]);

      if (strtolower(trim($_POST["answer"])) == strtolower($question["answer"])) {
        $message = "<div class=\"alert alert-success\" role=\"alert\">
          Correct!
          </div>";
        // Remove the question ID from the session (new!)
        unset($_SESSION["qid"]);
        // Update the user's score (new!)
        $_SESSION["score"] += 10; // + 10 points!
        $this->db->query("update users set score = $1 where email = $2;",
                            $_SESSION["score"], $_SESSION["email"]);

      }
      else {
        $message = "<div class=\"alert alert-danger\" role=\"alert\">
          Incorrect!
          </div>";
      }
    }

    $this->showQuestion($message);
  }

}