Skip to content

Commit

Permalink
Make sure same hook spot is not updated while executing
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Oct 4, 2021
1 parent 10a3fb5 commit 9d44d77
Showing 1 changed file with 25 additions and 17 deletions.
42 changes: 25 additions & 17 deletions src/HookTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@

trait HookTrait
{
/**
* Contains information about configured hooks (callbacks).
*
* @var array<string, array<int, array{0: \Closure, 1?: array<int, mixed>}>>
*/
/** @var array<string, array<int, array{0: \Closure, 1?: array<int, mixed>}>> Configured hooks (callbacks). */
protected $hooks = [];

/**
* Next hook index counter.
*
* @var int
*/
/** @var int Next hook index counter. */
private $_hookIndexCounter = 0;

/** @var string[] */
private $_hookActiveSpots = [];

private function _hookRequireInactive(string $spot): void
{
if ($this->_hookActiveSpots[$spot] ?? false) {
throw (new Exception('Hook spot must be inactive for requested operation, but it is already executing'))
->addMoreInfo('object', $this)
->addMoreInfo('spot', $spot);
}
}

/**
* @var static
*/
Expand Down Expand Up @@ -80,6 +84,7 @@ private function _rebindHooksIfCloned(): void
public function onHook(string $spot, \Closure $fx, array $args = [], int $priority = 5): int
{
$this->_rebindHooksIfCloned();
$this->_hookRequireInactive($spot);

if (!isset($this->hooks[$spot][$priority])) {
$this->hooks[$spot][$priority] = [];
Expand Down Expand Up @@ -175,6 +180,8 @@ public function onHookDynamicShort(string $spot, \Closure $getFxThisFx, \Closure
*/
public function removeHook(string $spot, int $priority = null, bool $priorityIsIndex = false)
{
$this->_hookRequireInactive($spot);

if ($priority !== null) {
if ($priorityIsIndex) {
$index = $priority;
Expand Down Expand Up @@ -228,17 +235,18 @@ public function hookHasCallbacks(string $spot, int $priority = null, bool $prior
*/
public function hook(string $spot, array $args = [], HookBreaker &$brokenBy = null)
{
$this->_rebindHooksIfCloned();

$brokenBy = null;
$this->_rebindHooksIfCloned();
$this->_hookRequireInactive($spot);

$return = [];

if (isset($this->hooks[$spot])) {
krsort($this->hooks[$spot]); // lower priority is called sooner
$hooksBackup = $this->hooks[$spot];
ksort($this->hooks[$spot]); // lower priority is called sooner

try {
while ($hooks = array_pop($this->hooks[$spot])) {
$this->_hookActiveSpots[$spot] = true;

foreach ($this->hooks[$spot] as $hooks) {
foreach ($hooks as $index => [$hookFx, $hookArgs]) {
$return[$index] = $hookFx($this, ...$args, ...$hookArgs);
}
Expand All @@ -248,7 +256,7 @@ public function hook(string $spot, array $args = [], HookBreaker &$brokenBy = nu

return $e->getReturnValue();
} finally {
$this->hooks[$spot] = $hooksBackup;
$this->_hookActiveSpots[$spot] = false;
}
}

Expand Down

0 comments on commit 9d44d77

Please sign in to comment.