seothemes/hooks

dev-main 2022-09-14 04:32 UTC

This package is auto-updated.

Last update: 2024-09-14 08:41:59 UTC


README

管理WordPress动作和过滤器钩子的替代语法。

包含两种独立的方法,一种仅使用函数,另一种使用OOP类。该软件包的主要目的是仅使用函数提供此功能,因为已经有很多管理钩子的OOP方法。OOP方法可以作为比较,或在不使用OOP方法的情况下使用。

功能

可移除匿名回调

允许以后删除使用钩子容器添加的匿名函数

// Add hook with anonymous arrow function:
add_hook( 'init', 'print_hello_world', function() {
    print 'Hello world!';
} );

// Remove hook by alias:
remove_hook( 'init', 'print_hello_world' );

钩子API语法

允许PHP钩子更紧密地匹配 @wordpress/hooks JS API 语法

addFilter( 
    'body_class',
    __NAMESPACE__ . '\\remove_home_body_class',
    function( array $classes ) : array {
        return array_diff( $classes, [ 'home' ] );
    }
);

箭头函数

允许使用可移除的箭头函数作为回调

add_hook( 'init', 'print_hello_world', fn() => print 'Hello world!' );
remove_hook( 'init', 'print_hello_world' );

安装

composer require seothemes/hooks

然后在您的项目中加载composer

require_once __DIR__ . '/vendor/autoload.php';

仅函数的方法可以在 hooks.php 文件中找到。

OOP方法可以在 src 目录中找到。

两者都是独立的,可以单独使用。

所有类和函数都是可插拔的,以避免命名冲突。

示例

添加钩子的常用方法(不使用类时)

function remove_home_body_class( array $classes ): array {
    return array_diff( $classes, [ 'home' ] );
}
add_filter( 'body_class', 'remove_home_body_class', 10, 1 );

在上面的示例中,函数名 remove_home_body_class 必须输入两次。IDE可以提供帮助,但仍然会导致代码重复。此外,使用此方法添加的匿名函数以后无法删除。

使用钩子工具,上面的示例可以重写如下

多行

匹配 @wordpress/hooks JS API 语法

addFilter( 
    'body_class',
    'remove_home_body_class',
    fn( $classes ) => array_diff( $classes, [ 'home' ] ),
    10,
    1
);

添加带匿名函数的动作钩子

addAction(
    'init',
    'my_callback_alias',
    function() {
        echo 'Hello World!';
    },
    10,
    1
);

一行:

add_hook( 'body_class', 'remove_home_body_class', fn( $classes ) => array_diff( $classes, [ 'home' ] ), 10, 1 );

现在函数名已经减少到一,匿名函数可以通过指定的别名以后删除。

工作原理

钩子对象存储在 hook_container 函数中的静态变量 $hooks 中。钩子可以在任何时间添加和从容器中删除,就像常规钩子一样。

add_hook 函数接受与 add_actionadd_filter 相同的参数,增加“别名”作为第二个参数。

string  $hook_name     Hook name, e.g `init`
string  $alias         Function alias. Used for hook removal.
Closure $callback      Actual closure to be called.
int     $priority      Hook priority.
int     $accepted_args Accepted number of arguments.

快速设置

使用composer安装到您的自定义插件或主题,或者简单地将 hooks.php 文件中的函数复制粘贴到您的项目中。

composer require seothemes/hooks

添加钩子

安装后,添加以下示例进行测试

namespace Company\Project;

require_once __DIR__ . '/vendor/autoload.php'; 

// Add 'test' class to body.
add_hook( 'body_class', 'add_test_class', fn( $classes ) => [ ...$classes, 'test' ] );

var_dump( hook_container() ); // Should return an array of hook objects.

可以使用提供的任何实用函数添加钩子

add_hook( 'body_class', 'remove_home_body_class', fn($classes) => $classes );
addFilter( 'body_class', 'remove_home_body_class', fn($classes) => $classes );
addAction( 'body_class', 'remove_home_body_class', fn($classes) => $classes );

删除钩子

“别名”参数为使用钩子系统注册的匿名函数添加一个ID,允许在以后阶段删除它们。

使用 add_hook 函数添加的钩子只能使用 remove_hook 函数删除。

remove_hook 函数从WordPress注销回调,并从容器中删除钩子。它接受与 remove_actionremove_filter 相同的参数

