From 619026a844e89d2283dc25f8d140f048be2b2a01 Mon Sep 17 00:00:00 2001 From: Belle Aerni Date: Thu, 10 Oct 2024 00:26:46 -0700 Subject: [PATCH] Expanded hooks a little bit --- src/AntCMS/ApiController.php | 2 +- src/AntCMS/Event.php | 38 ++++++++++++++++++++++++++-- src/AntCMS/Hook.php | 5 ++-- src/AntCMS/HookController.php | 8 +++--- src/AntCMS/Pages.php | 4 +-- src/AntCMS/PluginController.php | 2 ++ src/Plugins/Robotstxt/Controller.php | 1 - src/Plugins/System/Controller.php | 13 ++++++++++ src/index.php | 7 +++-- 9 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/AntCMS/ApiController.php b/src/AntCMS/ApiController.php index 4a0e16e..3885389 100644 --- a/src/AntCMS/ApiController.php +++ b/src/AntCMS/ApiController.php @@ -35,7 +35,7 @@ private function getApiCallData(string $plugin, string $method): array { // Some needed variable setup $url = rtrim(Flight::request()->url, '/'); - if($_GET !== []) { + if ($_GET !== []) { $query = '?' . http_build_query($_GET); $url = str_replace($query, '', $url); } diff --git a/src/AntCMS/Event.php b/src/AntCMS/Event.php index d0a89f3..21a516f 100644 --- a/src/AntCMS/Event.php +++ b/src/AntCMS/Event.php @@ -14,13 +14,14 @@ class Event private int $paramUpdateCount = 0; private int $paramReadCount = 0; private int $lastCallback = 0; + private bool $defaultPrevented = false; /** * @param string $associatedHook The hook that this event is associated with. Hook must exist. * * @param mixed[] $parameters */ - public function __construct(string $associatedHook, private array $parameters, private readonly int $totalCallbacks) + public function __construct(string $associatedHook, private array $parameters, private readonly int $totalCallbacks, private readonly bool $preventable) { if (!HookController::isRegistered($associatedHook)) { throw new \Exception("Hook $associatedHook is not registered!"); @@ -127,7 +128,7 @@ public function setParameters(array $parameters): Event } /** - * Returns the number of times the event parameters were read from + * Returns the number of times the event parameters were read from. */ public function getReadCount(): int { @@ -141,4 +142,37 @@ public function getUpdateCount(): int { return $this->paramUpdateCount; } + + /** + * Indicates if the default behavior for an event is preventable. + */ + public function isDefaultPreventable(): bool + { + return $this->preventable; + } + + /** + * Indicates if the default behavior for an event is prevented. + */ + public function isDefaultPrevented(): bool + { + return $this->defaultPrevented; + } + + /** + * Sets a flag for the default behavior of this event to be prevented. + * Not all events can be prevented. Triggers a non-fatal error if the event's default behavior is not preventable. + * + * @return Event + */ + public function preventDefault(): Event + { + if (!$this->isDefaultPreventable()) { + trigger_error("The default behavior for the `$this->associatedHook` hook cannot be prevented."); + } else { + $this->defaultPrevented = true; + } + + return $this; + } } diff --git a/src/AntCMS/Hook.php b/src/AntCMS/Hook.php index d3a6253..c0e811a 100644 --- a/src/AntCMS/Hook.php +++ b/src/AntCMS/Hook.php @@ -20,8 +20,9 @@ class Hook * * @param string $name The name of the hook * @param string $description A description of this hook + * @param bool $isDefaultPreventable Marks if the default behavior for a hook can be prevented. */ - public function __construct(string $name, public string $description) + public function __construct(string $name, public string $description, private readonly bool $isDefaultPreventable = false) { if (preg_match('/^\w+$/', $name) === 0 || preg_match('/^\w+$/', $name) === false) { throw new \Exception("The hook name '$name' is invalid. Only a-z A-Z, 0-9, and _ are allowed to be in the hook name."); @@ -40,7 +41,7 @@ public function fire(array $params): Event $this->timesFired++; // Create the new event object with the originally provided parameters - $event = new Event($this->name, $params, $this->registeredCallbacks); + $event = new Event($this->name, $params, $this->registeredCallbacks, $this->isDefaultPreventable); // Then fire each of the callbacks and update the event instance from each one. foreach ($this->callbacks as $callback) { diff --git a/src/AntCMS/HookController.php b/src/AntCMS/HookController.php index 4d7053b..7e1895e 100644 --- a/src/AntCMS/HookController.php +++ b/src/AntCMS/HookController.php @@ -20,7 +20,7 @@ public static function isRegistered(string $name): bool return array_key_exists($name, self::$hooks); } - public static function registerHook(string $name, string $description = ''): bool + public static function registerHook(string $name, string $description = '', bool $isDefaultPreventable = false): bool { if (self::isRegistered($name)) { if ($description !== '') { @@ -29,7 +29,7 @@ public static function registerHook(string $name, string $description = ''): boo return true; } - self::$hooks[$name] = new Hook($name, $description); + self::$hooks[$name] = new Hook($name, $description, $isDefaultPreventable); return true; } @@ -42,9 +42,9 @@ public static function registerCallback(string $name, callable $callback): void } /** - * @param mixed[] $params + * @param mixed[] $params (Optional) */ - public static function fire(string $name, array $params): Event + public static function fire(string $name, array $params = []): Event { if (self::isRegistered($name)) { return self::$hooks[$name]->fire($params); diff --git a/src/AntCMS/Pages.php b/src/AntCMS/Pages.php index 026b18d..e4ef6fe 100644 --- a/src/AntCMS/Pages.php +++ b/src/AntCMS/Pages.php @@ -109,10 +109,10 @@ private static function buildList(string $path = PATH_CONTENT): array if (isset($directoryMeta['pageOrder'][$a]) && isset($directoryMeta['pageOrder'][$b])) { return $directoryMeta['pageOrder'][$a] > $directoryMeta['pageOrder'][$b] ? 1 : -1; } - if(isset($directoryMeta['pageOrder'][$a]) && !isset($directoryMeta['pageOrder'][$b])) { + if (isset($directoryMeta['pageOrder'][$a]) && !isset($directoryMeta['pageOrder'][$b])) { return -1; } - if(!isset($directoryMeta['pageOrder'][$a]) && isset($directoryMeta['pageOrder'][$b])) { + if (!isset($directoryMeta['pageOrder'][$a]) && isset($directoryMeta['pageOrder'][$b])) { return 1; } // Ensure index items come first diff --git a/src/AntCMS/PluginController.php b/src/AntCMS/PluginController.php index c92f0e1..9a90da9 100644 --- a/src/AntCMS/PluginController.php +++ b/src/AntCMS/PluginController.php @@ -50,6 +50,8 @@ public static function init(): void Twig::addLoaderPath($templateDir); } } + + HookController::fire('onAfterPluginsInit'); } /** diff --git a/src/Plugins/Robotstxt/Controller.php b/src/Plugins/Robotstxt/Controller.php index 7f95986..9a17584 100644 --- a/src/Plugins/Robotstxt/Controller.php +++ b/src/Plugins/Robotstxt/Controller.php @@ -6,7 +6,6 @@ use AntCMS\Config; use AntCMS\PluginController; use AntCMS\Tools; - use Flight; class Controller extends AbstractPlugin diff --git a/src/Plugins/System/Controller.php b/src/Plugins/System/Controller.php index 0165d5c..0de98eb 100644 --- a/src/Plugins/System/Controller.php +++ b/src/Plugins/System/Controller.php @@ -3,6 +3,7 @@ namespace AntCMS\Plugins\System; use AntCMS\AbstractPlugin; +use AntCMS\AntCMS; use AntCMS\HookController; class Controller extends AbstractPlugin @@ -18,6 +19,8 @@ class Controller extends AbstractPlugin 'onHookFireComplete' => 'This event is fired when others have completed. The data provided will include the hook name, timing data, and parameter read / update statistics.', 'onBeforeMarkdownParsed' => 'This event is fired before markdown is converted, allowing for pre-processing before the markdown is run through the parser', 'onAfterMarkdownParsed' => 'This is fired after markdown is converted, allowing you to modify generated markdown content', + 'onAfterPluginsInit' => 'This event is fired after all plugins have been initialized.', + 'onBeforeOutputFinalized' => 'This event is fired right before the generated response is finalized (compressed) and sent to the browser. No later chances to modify the output buffer exist.', ]; public function __construct() @@ -27,6 +30,16 @@ public function __construct() HookController::registerHook($name, $description); } + HookController::registerCallback('onBeforeOutputFinalized', $this->appendDebugInfo(...)); + $this->addDisallow('/api/*'); } + + private function appendDebugInfo(\AntCMS\Event $event): \AntCMS\Event + { + $params = $event->getParameters(); + $params['output'] = str_replace('', \AntCMS\Tools::buildDebugInfo(), $params['output'] ?? ''); + $event->setParameters($params); + return $event; + } } diff --git a/src/index.php b/src/index.php index 1e7ce8c..2d6f3d3 100644 --- a/src/index.php +++ b/src/index.php @@ -17,8 +17,11 @@ $AntCMS = new AntCMS(); -// Add a response body callback to display debug info -Flight::response()->addResponseBodyCallback(fn ($body): string => str_replace('', Tools::buildDebugInfo(), $body)); +// Use hooks to perform any final changes to the output buffer before compressing and sending it +Flight::response()->addResponseBodyCallback(function (string $body): string { + $event = HookController::fire('onBeforeOutputFinalized', ['output' => $body]); + return $event->getParameters()['output']; +}); // Setup CompressionBuffer & enable it in Flight CompressionBuffer::setUp(true, false, [Flight::response(), 'header']);