jrbarnard/hookable

一个使任何类可挂载的特质

v1.0.3 2017-02-25 22:46 UTC

This package is auto-updated.

Last update: 2024-09-10 11:02:42 UTC


README

Build Status StyleCI

Hookable 是一个特质,任何 PHP 类都可以使用,它的目的是提供创建可挂载 '点' 的简单 API,并允许您在使用类外部或从扩展的子类中 '挂载' 到这些点。

这样做是为了能够在代码执行期间快速添加类似于事件的功能,同时提供非常灵活的 API。

例如

namespace App;

use JRBarnard\Hookable\Hookable;

class ToBeHooked
{
    use Hookable;

    public function methodToBeHooked()
    {
        $data = ['item', 'another_item'];

        $someOtherVariable = 'Hello';

        // We run a hook by key and pass through some data
        $this->runHook('hook_key', $data, $someOtherVariable);
        
        // Because we pass by reference, this variable may have been changed
        return $someOtherVariable;
    }
}

$toBeHooked = new ToBeHooked();

// Before we have registered the hook
$return = $toBeHooked->methodToBeHooked();
// $return === 'Hello';

// Registering a hook on the key is as simple as specifying the key and the relevant callback.
$toBeHooked->registerHook('hook_key', function($data, &$someOtherVariable){
    if ($someOtherVariable === 'Hello') {
        $someOtherVariable = 'world';        
    }
});

// After registering the hook
$return = $toBeHooked->methodToBeHooked();
// $return === 'world';

内容

  1. 要求
  2. 安装
  3. 使用
  4. 贡献
  5. 许可

要求

Hookable 的最低 PHP 版本要求是 5.6,这是因为它使用了 可变参数函数。除此之外,不应有任何其他要求,如果您发现有任何要求,请记录为工单/提交拉取请求。

目前不支持 HHVM / Hack。

安装

Composer 是推荐的安装方法

composer require jrbarnard/hookable

但是,您也可以下载此仓库,解压缩它并将它包含到您的项目中。

使用

注册和运行钩子

此特质的主要用例之一是当您有一个非常通用的抽象/父类被广泛扩展,并且有时您想根据通用方法覆盖/运行操作,而不需要覆盖整个方法。

以下是一个示例

use JRBarnard\Hookable\Hookable;

abstract class SomeClass
{
    use Hookable;

    public function store(array $data) 
    {
        $model = new Model();
        $model->fill($data);
        $result = $model->save($result);
        
        $this->runHook('after_store', $data, $model);
        
        return $result;
    }
}
class ChildClass extends SomeClass
{
    public function __construct()
    {
        $this->registerHook('after_store', function(array $data, Model $model){
            if ($model->isSpecial()) {
                // Do some sort of specific action for this child.
                // Perhaps send an email, log something etc.
            }
        });
        
        // Same hook, will still run, but this will run after, unless we specify priorities.
        $this->registerHook('after_store', function(array $data, Model $model){
            if ($model->isSpecial()) {
                // Do some sort of other action for this child.
            }
        });
    }
}

您也可以在外部对象上注册钩子

class SomeClass
{
    use Hookable;
}

$someObject = new SomeClass();
$someObject->registerHook('some_hook', function(){ // Do something })

// You can also run hooks externally too (useful for testing, not sure if useful elsewhere)
$someObject->runHook('some_hook');

您不需要直接传递闭包,您也可以使用数组语法引用特定的函数或类方法

// Standalone function
function someFunction(){
    // Do something
}

// Within a hookable classes constructor
$this->registerHook('hook_key', ['someFunction']);

// Class method
class SomeClass {
    use Hookable;
    
    public function someMethod(){
        // Do something
    }
}
$someObject = new SomeClass();

// Within a hookable classes constructor
$this->registerHook('hook_key', ['someMethod', $someObject]);

运行钩子时传递的所有参数默认都是通过引用传递的,这允许我们(如果我们想更改传递的参数而不是对象)

class SomeClass {
    use Hookable;
    
    public function someMethod($array) {
        $this->runHook('hook_key', $array);
        
        return $array;
    }
}
$someObject = new SomeClass();

// Calling someMethod now will return the standard array
$array = $someObject->someMethod(['test']);
// $array will equal ['test']

$someObject->registerHook('hook_key', function(&$array){
    $array = [];
});

// Calling someMethod now will return the altered array as we have a hook
$array = $someObject->someMethod(['test']);
// $array will equal [];

您可以使用 runHook 传递任意数量的参数,它们将被解包并在您用 registerHook 指定的回调中作为参数可用

优先级

Hookable 还内置了优先级,当您注册一个钩子时,您可以在回调之后传递第三个参数,这将作为回调的优先级,这定义了它在调用顺序中的位置,数字越高,在调用顺序中运行得越晚

class SomeClass {
    use Hookable;
}
$someObject = new SomeClass();

$someObject->registerHook('test_priority', function(){ echo 'this will appear last'; }, 99);
$someObject->registerHook('test_priority', function(){ echo 'this will appear first'; }, 1);
$someObject->registerHook('test_priority', function(){ echo 'this will appear in the middle'; }, 50);

// We would expect the output:
// this will appear first
// this will appear in the middle
// this will appear last

如果您设置了两个具有相同键和相同优先级的钩子,它将使用它们注册的顺序,不会覆盖已设置的钩子。

检查和删除钩子

您可以通过在相关的可挂载对象上调用 hasHook 来检查是否存在钩子

class SomeClass {
    use Hookable;
}
$someObject = new SomeClass();

// Will be false
$someObject->hasHook('test_has_hook');

$someObject->registerHook('test_has_hook', function(){ // Do something });
// Will be true
$someObject->hasHook('test_has_hook');

如果您想删除对象上的钩子,可以使用 clearHooks 方法

class SomeClass {
    use Hookable;
}
$someObject = new SomeClass();
$someObject->registerHook('hook_one', function(){ // Do something });
$someObject->registerHook('hook_two', function(){ // Do something });

// This will remove all hooks registered to the name hook_one
$someObject->clearHooks('hook_one');

// This will remove all hooks from the object, under all names
$someObject->clearHooks();

贡献

请参阅贡献文件

许可

MIT 许可