305 lines
8.5 KiB
PHP
305 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
use Exception;
|
|
use interfaces\response_i;
|
|
use JetBrains\PhpStorm\NoReturn;
|
|
use objects\users_o;
|
|
|
|
class response implements response_i
|
|
{
|
|
private bool $matching_route_found = false;
|
|
private array $data = [];
|
|
private array $meta = [];
|
|
private array $includes = [];
|
|
private users_o $users_o;
|
|
private ?array $jsonRequestBody = null;
|
|
|
|
#[NoReturn] public function success(mixed $data, int $status = null): void
|
|
{
|
|
$this->response(true, $data, $status);
|
|
}
|
|
|
|
#[NoReturn] public function response(bool $success, mixed $data, int $status = null): void
|
|
{
|
|
global $DEBUG;
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
if ($status) {
|
|
http_response_code($status);
|
|
} else {
|
|
http_response_code($success ? 200 : 400);
|
|
}
|
|
// If the data isn't an array, convert it to an array
|
|
if (!is_array($data) && !is_object($data)) {
|
|
// Check if the variable is a valid JSON string
|
|
if (is_string($data) && json_decode($data) !== null) {
|
|
$data = json_decode($data, true);
|
|
} else {
|
|
// If the variable is not a valid JSON string, convert it to an array
|
|
$data = ['message' => $data];
|
|
}
|
|
}
|
|
// If the debug mode is enabled, add the debug data to the response
|
|
if ($DEBUG && false) {
|
|
$this->add_include('debug', [
|
|
'memory' => memory_get_usage(),
|
|
'time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'],
|
|
'data' => $this->get_data()
|
|
]);
|
|
}
|
|
try {
|
|
release_manager::recordBackendFailure($success, $data, $status ?? ($success ? 200 : 400));
|
|
} catch (\Throwable) {
|
|
// Release failure telemetry is best-effort and must not block responses.
|
|
}
|
|
echo json_encode([
|
|
'success' => $success,
|
|
'data' => $data,
|
|
'meta' => $this->meta,
|
|
'includes' => $this->includes
|
|
], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
#[NoReturn] public function rawJson(mixed $data, int $status = 200): void
|
|
{
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
http_response_code($status);
|
|
|
|
if (!is_array($data) && !is_object($data)) {
|
|
if (is_string($data) && json_decode($data) !== null) {
|
|
echo $data;
|
|
exit;
|
|
}
|
|
|
|
$data = ['message' => $data];
|
|
}
|
|
|
|
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
exit;
|
|
}
|
|
|
|
public function add_include(string $string, array $dataArray): void
|
|
{
|
|
$this->add_included($string, $dataArray);
|
|
}
|
|
|
|
public function add_included(string $key, mixed $value): void
|
|
{
|
|
$this->includes[$key] = $value;
|
|
}
|
|
|
|
public function get_data(): array
|
|
{
|
|
return $this->data;
|
|
}
|
|
|
|
public function get_meta(): array
|
|
{
|
|
return $this->meta;
|
|
}
|
|
|
|
public function get_includes(): array
|
|
{
|
|
return $this->includes;
|
|
}
|
|
|
|
#[NoReturn] public function not_found(): void
|
|
{
|
|
$this->error('Not found', 404);
|
|
}
|
|
|
|
#[NoReturn] public function error(mixed $data, int $status = null): void
|
|
{
|
|
$this->response(false, $data, $status);
|
|
}
|
|
|
|
#[NoReturn] public function rate_limit_exceeded(): void
|
|
{
|
|
$this->error('Rate limit exceeded', 429);
|
|
}
|
|
|
|
public function matching_route_found(): void
|
|
{
|
|
$this->matching_route_found = true;
|
|
}
|
|
|
|
#[NoReturn] public function method_not_allowed(): void
|
|
{
|
|
$this->error('Method not allowed', 405);
|
|
}
|
|
|
|
#[NoReturn] public function internal_server_error($error): void
|
|
{
|
|
$this->error('Internal server error' . ($error ? ': ' . $error : ''), 500);
|
|
}
|
|
|
|
#[NoReturn] public function forbidden(array $permissions): void
|
|
{
|
|
$this->error([
|
|
'message' => 'Missing permission(s)',
|
|
'permissions' => $permissions
|
|
], 403);
|
|
}
|
|
|
|
public function paginate(int $page, int $per_page, int $total, string $search = null, array $filters = null, array $order = null): void
|
|
{
|
|
// If the total is 0, return 1 page, 0 total
|
|
if ($total === 0) {
|
|
$total = 1;
|
|
}
|
|
$this->add_meta('pagination', [
|
|
'page' => $page,
|
|
'per_page' => $per_page,
|
|
'total' => $total,
|
|
'search' => $search,
|
|
'filters' => $filters,
|
|
'order' => $order
|
|
]);
|
|
}
|
|
|
|
public function add_meta(string $key, mixed $value): void
|
|
{
|
|
$this->meta[$key] = $value;
|
|
}
|
|
|
|
public function is_matching_route_found(): bool
|
|
{
|
|
return $this->matching_route_found;
|
|
}
|
|
|
|
public function add_debug(mixed $data): void
|
|
{
|
|
$this->add_data('debug', $data);
|
|
}
|
|
|
|
public function add_data(string $key, mixed $value): void
|
|
{
|
|
$this->data[$key] = $value;
|
|
}
|
|
|
|
public function getRequestParameter(string $key): mixed
|
|
{
|
|
return $this->requestParametersForMethod()[$key] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Get all request parameters
|
|
* @return array
|
|
*/
|
|
public function getAllRequestParameters(): array
|
|
{
|
|
return $this->requestParametersForMethod();
|
|
}
|
|
|
|
/**
|
|
* This function checks if a request parameter is set.
|
|
* This is different from getRequestParameter because this function allows for empty & null values.
|
|
* @param string $key
|
|
* @return bool
|
|
*/
|
|
public function isRequestParameterSet(string $key): bool
|
|
{
|
|
return array_key_exists($key, $this->requestParametersForMethod());
|
|
}
|
|
|
|
private function requestParametersForMethod(): array
|
|
{
|
|
$method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
|
|
|
|
if (in_array($method, ['POST', 'PUT', 'PATCH'], true)) {
|
|
return array_replace($_GET, $this->jsonRequestBody());
|
|
}
|
|
|
|
if ($method === 'DELETE') {
|
|
return array_replace($_GET, $this->jsonRequestBody());
|
|
}
|
|
|
|
if ($method === 'GET' || $method === 'OPTIONS') {
|
|
return $_GET;
|
|
}
|
|
|
|
return $this->jsonRequestBody();
|
|
}
|
|
|
|
private function jsonRequestBody(): array
|
|
{
|
|
if ($this->jsonRequestBody !== null) {
|
|
return $this->jsonRequestBody;
|
|
}
|
|
|
|
$decoded = json_decode($this->rawRequestBody(), true);
|
|
$this->jsonRequestBody = is_array($decoded) ? $decoded : [];
|
|
|
|
return $this->jsonRequestBody;
|
|
}
|
|
|
|
protected function rawRequestBody(): string
|
|
{
|
|
$body = file_get_contents('php://input');
|
|
return is_string($body) ? $body : '';
|
|
}
|
|
|
|
public function parseFilters(?string $filters): array|null
|
|
{
|
|
if ($filters) {
|
|
$filters = explode(',', $filters);
|
|
$temp = [];
|
|
foreach ( $filters as $filter ) {
|
|
$filter = explode(':', $filter);
|
|
$temp[$filter[0]] = $filter[1];
|
|
}
|
|
$filters = $temp;
|
|
}
|
|
if ($filters) {
|
|
return $filters;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function add_debug_list(string $list_name, int $key, $value): void
|
|
{
|
|
$this->add_data($list_name, [$key => $value]);
|
|
}
|
|
|
|
#[NoReturn] public function displayPDF($raw_pdf): void
|
|
{
|
|
self::disableAll();
|
|
$virtual_file = fopen('php://temp', 'r+');
|
|
fwrite($virtual_file, $raw_pdf);
|
|
fseek($virtual_file, 0);
|
|
file_put_contents('/tmp/debug_output.pdf', stream_get_contents($virtual_file));
|
|
// Return the PDF /tmp/debug_output.pdf
|
|
header('Content-Type: application/pdf');
|
|
header('Content-Disposition: inline; filename="debug_output.pdf"');
|
|
header('Content-Length: ' . filesize('/tmp/debug_output.pdf'));
|
|
header('Content-Transfer-Encoding: binary');
|
|
header('Accept-Ranges: bytes');
|
|
readfile('/tmp/debug_output.pdf');
|
|
exit;
|
|
|
|
}
|
|
|
|
private static function disableAll(): void
|
|
{
|
|
error_reporting(0);
|
|
ini_set('display_errors', '0');
|
|
}
|
|
|
|
/**
|
|
* This function gets the user object from the token
|
|
* @throws Exception If the token is invalid, or the user is not found
|
|
*/
|
|
public function get_user(): users_o|false
|
|
{
|
|
// Check if the user is already set
|
|
if (!isset($this->users_o)) {
|
|
// Get the user object
|
|
$this->users_o = (new authentication())->get_user();
|
|
}
|
|
// Return the user object
|
|
return $this->users_o;
|
|
}
|
|
}
|