<?php
/**
* @file classes/plugins/Hook.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Hook
*
* @ingroup plugins
*
* @brief Class implementing a registry of hooks for extending core functionality
*/
namespace PKP\plugins;
use PKP\core\Registry;
class Hook
{
public const SEQUENCE_CORE = 0;
public const SEQUENCE_NORMAL = 256;
public const SEQUENCE_LATE = 512;
public const SEQUENCE_LAST = 768;
public const CONTINUE = false;
public const ABORT = true;
/**
* Get the current set of hook registrations.
*
* @param string $hookName Name of hook to optionally return
*
* @return mixed Array of all hooks or just those attached to $hookName, or
* null if nothing has been attached to $hookName
*/
public static function &getHooks(?string $hookName = null): ?array
{
$hooks = & Registry::get('hooks', true, []);
if ($hookName === null) {
return $hooks;
}
if (isset($hooks[$hookName])) {
return $hooks[$hookName];
}
$returner = null;
return $returner;
}
/**
* Clear hooks registered against the given name.
*/
public static function clear(string $hookName): void
{
$hooks = & static::getHooks();
unset($hooks[$hookName]);
}
/**
* Register a hook against the given hook name.
*
* @param string $hookName Name of hook to register against
* @param object|array $callback Callback pseudo-type
* @param int $hookSequence Optional hook sequence specifier SEQUENCE_...
*/
public static function add(string $hookName, callable $callback, int $hookSequence = self::SEQUENCE_NORMAL): void
{
$hooks = & static::getHooks();
$hooks[$hookName][$hookSequence][] = $callback;
}
/**
* Alias of Hook::add
*
* @deprecated 3.4
*/
public static function register(string $hookName, callable $callback, int $hookSequence = self::SEQUENCE_NORMAL): void
{
self::add($hookName, $callback, $hookSequence);
}
/**
* Call each callback registered against $hookName in sequence.
* The first callback that returns ABORT will interrupt processing and
* this function will return ABORT; otherwise, all callbacks will be
* called in sequence and the return value of this call will be
* CONTINUE.
*
* The signature of the callback function should be:
* function callback($hookName, $args) : bool;
* ...and $args will receive the array provided here.
*
* This function should be considered deprecated in favour of
* Hook::call.
*/
public static function call(string $hookName, array $args = []): mixed
{
// Called only by Unit Test
// This behaviour is DEPRECATED and not replicated in the preferred
// Hook::call function.
if (self::rememberCalledHooks(true)) {
// Remember the called hooks for testing.
$calledHooks = & static::getCalledHooks();
$calledHooks[] = [
$hookName, $args
];
}
return self::run($hookName, [$args]);
}
/**
* Call each callback registered against $hookName in sequence.
* The first callback that returns ABORT will interrupt processing and
* this function will return ABORT; otherwise, all callbacks will be
* called in sequence and the return value of this call will be
* CONTINUE.
*
* The signature of a callback function should be:
* function callback($hookName, ...) : bool;
* where ... corresponds to the parameters named/listed in the $args
* parameter to Hook::call. These may be named if desired,
* and may include references.
*/
public static function run(string $hookName, $args): bool
{
$hooks = & static::getHooks();
if (!isset($hooks[$hookName])) {
return self::CONTINUE;
}
ksort($hooks[$hookName], SORT_NUMERIC);
foreach ($hooks[$hookName] as $priority => $hookList) {
foreach ($hookList as $callback) {
if (call_user_func_array($callback, array_merge([$hookName], $args)) === self::ABORT) {
return self::ABORT;
}
}
}
return self::CONTINUE;
}
//
// Methods required for testing only.
//
/**
* Set/query the flag that triggers storing of
* called hooks.
*
* @param bool $askOnly When set to true, the flag will not
* be changed but only returned.
* @param bool $updateTo When $askOnly is set to 'true' then
* this parameter defines the value of the flag.
*
* @return bool The current value of the flag.
*/
public static function rememberCalledHooks(bool $askOnly = false, bool $updateTo = true): bool
{
static $rememberCalledHooks = false;
if (!$askOnly) {
$rememberCalledHooks = $updateTo;
}
return $rememberCalledHooks;
}
/**
* Switch off the function to store hooks and delete all stored hooks.
* Always call this after using otherwise we get a severe memory.
*
* @param bool $leaveAlive Set this to true if you only want to
* delete hooks stored so far but if you want to record future
* hook calls, too.
*/
public static function resetCalledHooks(bool $leaveAlive = false): void
{
if (!$leaveAlive) {
static::rememberCalledHooks(false, false);
}
$calledHooks = & static::getCalledHooks();
$calledHooks = [];
}
/**
* Return a reference to the stored hooks.
*/
public static function &getCalledHooks(): array
{
static $calledHooks = [];
return $calledHooks;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\plugins\Hook', '\HookRegistry');
foreach (['SEQUENCE_CORE', 'SEQUENCE_NORMAL', 'SEQUENCE_LATE', 'SEQUENCE_LAST'] as $constantName) {
define('HOOK_' . $constantName, constant('\HookRegistry::' . $constantName));
}
}
|