summaryrefslogtreecommitdiff
path: root/src/lib/hooks.php
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-12-23 10:39:16 -0500
committerFreya Murphy <freya@freyacat.org>2024-12-23 10:39:16 -0500
commitde9cae795f93d03e68d965c59af4b21d96df4ec7 (patch)
treead4f903c04630b3b92e2b9b5d06d5b8647d299bb /src/lib/hooks.php
parentlicense (diff)
downloadcrimson-de9cae795f93d03e68d965c59af4b21d96df4ec7.tar.gz
crimson-de9cae795f93d03e68d965c59af4b21d96df4ec7.tar.bz2
crimson-de9cae795f93d03e68d965c59af4b21d96df4ec7.zip
initial
Diffstat (limited to '')
-rw-r--r--src/lib/hooks.php178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/lib/hooks.php b/src/lib/hooks.php
new file mode 100644
index 0000000..21854bd
--- /dev/null
+++ b/src/lib/hooks.php
@@ -0,0 +1,178 @@
+<?php
+/// CRIMSON --- A simple PHP framework.
+/// Copyright © 2024 Freya Murphy <contact@freyacat.org>
+///
+/// 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 <http://www.gnu.org/licenses/>.
+
+/**
+ * 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_<name>_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]);
+}