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; } }