WordPress源码深度解析之:`WordPress`的`action/filter`机制:`do_action()`和`apply_filters()`的内部工作原理。

各位观众老爷们,大家好!我是你们的老朋友,今天咱们来聊聊WordPress的“魔法”——action/filter机制。这玩意儿就像WordPress的神经系统,让各种插件、主题之间能够自由地“对话”,从而实现各种酷炫的功能。

一、开场白:WordPress的“神经系统”

话说,WordPress之所以能成为如此强大且灵活的CMS,很大程度上要归功于它那精妙的actionfilter机制。想象一下,如果没有这套机制,所有的代码都得硬编码到WordPress核心文件里,那画面太美我不敢看!

actionfilter,就像WordPress的神经末梢,允许开发者在特定的“神经节点”(也就是代码中的特定位置)插入自己的代码,从而改变WordPress的行为或输出。

二、Action:事件驱动的“广播站”

action,顾名思义,就是“动作”。它就像一个广播站,当某个事件发生时,WordPress会向所有订阅了这个事件的“听众”(也就是注册了相应action的回调函数)发送信号,让它们执行各自的任务。

  1. do_action():发出“广播”

do_action()函数就是那个广播员,它负责发出“广播”。它的基本语法如下:

do_action( string $hook_name, mixed $arg = '' );
  • $hook_name:广播的频道名称,也就是action的名称。
  • $arg:传递给“听众”的参数,可以是一个值,也可以是一个数组。

举个例子,WordPress在加载主题的functions.php文件后,会执行一个名为after_setup_themeaction

do_action( 'after_setup_theme' );

这意味着,所有注册了after_setup_theme这个action的回调函数都会被执行。

  1. add_action():成为“听众”

add_action()函数就是用来注册action的回调函数的,也就是让自己成为“听众”。它的基本语法如下:

add_action( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );
  • $hook_name:要订阅的“广播频道”名称,也就是action的名称。
  • $callback:回调函数,也就是当接收到“广播”后要执行的函数。
  • $priority:优先级,数字越小,优先级越高,越早执行。默认值为10。
  • $accepted_args:回调函数接收的参数个数。默认值为1。

举个例子,假设我们想在主题加载后,输出一段文字:

function my_custom_function() {
    echo '<p>主题加载完毕!</p>';
}

add_action( 'after_setup_theme', 'my_custom_function' );

这段代码的意思是:当after_setup_theme这个action被触发时,执行my_custom_function函数,输出一段文字。

  1. Action的执行流程

简单来说,action的执行流程如下:

  • WordPress在代码中遇到do_action()函数。
  • do_action()函数根据action的名称,找到所有注册了该action的回调函数。
  • do_action()函数按照优先级顺序,依次执行这些回调函数,并将传递的参数传递给回调函数。

三、Filter:内容修改的“变形金刚”

filter,顾名思义,就是“过滤器”。它就像一个变形金刚,允许开发者在特定的数据流经过时,对数据进行修改,从而改变WordPress的输出。

  1. apply_filters():数据“流经”

apply_filters()函数就是那个数据流动的“管道”,它负责让数据“流经”各个“过滤器”。它的基本语法如下:

apply_filters( string $hook_name, mixed $value, mixed ...$args );
  • $hook_name:过滤器的名称。
  • $value:要被过滤的值。
  • ...$args:传递给过滤器的其他参数,可以有多个。

举个例子,WordPress在显示文章内容时,会使用一个名为the_contentfilter

$content = apply_filters( 'the_content', $content );

这意味着,$content变量的值会被传递给所有注册了the_content这个filter的回调函数,经过它们的修改后,最终的值会赋给$content变量。

  1. add_filter():成为“变形金刚”

add_filter()函数就是用来注册filter的回调函数的,也就是让自己成为“变形金刚”。它的基本语法如下:

add_filter( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );
  • $hook_name:要过滤的名称。
  • $callback:回调函数,也就是当数据流经过时要执行的函数。这个函数必须返回修改后的值。
  • $priority:优先级,数字越小,优先级越高,越早执行。默认值为10。
  • $accepted_args:回调函数接收的参数个数。默认值为1。

举个例子,假设我们想在文章内容中添加一段文字:

function my_custom_filter( $content ) {
    return $content . '<p>这是一段自定义的文字。</p>';
}

add_filter( 'the_content', 'my_custom_filter' );

这段代码的意思是:当the_content这个filter被触发时,执行my_custom_filter函数,将文章内容加上一段文字后返回。

  1. Filter的执行流程

简单来说,filter的执行流程如下:

  • WordPress在代码中遇到apply_filters()函数。
  • apply_filters()函数根据filter的名称,找到所有注册了该filter的回调函数。
  • apply_filters()函数按照优先级顺序,依次执行这些回调函数,并将要过滤的值和传递的参数传递给回调函数。
  • 每个回调函数必须返回修改后的值,这个值会被传递给下一个回调函数,直到所有回调函数都执行完毕。
  • apply_filters()函数返回最终被过滤后的值。

四、Action和Filter的区别

特性 Action Filter
作用 触发事件,执行一系列操作 修改数据,改变输出
返回值 无返回值(void) 必须返回修改后的值
使用场景 在特定事件发生后执行某些操作,例如发送邮件等 修改文章内容、标题、摘要等
核心函数 do_action()add_action() apply_filters()add_filter()
形象比喻 广播站,通知听众执行任务 变形金刚,改变数据的形态

五、深入解析:WordPress源码中的Action/Filter实现

WordPress的action/filter机制的核心代码位于wp-includes/plugin.php文件中。咱们来扒一扒它的源码,看看它是如何实现的。

  1. $wp_filter 全局变量

