Skip to content

Instantly share code, notes, and snippets.

@ChristianOellers
Created July 3, 2023 12:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChristianOellers/c1c0448353394bc94adbdadc4f2e2e62 to your computer and use it in GitHub Desktop.
Save ChristianOellers/c1c0448353394bc94adbdadc4f2e2e62 to your computer and use it in GitHub Desktop.
Request + Routing - Minimalist zero-dependency PHP solution for JSON handling. Useful for development, not suited for production.
<?php
declare(strict_types=1);
namespace App;
use Exception;
use function addslashes;
use function file_get_contents;
use function header;
use function htmlspecialchars;
use function is_array;
use function is_string;
use function json_decode;
use function json_encode;
use function trim;
use const ENT_QUOTES;
/**
* HTTP request handling.
*/
// phpcs:ignore
abstract class Request
{
/**
* API method name to match to functionality.
*
* @var null|string
*/
protected $method;
public function __construct()
{
$this->method = $this->getRequestMethodName();
}
/**
* Get input data from request as JSON.
*
* @todo Optimize - Check if security measures are needed
*/
public function getInputAsJson(): array
{
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (!$data || !is_array($data)) {
throw new Exception('Input is invalid JSON');
}
return $data;
}
/**
* Output data as JSON.
*/
public function showAsJson(array $data)
{
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);
}
/**
* Get requested API method name.
*/
private function getRequestMethodName(): string
{
$raw = $_REQUEST['method'] ?? '';
if (!is_string($raw)) {
return '';
}
$name = empty($raw) ? '' : $raw;
$name = trim($raw);
$name = htmlspecialchars($raw, ENT_QUOTES);
$name = addslashes($raw);
return $name;
}
}
<?php
declare(strict_types=1);
namespace App;
use Exception;
/**
* API method handling from routes.
*/
// phpcs:ignore
final class RequestRoutes extends Request
{
/**
* Database queries instance.
*
* @var null|object
*/
private $dbQueries;
/**
* Constructor initializes request and sets dependencies.
*/
public function __construct(/* DatabaseQueries */ $dbQueries)
{
parent::__construct();
$this->dbQueries = $dbQueries;
}
/**
* Route API URLs to corresponding functions or throw error.
*
* @throws Exception
*/
public function route()
{
switch ($this->method) {
case "price":
$this->handleTotalPrice();
break;
case "products":
$this->handleProductPost();
break;
default:
throw new Exception("Method '$this->method' does not correspond to API functionality.");
}
}
/**
* @todo WIP - Implement feature
*/
private function handleTotalPrice()
{
$price = $this->dbQueries
->start()
->getTotalPrice();
$this->showAsJson(['sum' => $price]);
}
/**
* Save products to database.
* Get from input, save and output status result.
*
* @todo Optimize - Implement checking for $_POST request and deny others.
* @todo Concept - Decide on return values for errors.
* @todo Concept - Check for additionally required security measures.
* @throws Exception
*/
private function handleProductPost()
{
$productData = $this->getInputAsJson();
$this->dbQueries
->start()
->saveOrder($productData);
$this->showAsJson(['success' => true]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment