-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Hook mechanism in order to trigger contents. #1120
- Loading branch information
Showing
4 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
<?php | ||
|
||
namespace luya; | ||
|
||
use yii\base\Object; | ||
use luya\base\HookEvent; | ||
|
||
/** | ||
* Simple Hooking mechanism. | ||
* | ||
* Attaching callables or object methods to the Hook mechnism in your controller, block or elsewhere. | ||
* | ||
* A simple string output behavior: | ||
* | ||
* ```php | ||
* Hook::on('fooBar', function() { | ||
* return 'Hello World'; | ||
* }); | ||
* ``` | ||
* | ||
* In order to trigger the hook output somewher in your layout file use: | ||
* | ||
* ```php | ||
* echo Hook::string('fooBar'); | ||
* ``` | ||
* | ||
* In order to generate iteration output. | ||
* | ||
* ```php | ||
* Hook::on('fooBarArray', function($hook) { | ||
* $hook[] = 'Hello'; | ||
* $hook[] = 'World'; | ||
* }); | ||
* ``` | ||
* | ||
* The array would print: | ||
* | ||
* ```php | ||
* $array = Hook::iterate('fooBarArray'); // Array ( 0 => 'Hello', 1 => 'World') | ||
* ``` | ||
* | ||
* @since 1.0.0 | ||
* @author Basil Suter <basil@nadar.io> | ||
*/ | ||
class Hook extends Object | ||
{ | ||
private static $_hooks = []; | ||
|
||
/** | ||
* Register a hook listener. | ||
* | ||
* Define the name of the type of listener. | ||
* | ||
* @param string $name The name of the hook. | ||
* @param callable|array $value An array with `[$object, 'method']` or a callable function `function($hook) {}`. | ||
* @param boolean $prepend Whether to prepend the item to the start or not, defaults to false. | ||
*/ | ||
public static function on($name, $value, $prepend = false) | ||
{ | ||
/// @TODO implement prepend | ||
static::$_hooks[$name][] = new HookEvent(['handler' => $value]); | ||
} | ||
|
||
/** | ||
* Trigger the hooks and returns the {{luya\base\HookEvent}} objects. | ||
* | ||
* @param string $name The hook name. | ||
* @return array An array with {{luya\base\HookEvent}} objects. | ||
*/ | ||
protected static function trigger($name) | ||
{ | ||
if (isset(static::$_hooks[$name])) { | ||
|
||
$events = []; | ||
|
||
foreach (static::$_hooks[$name] as $hookEvent) { | ||
|
||
if ($hookEvent->isHandled) { | ||
continue; | ||
} | ||
|
||
if (is_callable($hookEvent->handler)) { | ||
$hookEvent->output = call_user_func($hookEvent->handler, $hookEvent); | ||
} elseif (is_array($hookValue)) { | ||
$hookEvent->output = call_user_func_array($hookEvent->handler, [$hookEvent]); | ||
} | ||
|
||
$hookEvent->isHandled = true; | ||
|
||
if ($hookEvent->isValid) { | ||
$events[] = $hookEvent; | ||
} | ||
} | ||
|
||
return $events; | ||
} | ||
|
||
return []; | ||
} | ||
|
||
/** | ||
* Get the string output of the hooks. | ||
* | ||
* @param string $name The name of the hook to trigger. | ||
* @return string | ||
*/ | ||
public static function string($name) | ||
{ | ||
$buffer = []; | ||
foreach (self::trigger($name) as $hook) { | ||
$buffer[] = $hook->output; | ||
} | ||
|
||
return implode("", $buffer); | ||
} | ||
|
||
/** | ||
* Get the array output of iteration hooks. | ||
* | ||
* @param string $name The name of the hook to trigger. | ||
* @return array | ||
*/ | ||
public static function iterate($name) | ||
{ | ||
$buffer = []; | ||
foreach (self::trigger($name) as $hook) { | ||
$buffer = array_merge($buffer, $hook->getIterations()); | ||
} | ||
|
||
return $buffer; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?php | ||
|
||
namespace luya\base; | ||
|
||
use yii\base\Object; | ||
|
||
/** | ||
* HookEvent Object. | ||
* | ||
* Each Hook Event is represented and evaluated by this Object. See {{luya\Hook}}. | ||
* | ||
* @since 1.0.0 | ||
* @author Basil Suter <basil@nadar.io> | ||
*/ | ||
final class HookEvent extends Object implements \ArrayAccess | ||
{ | ||
/** | ||
* @var array|callable The executable handler to use when calling the hook event in order to generate the $output. | ||
*/ | ||
public $handler; | ||
|
||
/** | ||
* @var string The output which is generated when the $handler is called, this is used in order to return string outputs. | ||
*/ | ||
public $output; | ||
|
||
/** | ||
* @var boolean Whether the hook event is handled already or not. | ||
*/ | ||
public $isHandled = false; | ||
|
||
/** | ||
* @var boolean Whether the current event is valid or not, if set to false, this event will not be triggered on output. | ||
*/ | ||
public $isValid = true; | ||
|
||
private $_iterations = []; | ||
|
||
/** | ||
* Getter method for iteration. | ||
* | ||
* @return array | ||
*/ | ||
public function getIterations() | ||
{ | ||
return $this->_iterations; | ||
} | ||
|
||
/** | ||
* Add Iteration | ||
* @param mixed $value The value of the key | ||
* @param string $key The key of the array | ||
*/ | ||
public function iteration($value, $key = null) | ||
{ | ||
if (is_null($key)) { | ||
$this->_iterations[] = $value; | ||
} else { | ||
$this->_iterations[$key] = $value; | ||
} | ||
} | ||
|
||
public function offsetSet($offset, $value) | ||
{ | ||
$this->iteration($value, $offset); | ||
} | ||
|
||
public function offsetExists($offset) | ||
{ | ||
return isset($this->_iterations[$offset]); | ||
} | ||
|
||
public function offsetUnset($offset) | ||
{ | ||
unset($this->_iterations[$offset]); | ||
} | ||
|
||
public function offsetGet($offset) | ||
{ | ||
return isset($this->_iterations[$offset]) ? $this->_iterations[$offset] : null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?php | ||
|
||
namespace luyatests\core; | ||
|
||
use luyatests\LuyaWebTestCase; | ||
use luya\Hook; | ||
|
||
class TestClass | ||
{ | ||
public function helloHook($hook) | ||
{ | ||
$hook->iteration('hello'); | ||
} | ||
|
||
public function worldHook($hook) | ||
{ | ||
$hook->iteration('world'); | ||
} | ||
} | ||
|
||
class HookTest extends LuyaWebTestCase | ||
{ | ||
public function testStringHooks() | ||
{ | ||
Hook::on('fooBar', function($hook) { | ||
return 'Hello World'; | ||
}); | ||
|
||
$this->assertSame('Hello World', Hook::string('fooBar')); | ||
|
||
Hook::on('fooBar', function($hook) { | ||
return 'Another'; | ||
}); | ||
Hook::on('fooBar', function($hook) { | ||
return 'Test'; | ||
}); | ||
|
||
$this->assertSame('AnotherTest', Hook::string('fooBar')); | ||
} | ||
|
||
public function testIterationHooks() | ||
{ | ||
Hook::on('fooBar', function($hook) { | ||
$hook->iteration('hello'); | ||
$hook->iteration('world'); | ||
}); | ||
|
||
$this->assertSame(['hello', 'world'], Hook::iterate('fooBar')); | ||
|
||
|
||
Hook::on('testIterator', function($hook) { | ||
$hook->iteration('hello'); | ||
$hook->iteration('world'); | ||
}); | ||
|
||
Hook::on('testIterator', function($hook) { | ||
$hook->iteration('another'); | ||
$hook->iteration('world'); | ||
}); | ||
|
||
$this->assertSame(['hello', 'world', 'another', 'world'], Hook::iterate('testIterator')); | ||
} | ||
|
||
public function testIterationWithObjectHooks() | ||
{ | ||
$obj = new TestClass(); | ||
Hook::on('fooBar', [$obj, 'helloHook']); | ||
Hook::on('fooBar', [$obj, 'worldHook']); | ||
|
||
$this->assertSame(['hello', 'world'], Hook::iterate('fooBar')); | ||
} | ||
|
||
public function testIterationArrayAccessHook() | ||
{ | ||
Hook::on('fooBar', function($hook) { | ||
$hook[] = 'Hello'; | ||
$hook[] = 'World'; | ||
|
||
$hook['foo'] = 'Bar'; | ||
$hook['unfoo'] = 'Unfoo'; | ||
|
||
if ($hook['unfoo']) { | ||
unset($hook['unfoo']); | ||
} | ||
}); | ||
|
||
$this->assertSame(['Hello', 'World', 'foo' => 'Bar'], Hook::iterate('fooBar')); | ||
} | ||
|
||
public function testIterationOverrideKey() | ||
{ | ||
Hook::on('fooBar', function($hook) { | ||
$hook['test'] = 'value1'; | ||
$hook['test'] = 'value2'; | ||
}); | ||
|
||
Hook::on('fooBar', function($hook) { | ||
$hook['test'] = 'value3'; | ||
$hook['test'] = 'value4'; | ||
}); | ||
|
||
$this->assertSame(['test' => 'value4'], Hook::iterate('fooBar')); | ||
} | ||
} |