1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-15 11:14:12 +02:00

Add support for hooking multiple methods to the same event handler via addHook*() calls by separating the methods to hook with commas, or by providing an array rather than a string. Also updated the corresponding removeHook() method to support removing multiple in the same call.

This commit is contained in:
Ryan Cramer
2019-08-01 11:44:33 -04:00
parent b179aa9afc
commit 2f6497d1e5
3 changed files with 131 additions and 17 deletions

View File

@@ -627,8 +627,9 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*
* #pw-internal
*
* @param string $method Method name to hook into, NOT including the three preceding underscores.
* @param string|array $method Method name to hook into, NOT including the three preceding underscores.
* May also be Class::Method for same result as using the fromClass option.
* May also be array or CSV string of hook definitions to attach multiple to the same $toMethod (since 3.0.137).
* @param object|null|callable $toObject Object to call $toMethod from,
* Or null if $toMethod is a function outside of an object,
* Or function|callable if $toObject is not applicable or function is provided as a closure.
@@ -650,8 +651,10 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* must match, in order to execute hook. Default is null.
* - `objMatch` (array|null): Selectors object that the current object must match in order to execute hook.
* Default is null.
* @return string A special Hook ID that should be retained if you need to remove the hook later
* @return string A special Hook ID that should be retained if you need to remove the hook later.
* If multiple methods were hooked then it is a CSV string of hook IDs, accepted removeHook method (since 3.0.137).
* @throws WireException
* @see https://processwire.com/docs/modules/hooks/
*
*/
public function addHook($method, $toObject, $toMethod = null, $options = array()) {
@@ -682,9 +685,10 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*
* #pw-group-hooks
*
* @param string $method Method to hook in one of the following formats (please omit 3 leading underscores):
* @param string|array $method Method to hook in one of the following formats (please omit 3 leading underscores):
* - `Class::method` - If hooking to *all* object instances of the class.
* - `method` - If hooking to a single object instance.
* - Since 3.0.137 it may also be multiple methods to hook in CSV string or array.
* @param object|null|callable $toObject Specify one of the following:
* - Object instance to call `$toMethod` from (like `$this`).
* - Inline function (closure) if providing implemention inline.
@@ -698,7 +702,8 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* rather than $obj->method(). The default type is 'method'.
* - `priority` (int): A number determining the priority of a hook, where lower numbers are executed before
* higher numbers. The default priority is 100.
* @return string A special Hook ID that should be retained if you need to remove the hook later.
* @return string A special Hook ID (or CSV string of hook IDs) that should be retained if you need to remove the hook later.
* @see https://processwire.com/docs/modules/hooks/
*
*/
public function addHookBefore($method, $toObject, $toMethod = null, $options = array()) {
@@ -730,9 +735,10 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*
* #pw-group-hooks
*
* @param string $method Method to hook in one of the following formats (please omit 3 leading underscores):
* @param string|array $method Method to hook in one of the following formats (please omit 3 leading underscores):
* - `Class::method` - If hooking to *all* object instances of the class.
* - `method` - If hooking to a single object instance.
* - Since 3.0.137 it may also be multiple methods to hook in CSV string or array.
* @param object|null|callable $toObject Specify one of the following:
* - Object instance to call `$toMethod` from (like `$this`).
* - Inline function (closure) if providing implemention inline.
@@ -746,7 +752,8 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* rather than $obj->method(). The default type is 'method'.
* - `priority` (int): A number determining the priority of a hook, where lower numbers are executed before
* higher numbers. The default priority is 100.
* @return string A special Hook ID that should be retained if you need to remove the hook later.
* @return string A special Hook ID (or CSV string of hook IDs) that should be retained if you need to remove the hook later.
* @see https://processwire.com/docs/modules/hooks/
*
*/
public function addHookAfter($method, $toObject, $toMethod = null, $options = array()) {
@@ -778,9 +785,10 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*
* #pw-group-hooks
*
* @param string $property Name of property you want to add, must not collide with existing property or method names:
* @param string|array $property Name of property you want to add, must not collide with existing property or method names:
* - `Class::property` to add the property to all instances of Class.
* - `property` if just adding to a single object instance.
* - Since 3.0.137 it may also be multiple properties to hook in CSV string or array.
* @param object|null|callable $toObject Specify one of the following:
* - Object instance to call `$toMethod` from (like `$this`).
* - Inline function (closure) if providing implemention inline.
@@ -790,7 +798,8 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* This argument can be sustituted as the 2nd argument when the 2nd argument isnt needed,
* or it can be the $options argument.
* @param array $options Options typically aren't used in this context, but see Wire::addHookBefore() $options if you'd like.
* @return string A special Hook ID that should be retained if you need to remove the hook later.
* @return string A special Hook ID (or CSV string of hook IDs) that should be retained if you need to remove the hook later.
* @see https://processwire.com/docs/modules/hooks/
*
*/
public function addHookProperty($property, $toObject, $toMethod = null, $options = array()) {
@@ -841,6 +850,7 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* @param string $method Name of method you want to add, must not collide with existing property or method names:
* - `Class::method` to add the method to all instances of Class.
* - `method` to just add to a single object instance.
* - Since 3.0.137 it may also be multiple methods to hook in CSV string or array.
* @param object|null|callable $toObject Specify one of the following:
* - Object instance to call `$toMethod` from (like `$this`).
* - Inline function (closure) if providing implemention inline.
@@ -850,8 +860,9 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
* This argument can be sustituted as the 2nd argument when the 2nd argument isnt needed,
* or it can be the $options argument.
* @param array $options Options typically aren't used in this context, but see Wire::addHookBefore() $options if you'd like.
* @return string A special Hook ID that should be retained if you need to remove the hook later.
* @return string A special Hook ID (or CSV string of hook IDs) that should be retained if you need to remove the hook later.
* @since 3.0.16 Added as an alias to addHook() for syntactic clarity, previous versions can use addHook() method with same arguments.
* @see https://processwire.com/docs/modules/hooks/
*
*/
public function addHookMethod($method, $toObject, $toMethod = null, $options = array()) {
@@ -882,7 +893,8 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable {
*
* #pw-group-hooks
*
* @param string|null $hookId ID of hook to remove (ID is returned by the addHook() methods)
* @param string|array|null $hookId ID of hook to remove (ID is returned by the addHook() methods)
* Since 3.0.137 it may also be an array or CSV string of hook IDs to remove.
* @return $this
*
*/

View File

@@ -265,9 +265,13 @@ class WireData extends Wire implements \IteratorAggregate, \ArrayAccess {
$keys = array();
}
if($from->wire($key) !== null) return null; // don't allow API vars to be retrieved this way
if($from instanceof WireData) $value = $from->get($key);
else if($from instanceof WireArray) $value = $from->getProperty($key);
else $value = $from->$key;
if($from instanceof WireData) {
$value = $from->get($key);
} else if($from instanceof WireArray) {
$value = $from->getProperty($key);
} else {
$value = $from->$key;
}
if(!count($keys)) return $value; // final value
if(is_object($value)) {
if(count($keys) > 1) {

View File

@@ -474,19 +474,26 @@ class WireHooks {
* $this->addHook($method, 'function_name'); or $this->addHook($method, 'function_name', $options);
*
* @param Wire $object
* @param string $method Method name to hook into, NOT including the three preceding underscores.
* @param string|array $method Method name to hook into, NOT including the three preceding underscores.
* May also be Class::Method for same result as using the fromClass option.
* May also be array OR CSV string of either of the above to add multiple (since 3.0.137).
* @param object|null|callable $toObject Object to call $toMethod from,
* Or null if $toMethod is a function outside of an object,
* Or function|callable if $toObject is not applicable or function is provided as a closure.
* @param string|array $toMethod Method from $toObject, or function name to call on a hook event, or $options array.
* @param array $options See $defaultHookOptions at the beginning of this class. Optional.
* @return string A special Hook ID that should be retained if you need to remove the hook later
* @return string A special Hook ID that should be retained if you need to remove the hook later.
* If the $method argument was a CSV string or array of multiple methods to hook, then CSV string of hook IDs
* will be returned, and the same CSV string can be used with removeHook() calls. (since 3.0.137).
* @throws WireException
*
*/
public function addHook(Wire $object, $method, $toObject, $toMethod = null, $options = array()) {
if(is_array($method) || strpos($method, ',') !== false) {
return $this->addHooks($object, $method, $toObject, $toMethod, $options);
}
if(is_array($toMethod)) {
// $options array specified as 3rd argument
if(count($options)) {
@@ -673,6 +680,77 @@ class WireHooks {
return $id;
}
/**
* Add a hooks to multiple methods at once
*
* This is the same as addHook() except that the $method argument is an array or CSV string of hook definitions.
* See the addHook() method for more detailed info on arguments.
*
* @param Wire $object
* @param array|string $methods Array of one or more strings hook definitions, or CSV string of hook definitions
* @param object|null|callable $toObject
* @param string|array|null $toMethod
* @param array $options
* @return string CSV string of hook IDs that were added
* @throws WireException
* @since 3.0.137
*
*/
protected function addHooks(Wire $object, $methods, $toObject, $toMethod = null, $options = array()) {
if(!is_array($methods)) {
// potentially multiple methods defined in a CSV string
// could also be a single method with CSV arguments
$str = (string) $methods;
$argSplit = '|';
// skip optional useless parenthesis in definition to avoid unnecessary iterations
if(strpos($str, '()') !== false) $str = str_replace('()', '', $str);
if(strpos($str, '(') === false) {
// If there is a parenthesis then it is multi-method definition without arguments
// Example: "Pages::saveReady, Pages::saved"
$methods = explode(',', $str);
} else {
// Single or multi-method definitions, at least one with arguments
// Isolate commas that are for arguments versus comments that separate multiple hook methods:
// Single method example: "Page(template=order)::changed(0:order_status, 1:name=pending)"
// Multi method example: "Page(template=order)::changed(0:order_status, 1:name=pending), Page::saved"
while(strpos($str, $argSplit) !== false) $argSplit .= '|';
$strs = explode('(', $str);
foreach($strs as $key => $val) {
if(strpos($val, ')') === false) continue;
list($a, $b) = explode(')', $val, 2);
if(strpos($a, ',') !== false) $a = str_replace(array(', ', ','), $argSplit, $a);
$strs[$key] = "$a)$b";
}
$str = implode('(', $strs);
$methods = explode(',', $str);
foreach($methods as $key => $method) {
if(strpos($method, $argSplit) === false) continue;
$methods[$key] = str_replace($argSplit, ', ', $method);
}
}
}
$result = array();
foreach($methods as $method) {
$method = trim($method);
$hookID = $this->addHook($object, $method, $toObject, $toMethod, $options);
$result[] = $hookID;
}
$result = implode(',', $result);
return $result;
}
/**
* Provides the implementation for calling hooks in ProcessWire
@@ -926,11 +1004,14 @@ class WireHooks {
* }
*
* @param Wire $object
* @param string|null $hookID
* @param string|array|null $hookID Can be single hook ID, array of hook IDs, or CSV string of hook IDs
* @return Wire
*
*/
public function removeHook(Wire $object, $hookID) {
if(is_array($hookID) || strpos($hookID, ',')) {
return $this->removeHooks($object, $hookID);
}
if(!empty($hookID) && strpos($hookID, ':')) {
list($hookClass, $priority, $method) = explode(':', $hookID);
if(empty($hookClass)) {
@@ -947,6 +1028,23 @@ class WireHooks {
return $object;
}
/**
* Given a hook ID or multiple hook IDs (in array or CSV string) remove the hooks
*
* @param Wire $object
* @param array|string $hookIDs
* @return Wire
* @since 3.0.137
*
*/
protected function removeHooks(Wire $object, $hookIDs) {
if(!is_array($hookIDs)) $hookIDs = explode(',', $hookIDs);
foreach($hookIDs as $hookID) {
$this->removeHook($object, $hookID);
}
return $object;
}
/**
* Return the "all local hooks" cache
*