首先,WordPress使用一个全局变量$wp_filter来存储所有的actionfilter。这是一个多维数组,它的结构如下:

$wp_filter = array(
    'hook_name' => array(
        'priority' => array(
            'callback_id' => array(
                'function' => 'callback_function',
                'accepted_args' => 'number_of_arguments'
            )
        )
    )
);
  • hook_nameactionfilter的名称。
  • priority:优先级。
  • callback_id:回调函数的唯一标识符。
  • function:回调函数的名称。
  • accepted_args:回调函数接收的参数个数。
  1. add_action()add_filter() 的内部实现

add_action()add_filter()函数本质上是同一个函数,它们都调用了add_filter()函数。区别在于,add_action()函数会将accepted_args参数设置为0,而add_filter()函数则保持默认值1。

add_filter()函数的内部实现如下:

function add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) {
    global $wp_filter, $wp_actions, $wp_current_filter;

    $idx = _wp_filter_build_unique_id( $hook_name, $callback, $priority );

    $wp_filter[$hook_name][$priority][$idx] = array(
        'function' => $callback,
        'accepted_args' => $accepted_args
    );

    ksort( $wp_filter[$hook_name], SORT_NUMERIC );

    return true;
}
  • _wp_filter_build_unique_id() 函数用于生成回调函数的唯一标识符。
  • ksort() 函数用于按照优先级对回调函数进行排序。
  1. do_action() 的内部实现

do_action()函数的内部实现如下:

function do_action( $hook_name, ...$args ) {
    global $wp_filter, $wp_actions, $wp_current_filter;

    if ( ! isset( $wp_actions[ $hook_name ] ) ) {
        $wp_actions[ $hook_name ] = 1;
    } else {
        ++$wp_actions[ $hook_name ];
    }

    if ( ! isset( $wp_filter[ $hook_name ] ) ) {
        return;
    }

    $wp_current_filter[] = $hook_name;

    $args = func_get_args();
    array_shift( $args );

    $wp_filter = apply_filters( 'wp_filter', $wp_filter );

    if ( isset( $wp_filter[ $hook_name ] ) ) {
        _wp_call_all_hook( $wp_filter[ $hook_name ], $args );
    }

    array_pop( $wp_current_filter );
}
  • $wp_actions 数组用于记录action被触发的次数。
  • $wp_current_filter 数组用于记录当前正在执行的actionfilter
  • _wp_call_all_hook() 函数用于执行所有注册了该action的回调函数。
  1. apply_filters() 的内部实现

apply_filters()函数的内部实现如下:

function apply_filters( $hook_name, $value, ...$args ) {
    global $wp_filter, $wp_actions, $wp_current_filter;

    if ( ! isset( $wp_filter[ $hook_name ] ) ) {
        return $value;
    }

    $wp_current_filter[] = $hook_name;

    $args = func_get_args();
    array_shift( $args );

    $wp_filter = apply_filters( 'wp_filter', $wp_filter );

    if ( isset( $wp_filter[ $hook_name ] ) ) {
        $value = _wp_call_all_hook( $wp_filter[ $hook_name ], $args );
    }

    array_pop( $wp_current_filter );

    return $value;
}
  • 如果没有任何回调函数注册了该filter,则直接返回原始值。
  • _wp_call_all_hook() 函数用于执行所有注册了该filter的回调函数,并将返回值传递给下一个回调函数。
  1. _wp_call_all_hook() 的内部实现

_wp_call_all_hook() 函数是action/filter机制的核心函数,它负责执行所有注册的回调函数。它的内部实现如下:

function _wp_call_all_hook( $callbacks, $args ) {
    global $wp_filter;

    reset( $callbacks );

    foreach ( $callbacks as $priority => $functions ) {
        foreach ( $functions as $function ) {
            $all_args = $args;
            switch ( $function['accepted_args'] ) {
                case 0:
                    $value = call_user_func( $function['function'] );
                    break;
                case 1:
                    $value = call_user_func( $function['function'], $args[0] );
                    break;
                case 2:
                    $value = call_user_func( $function['function'], $args[0], $args[1] );
                    break;
                default:
                    $value = call_user_func_array( $function['function'], array_slice( $all_args, 0, $function['accepted_args'] ) );
                    break;
            }
        }
    }

    return $args[0];
}
  • reset() 函数用于重置数组的内部指针。
  • call_user_func() 函数用于调用回调函数,并传递参数。
  • call_user_func_array() 函数用于调用回调函数,并传递一个数组作为参数。

六、最佳实践:Action/Filter的正确使用姿势

  1. 命名规范:起个好名字很重要

actionfilter的命名应该具有描述性,能够清晰地表达其作用。例如,the_content表示文章内容,wp_head表示HTML头部。

  1. 优先级:掌握执行顺序

合理设置优先级,确保回调函数按照正确的顺序执行。通常情况下,默认值10就足够了。如果需要提前执行,可以设置更小的优先级;如果需要延后执行,可以设置更大的优先级。

  1. 参数个数:明确需要接收的参数

回调函数应该只接收需要的参数,避免不必要的性能开销。如果不需要任何参数,可以将accepted_args设置为0。

  1. 返回值:Filter必须返回修改后的值

filter的回调函数必须返回修改后的值,否则会导致数据丢失。

  1. 避免过度使用:适可而止

action/filter机制虽然强大,但也应该避免过度使用。过多的actionfilter会导致代码难以维护,降低性能。

七、总结:Action/Filter是WordPress的灵魂

action/filter机制是WordPress的灵魂,它赋予了WordPress强大的扩展性和灵活性。掌握action/filter机制,你就能轻松地定制WordPress,实现各种酷炫的功能。

好了,今天的讲座就到这里。希望大家能够掌握action/filter机制,成为WordPress的高手!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注