Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expanded hooks a little bit #179

Merged
merged 4 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/AntCMS/AntCMS.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function renderPage(?string $page = null): string
$this->renderException("404");
}

HookController::fire('contentHit', ['contentUri' => $page]);
HookController::fire('onAfterContentHit', ['contentUri' => $page]);

$themeConfig = self::getThemeConfig();
$params = [
Expand Down
2 changes: 1 addition & 1 deletion src/AntCMS/AntYaml.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static function saveFile(string $path, array $data): bool
}

/**
* Parses a string containing YAML data and returns the content as an array.
* Parses a string containing YAML data and returns the content as an array.
* @return mixed[]
*/
public static function parseYaml(string $yaml): array
Expand Down
6 changes: 3 additions & 3 deletions src/AntCMS/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -91,7 +91,7 @@ private function call(string $type, string $plugin, string $method): void
}

$hookData = ['plugin' => ucfirst($plugin), 'method' => $method];
HookController::fire('beforeApiCalled', $hookData);
HookController::fire('onBeforeApiCalled', $hookData);

// Sanity checks passed, now actually process the request
try {
Expand All @@ -108,7 +108,7 @@ private function call(string $type, string $plugin, string $method): void
}

$hookData['response'] = $response;
HookController::fire('afterApiCalled', $hookData);
HookController::fire('onAfterApiCalled', $hookData);

$this->sendResponse($response);
}
Expand Down
2 changes: 1 addition & 1 deletion src/AntCMS/ApiResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class ApiResponse
{
public function __construct(private mixed $result, private readonly bool $error = false, private readonly int $code = 200, private string $message = '')
public function __construct(private readonly mixed $result, private readonly bool $error = false, private readonly int $code = 200, private readonly string $message = '')
{
}

Expand Down
12 changes: 6 additions & 6 deletions src/AntCMS/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class Cache

/**
* Configures the cache system by selecting and prioritizing specific cache adapters.
*
*
* The `$allowed` array specifies which cache types to enable. Available options are 'apcu', 'php_file', and 'filesystem'.
*
*
* @param string[] $allowed An array of allowed cache adapter names.
*/
public static function setup(array $allowed = []): void
Expand Down Expand Up @@ -49,13 +49,13 @@ public static function setup(array $allowed = []): void

/**
* Retrieves a value from the cache.
*
*
* If the value is not found in the cache, it executes the provided callable and stores the result for future retrieval.
*
*
* @param string $key The unique identifier for the cached value.
* @param callable|CallbackInterface $callable A function that returns the value to be cached.
* @param ?float $beta (Optional) Controls the cache update strategy. See Symfony documentation for details.
* @param ?mixed[] &$metadata (Optional) Stores metadata about the cached item.
* @param ?mixed[] &$metadata (Optional) Stores metadata about the cached item.
* @return mixed The cached value or the result of the callable if it's not found.
*/
public static function get(string $key, callable|CallbackInterface $callable, ?float $beta = null, ?array &$metadata = []): mixed
Expand All @@ -65,7 +65,7 @@ public static function get(string $key, callable|CallbackInterface $callable, ?f

/**
* Prunes the cache. This removes stale entries that are no longer needed.
*
*
* @return bool True if any items were pruned, false otherwise.
*/
public static function prune(): bool
Expand Down
38 changes: 36 additions & 2 deletions src/AntCMS/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
Expand Down Expand Up @@ -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
{
Expand All @@ -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;
}
}
5 changes: 3 additions & 2 deletions src/AntCMS/Hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand All @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions src/AntCMS/HookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 !== '') {
Expand All @@ -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;
}

Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/AntCMS/Pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/AntCMS/PluginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public static function init(): void
Twig::addLoaderPath($templateDir);
}
}

HookController::fire('onAfterPluginsInit');
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/AntCMS/Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace AntCMS;

use Flight;
use HostByBelle\CompressionBuffer;
use Symfony\Contracts\Cache\ItemInterface;