remove_hook( 'body_class', 'remove_home_body_class' );

提供了两个用于删除钩子的包装函数(它们都做同样的事情)

removeFilter( 'body_class', 'remove_home_body_class' );
removeAction( 'body_class', 'remove_home_body_class' );

OOP

此软件包中已包括另一个示例作为OOP的替代方案。安装此软件包时,将自动加载所有类。所有类(和函数)都是可插拔的,以避免命名冲突。

Factory类提供对单个Hooks实例的访问,在您的代码中添加以下行以获取

use SEOThemes\Hooks\Factory;
use SEOThemes\Hooks\Hooks;
use SEOThemes\Hooks\Container;

$hooks = ( new Factory() )->instance( new Hooks( new Container() ) );

要使用 Hooks 类,只需将以下代码添加到您的代码中

use SEOThemes\Hooks\Factory;
use SEOThemes\Hooks\Hooks;
use SEOThemes\Hooks\Container;

$hooks = ( new Factory() )->instance( new Hooks( new Container() ) );

$hooks->add( 'body_class', 'add_body_class', function ( $classes ) {
    $classes[] = 'test';

    return $classes;
} );

// To remove the hook uncomment this line.
// $hooks->remove( 'body_class', 'add_body_class' );

自动前缀别名

默认情况下,add_hook 函数不添加任何前缀。为了保持代码整洁,我建议创建一个命名空间 const 以缩短别名

namespace Company\Project;

const NS = __NAMESPACE__ . '\\';

add_hook( 'body_class', NS . 'remove_test_body_class', function ( $classes ) {
    return array_diff( $classes, [ 'test' ] );
} );

另一个选项是创建自己的包装函数,该函数将命名空间添加到每个别名前。这也需要一个具有相同别名的自定义移除函数

namespace Company\Project;

use Closure;

function addNamespacedHook( 
    string $hook_name, 
    string $alias, 
    Closure $callback, 
    int $priority = 10, 
    int $accepted_args = 1 
    ): bool {
    return add_hook( 
        $hook_name, 
        __NAMESPACE__ . '\\' . $alias, 
        $callback, 
        $priority, 
        $accepted_args 
    );
}

function removeNamespacedHook( 
    string $hook_name, 
    string $alias, 
    int $priority = 10 
    ): bool {
    return remove_hook( 
        $hook_name, 
        __NAMESPACE__ . '\\' . $alias, 
        $priority 
    );
}

如果使用面向对象编程,Container 类接受一个可选参数,可以自动将容器内所有别名的前缀设置为给定的字符串

use SEOThemes\Hooks\Factory;
use SEOThemes\Hooks\Hooks;
use SEOThemes\Hooks\Container;

// Inject dependencies.
$container = new Container( __NAMESPACE__ . '\\' );
$hooks     = new Hooks( $container );
$factory   = new Factory();
$instance  = $factory->instance( $hooks );

$instance->add('init', 'my_function', fn() => print 'Hello World!' );
$instance->remove('init', 'my_function' );

// Alternative short syntax.
( new Factory() )->instance( new Hooks( new Container( __NAMESPACE__ . '\\' ) ) )
->add( 'init', 'new_function', fn() => print 'Hello World!' );

容器结构

钩子容器以以下结构存储钩子,以允许有多个钩子和优先级

$hook_container = [
    'hook_name' => [
        'alias' => [
            'priority' =>  '', // Closure, e.g. fn() => print 'Hello World!'.
        ],
    ],
];

// A real example of var_dumping the hook container contents would look something like this:
$hook_container = [
    'init' => [
        'print_hello_world' => [
            10 => fn() => print 'Hello World!',
            11 => fn() => print 'Hello World, again!',
        ],
        'print_something_else' => [
            10 => fn() => print 'Something else!',
        ],
    ],
    'body_class' => [
        'add_test_body_class' => [
            10 => fn( $classes ) => [ ...$classes, 'test' ],
        ],
    ],
];

测试和贡献

该项目是实验性的,未经进一步测试,不打算用于生产。它旨在作为示例,探索为使用主要是函数式编程的较小项目编写 WordPress 钩子的更灵活和最小化方式。

欢迎并鼓励所有贡献。可能有我还没有想到的更好的实现方式,所以请随意提交拉取请求或打开一个问题。