205 lines
6.1 KiB
PHP
205 lines
6.1 KiB
PHP
<?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));
|
|
}
|
|
}
|