Expand Down Expand Up @@ -257,7 +258,7 @@ public static function getPerformanceMetrics(): array
];
}

private static function createDebugLogLine(string $wording, bool|string $value): string
private static function createDebugLogLine(string $wording, bool|string|int $value): string
{
if (is_bool($value)) {
$value = $value ? "enabled" : "disabled";
Expand Down Expand Up @@ -291,6 +292,7 @@ public static function buildDebugInfo(): string
$result .= self::createDebugLogLine('This page was compressed with', $method);
}

$result .= self::createDebugLogLine('Page output size', Flight::response()->getContentLength());
$result .= self::createDebugLogLine('Asset compression', COMPRESS_TEXT_ASSETS);
$result .= self::createDebugLogLine('Image compression', COMPRESS_IMAGES);
$result .= self::createDebugLogLine('PHP version', PHP_VERSION);
Expand Down
1 change: 0 additions & 1 deletion src/Plugins/Robotstxt/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use AntCMS\Config;
use AntCMS\PluginController;
use AntCMS\Tools;

use Flight;

class Controller extends AbstractPlugin
Expand Down
25 changes: 19 additions & 6 deletions src/Plugins/System/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace AntCMS\Plugins\System;

use AntCMS\AbstractPlugin;
use AntCMS\AntCMS;
use AntCMS\HookController;

class Controller extends AbstractPlugin
Expand All @@ -11,13 +12,15 @@ class Controller extends AbstractPlugin
* @var array<string, string>
*/
private array $hooks = [
'contentHit' => 'This is fired when markdown content is accessed. The URI will be passed in the data.',
'performanceMetricsBuilt' => 'When fired, this event contains all performance metrics AntCMS was able to collect on a request. These are more complete & accurate than the metrics shown on the bottom of the screen.',
'beforeApiCalled' => 'This event is fired before an API endpoint is called',
'afterApiCalled' => 'This event is fired after an API endpoint is called and the response is available',
'onAfterContentHit' => 'This is fired when markdown content is accessed. The URI will be passed in the data.',
'onAfterPerformanceMetricsBuilt' => 'When fired, this event contains all performance metrics AntCMS was able to collect on a request. These are more complete & accurate than the metrics shown on the bottom of the screen.',
'onBeforeApiCalled' => 'This event is fired before an API endpoint is called.',
'onAfterApiCalled' => 'This event is fired after an API endpoint is called and the response is available.',
'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',
'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.',
'onBeforeOutputFlushed' => '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()
Expand All @@ -27,6 +30,16 @@ public function __construct()
HookController::registerHook($name, $description);
}

HookController::registerCallback('onBeforeOutputFlushed', $this->appendDebugInfo(...));

$this->addDisallow('/api/*');
}

private function appendDebugInfo(\AntCMS\Event $event): \AntCMS\Event
{
$params = $event->getParameters();
$params['output'] = str_replace('<!--AntCMS-Debug-->', \AntCMS\Tools::buildDebugInfo(), $params['output'] ?? '');
$event->setParameters($params);
return $event;
}
}
9 changes: 6 additions & 3 deletions src/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@

$AntCMS = new AntCMS();

// Add a response body callback to display debug info
Flight::response()->addResponseBodyCallback(fn ($body): string => str_replace('<!--AntCMS-Debug-->', 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('onBeforeOutputFlushed', ['output' => $body]);
return $event->getParameters()['output'];
});

// Setup CompressionBuffer & enable it in Flight
CompressionBuffer::setUp(true, false, [Flight::response(), 'header']);
Expand All @@ -27,7 +30,7 @@
}

Flight::response()->addResponseBodyCallback(function ($body) {
HookController::fire('performanceMetricsBuilt', tools::getPerformanceMetrics());
HookController::fire('onAfterPerformanceMetricsBuilt', tools::getPerformanceMetrics());
return $body;
});

Expand Down