/// /// This file is part of CRIMSON. /// /// CRIMSON is free software; you can redistribute it and/or modify it /// under the terms of the GNU General Public License as published by /// the Free Software Foundation; either version 3 of the License, or (at /// your option) any later version. /// /// CRIMSON is distributed in the hope that it will be useful, but /// WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details. /// /// You should have received a copy of the GNU General Public License /// along with CRIMSON. If not, see . /** * CRIMSON HOOKS * * Crimson supports hooks to allow the user to add functionality inside * crimson's control flow. This can be used for handling errors, or having * custom routing functionality. * * The current supported hooks are: 'error', 'pre_route', 'init', and 'die'. * * To run a hook yourself you can call CRIMSON_HOOK($name, [$args]); * * To create a handler, make a function in the global scope called: * CRIMSON__hook. i.e. if you are making a init hook handler, make a * function called CRIMSON_init_hook(...). * * If a hook is called and a user hook handeler does not exist, the crimson * builtin hook will be run instead. * * NOTE: It is also allowed for you to create your own hook types, but you must * also create your own handler for it. If you dont, it will result in a error * since crimson will not have a builtin hook handler to fall back to. * * NOTE: CRIMSON_HOOK does support a third argument called $builtin, but this * is only to be used by crimson itself, so please dont use it. * * WARNING: If a hook cannot find a handler to run, it will call * CRIMSON_FATAL_ERROR and NEVER return. */ /** * CRIMSON builtin ERROR hook. * * $req - The current request parsed by the Router. All fields are gurenteed * to be set BESIDES 'uri' and 'uri_str', since parsing of the URI can fail * and result to a call to this error handler. * * This hook is run when any Controller or the Router runs into an issue and * fails with an HTTP Error. * * EXAMPLE: If the Router cannot find the requested Controller and Method * based in the URI path, it will raise 404 Not Found. * * EXAMPLE: If the crimson postgres database is enabled, and db-init either * failed or is still running (the database is not ready), crimson will raise * 503 Service Unavaliable. * * EXAMPLE: If the provided URI to PHP is unable to be parsed, crimson will * raise 400 Bad Request. * * This is hook is called in ROUTER->handle_error(int code) and * Controller->error(int code). * * NOTE: Unlike CRIMSON's DIE hook, it is supported to re-enter crimson code. * This error handler must never return, but it is okay to recall crimson to * try to print an error page. * * WARNING: If the user tries to re-enter crimson from this handler and ends * up raising another error, the user error handler WILL NOT be called again. * To prevent recursion, a user error handle WILL only ever be called ONCE. * This is due to that the handler has return type 'never', and is also not * allowed to recurse. */ function CRIMSON_builtin_error_hook(array $req, int $code): never { http_response_code($code); CRIMSON_DIE("{$code} {$req['uri_str']}"); } /** * CRIMSON builtin PRE ROUTE hook. * * This hook does nothing by default since all required CRIMSON routing is done * in router.php. */ function CRIMSON_builtin_pre_route_hook(Router $router): void { } /** * CRIMSON builtin INIT hook. * * This hook does nothing by default since all required CRIMSON init work is * run in index.php. * * This hook can be overridden to run any user required initalization code * before CRIMSON launches and runs the router. * * WARNING: The ROUTER is NOT YET created when this hook is run. Do not call * ROUTER or ANY CRIMSON code in a user specified init hook. Doing so is * UNDEFINED BEHAVIOR. */ function CRIMSON_builtin_init_hook(): void { } /** * CRIMSON builtin DIE hook. * * Calls die with $status, i.e. this is an alias for die(). */ function CRIMSON_builtin_die_hook(string|int $status = 0): never { die($status); } /** * Executes a hook for crimson. * * $name - the name of the hook * $args - the arguments to pass to the hook * $builtin - force the use of the builtin hook * * Looks for CRIMSON_$name_hook first, which is the custom user defined hook. * If it does not exist, it will run CRIMSON_builtin_$name_hook instead. */ function CRIMSON_HOOK( string $name, array $args = array(), bool $builtin = FALSE, ): void { $names = array(); if (!$builtin) $names[] = "CRIMSON_{$name}_hook"; $names[] = "CRIMSON_builtin_{$name}_hook"; // find handler $handler = NULL; foreach ($names as $name) { try { $handler = new ReflectionFunction($name); if ($handler) break; } catch (Exception $_e) {}; } // error in invalid hook if (!$handler) { CRIMSON_ERROR("Invalid hook: {$name}"); return; } // i dont care to argument check, // if someone screws up a hook we have bigger problems $handler->invoke(...$args); } /** * Executes die() in php. But allows the user to add a hook to handle any * loose resources before php kills itself. * * NOTE: A DIE hook should NEVER be handeled and return. A user provided hook * must also have a return type of never and immediately die. Do NOT * call any crimson code such as rerunning the ROUTER. Doing so will result in * UNDEFINED BEHAVIOR and nasal demons showing up at your doorstep!!! */ function CRIMSON_DIE(string|int $status = 0): never { CRIMSON_HOOK('die', [$status]); }