Hook Trait
- trait Atk4\Core\HookTrait
Introduction
HookTrait adds some methods into your class to registering call-backs that would
be executed by triggering a hook. All hooks are local to the object, so if you
want to have application-wide hook then use app
property.
Hook Spots
Hook is described by a string identifier which we call hook-spot, which would normally be expressing desired action with prefixes “before” or “after” if necessary.
Some good examples for hook spots are:
beforeSave
afterDelete
validation
The framework or application would typically execute hooks like this:
$obj->hook('spot');
You can register multiple call-backs to be executed for the requested spot
:
$obj->onHook('spot', function ($obj) {
echo "Hook 'spot' is called!";
});
Adding callbacks
- Atk4\Core\HookTrait::onHook($spot, $fx = null, array $args = [], int $priority = 5)
Register a call-back method. Calling several times will register multiple callbacks which will be execute in the order that they were added.
Short way to describe callback method
There is a concise syntax for using $fx by specifying object only. In case $fx is omitted then $this object is used as $fx.
In this case a method with same name as $spot will be used as callback:
protected function init(): void
{
parent::init();
$this->onHookShort($spot, function (...$args) {
$this->beforeUpdate(...$args);
});
}
protected function beforeUpdate()
{
// will be called from the hook
}
Callback execution order
$priority will make hooks execute faster. Default priority is 5, but if you add hook with priority 1 it will always be executed before any hooks with priority 2, 3, 5 etc.
Normally hooks are executed in the same order as they are added, however if you use negative priority, then hooks will be executed in reverse order:
$obj->onHook('spot', third, [], -1);
$obj->onHook('spot', second, [], -5);
$obj->onHook('spot', first, [], -5);
$obj->onHook('spot', fourth, [], 0);
$obj->onHook('spot', fifth, [], 0);
$obj->onHook('spot', ten, [], 1000);
$obj->onHook('spot', sixth, [], 2);
$obj->onHook('spot', seventh, [], 5);
$obj->onHook('spot', eight);
$obj->onHook('spot', nine, [], 5);
- Atk4\Core\HookTrait::hook($spot, $args = null)
execute all hooks in order. Hooks can also return some values and those values will be placed in array and returned by hook():
$mul = function ($obj, $a, $b) {
return $a * $b;
};
$add = function ($obj, $a, $b) {
return $a + $b;
};
$obj->onHook('test', $mul);
$obj->onHook('test', $add);
$res1 = $obj->hook('test', [2, 2]);
// res1 = [4, 4]
$res2 = $obj->hook('test', [3, 3]);
// res2 = [9, 6]
Arguments
As you see in the code above, we were able to pass some arguments into those hooks. There are actually 3 sources that are considered for the arguments:
first argument to callbacks is always the $object
arguments passed as 3rd argument to onHook() are included
arguments passed as 2nd argument to hook() are included
You can also use key declarations if you wish to override arguments:
// continue from above example
$pow = function ($obj, $a, $b, $power) {
return pow($a, $power) + pow($b, $power);
}
$obj->onHook('test', $pow, [2]);
$obj->onHook('test', $pow, [7]);
// execute all 3 hooks
$res3 = $obj->hook('test', [2, 2]);
// res3 = [4, 4, 8, 256]
$res4 = $obj->hook('test', [2, 3]);
// res3 = [6, 5, 13, 2315]
Breaking Hooks
- Atk4\Core\HookTrait::breakHook()
When this method is called from a call-back then it will cause all other callbacks to be skipped.
If you pass $return argument then instead of returning all callback return values in array the $return will be returned by hook() method.
If you do not pass $return value (or specify null) then list of the values collected so far will be returned
Remember that adding breaking hook with a lower priority can prevent other call-backs from being executed:
$obj->onHook('test', function ($obj) {
$obj->breakHook("break1");
});
$obj->onHook('test', function ($obj) {
$obj->breakHook("break2");
}, [], -5);
$res3 = $obj->hook('test', [4, 4]);
// res3 = "break2"
breakHook method is implemented by throwing a special exception that is then caught inside hook() method.
Using references in hooks
In some cases you want hook to change certain value. For example when model value is set it may call normalization hook (methods will change $value):
public function set($field, $value)
{
$this->hook('normalize', [&$value]);
$this->data[$field] = $value;
}
$m->onHook('normalize', function (&$a) {
$a = trim($a);
});
Checking if hook has callbacks
- Atk4\Core\HookTrait::hookHasCallbacks()
This method will return true if at least one callback has been set for the hook.