SpreadQuiz/class/ReqHandler.php

155 lines
5.3 KiB
PHP

<?php
require_once "privilege_levels.php";
// response types
const RESP_NONE = "none";
const RESP_PLAIN = "plain";
const RESP_JSON = "json";
// Request Handler Assignment class
// Members get populated when instantiated by ReqHandler;
// should not be used outside ReqHandler
class ReqHandlerAssignment
{
// members
public string $action; // action string
public array $params; // request parameters
public string $handler; // handler function
public string $hint; // human-readable hint about what this action performs
public string $level; // required command authentication level
public string $resp_type; // response type
public ReqHandler $rh; // reference to request handler
function __construct(string $action, array $params, string $level, string $handler, string $resp_type, string $hint, ReqHandler &$rh)
{
$this->action = $action;
$this->params = $params;
$this->level = $level;
$this->handler = $handler;
$this->resp_type = $resp_type;
$this->hint = $hint;
$this->rh = &$rh; // assign be reference
}
function dump() : string {
return "<i><font color='#2f4f4f'>" . $this->action . "</font></i>(<code>" . join(", ", $this->params) . "</code>) <font color='#008b8b'>" . $this->hint . "</font> [" . $this->level . "]";
}
}
// Request Handler class
class ReqHandler
{
private array $mapping; // action-handler mapping
private array $request; // reference to $_REQUEST
private array $files; // reference to $_FILES
private string $level; // max privilege level
// Constructor
function __construct()
{
// create reference bindings
$this->request = &$_REQUEST;
$this->files = &$_FILES;
$this->mapping = [PRIVILEGE_NONE => [], PRIVILEGE_PLAYER => [], PRIVILEGE_CREATOR => [], PRIVILEGE_QUIZMASTER => []]; // create assignment categories
$this->level = PRIVILEGE_NONE;
}
// Add new handler
function add(string|array $actions, array $params, string $level, string $handler, string $resp_type, string $hint): bool
{
// convert single actions to array
$actions = is_string($actions) ? [$actions] : $actions;
// cannot assign more than one handler to the same action
foreach ($actions as $action) {
if (in_array($action, $this->mapping[$level])) {
return false;
}
// create the assignment object
$assignment = new ReqHandlerAssignment($action, $params, $level, $handler, $resp_type, $hint, $this);
$this->mapping[$level][$action] = $assignment;
}
return true;
}
const ACTION_KEY = "action";
// Process possible pending request. Also check access privilege.
function process(string $level = null, string $action_key = self::ACTION_KEY) : array {
// get action
$action = $this->request[$action_key] ?? "";
// don't process empty actions
if (trim($action) === "") {
return ["", false];
}
// if privilege level is specified, then store
$this->level = $level ?? $this->level;
$resp = "";
$success = false;
// look up action
$categories = array_keys($this->mapping);
$highest_category_index = array_search($this->level, $categories);
for ($i = 0; ($i < count($categories)) && ($i <= $highest_category_index); $i++) {
// get category
$category = &$this->mapping[$categories[$i]];
// find the command level category that includes this action
if (array_key_exists($action, $category)) {
// get assignment
$assignment = &$category[$action];
// check that all required parameters were passed
$all_params_passed = true;
foreach ($assignment->params as $param) {
$all_params_passed &= array_key_exists($param, $this->request);
}
// if params were provided, then invoke the callback
if ($all_params_passed) {
$handler = $assignment->handler;
$raw_resp = $handler($this, $this->request);
// convert response to preset response type
if ($assignment->resp_type !== RESP_NONE) { // if command returns with anything at all
if ($assignment->resp_type === RESP_JSON) { // JSON
$resp = json_encode($raw_resp);
} else if ($assignment->resp_type === RESP_PLAIN) { // PLAIN
$resp = $raw_resp;
}
}
// processing successful
$success = true;
break; // break the loop
}
}
}
return [$resp, $success];
}
// Set access privilege level
function set_privilege_level(string $level) {
$this->level = $level;
}
// Dump commands
function dump_actions() : string {
$dump = "";
foreach ($this->mapping as $level => $actions) {
foreach ($actions as $action) {
$dump .= $action->dump() . "<br>";
}
}
return $dump;
}
}