Files
api/services/nginx/app/classes/response.php
T
Jeppe Bundgaard ad0277ff17 Enhance input validation, unicode handling, and sanitization across department goal criteria
- Add `validateAndSanitize` method to `goals_criteria` for input validation and defaulting incorrect values.
- Ensure JSON encoding in `response` and database interactions respects Unicode (`JSON_UNESCAPED_UNICODE`).
- Sanitize and validate input for `department_goals_o::add` and criteria usage.
- Fix header character encoding in response (`Content-Type: application/json; charset=utf-8`).
- Update criteria `label` sanitization with trimming, length limits, and safe character handling.
2026-01-27 08:20:33 +01:00

281 lines
8.6 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;
#[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()
]);
}
echo json_encode([
'success' => $success,
'data' => $data,
'meta' => $this->meta,
'includes' => $this->includes
], JSON_UNESCAPED_UNICODE);
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;
}
#[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);
}
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
{
$data = [];
// Get the request data if the method is POST, PUT or PATCH
if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
$data = json_decode(file_get_contents('php://input'), true);
}
// Get the request data if the method is GET, DELETE or OPTIONS
if ($_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
$data = $_GET;
}
if (!is_array($data)) {
$data = [];
}
// If the data key is not set, try to get it from the opposite method
if (!array_key_exists($key, $data)) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
$data = $_GET;
} else {
$data = json_decode(file_get_contents('php://input'), true);
}
}
if (!is_array($data)) {
$data = [];
}
// Return the data
return $data[$key] ?? null;
}
/**
* Get all request parameters
* @return array
*/
public function getAllRequestParameters(): array
{
$data = [];
// Get the request data if the method is POST, PUT or PATCH
if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
$data = json_decode(file_get_contents('php://input'), true);
}
// Get the request data if the method is GET, DELETE or OPTIONS
if ($_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
$data = $_GET;
}
if (!is_array($data)) {
return [];
}
return $data;
}
/**
* 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
{
$data = [];
// Get the request data if the method is POST, PUT or PATCH
if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') {
$data = json_decode(file_get_contents('php://input'), true);
}
// Get the request data if the method is GET, DELETE or OPTIONS
if ($_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'DELETE' || $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
$data = $_GET;
}
if (!is_array($data)) {
return false;
}
return array_key_exists($key, $data);
}
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;
}
}