嘿,大家好!欢迎参加今天的WordPress源码探秘讲座,我是你们的向导,今天咱们一起扒一扒 add_action()
和 add_filter()
这两个WordPress的核心函数,重点看看它们是怎么处理优先级和参数数量的。放心,保证不枯燥,咱们用最接地气的方式来搞懂它们。
开场白:WordPress的“胶水”
如果把WordPress比作一个乐高积木城堡,那么add_action()
和add_filter()
就是连接这些积木的“胶水”。它们允许你自定义WordPress的行为,而无需直接修改核心代码,这是WordPress扩展性的关键。
add_action()
和 add_filter()
:双胞胎兄弟
虽然名字不一样,但add_action()
和add_filter()
本质上非常相似,它们都是用来注册钩子(Hook)的。钩子是WordPress在代码执行流程中预留的一些“插槽”,你可以在这些插槽里插入自己的代码。
add_action()
:用于注册动作钩子,当WordPress执行到这个钩子时,会执行你注册的函数。add_filter()
:用于注册过滤器钩子,当WordPress执行到这个钩子时,会先执行你注册的函数,然后将函数的返回值传递给下一个函数或最终使用。
源码剖析:add_action()
和 add_filter()
的骨架
让我们先看看add_action()
的源码(wp-includes/plugin.php
):
function add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
return add_filter( $tag, $function_to_add, $priority, $accepted_args );
}
看到没?add_action()
实际上只是 add_filter()
的一个别名,它们共享相同的底层机制。这说明它们在实现上是高度一致的。 接下来,我们重点分析 add_filter()
的内部运作,因为 add_action()
也是通过它来实现的。
add_filter()
函数的定义如下:
function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
global $wp_filter, $merged_filters, $wp_current_filter;
$idx = _wp_filter_build_unique_id( $tag, $function_to_add, $priority );
$wp_filter[ $tag ][ $priority ][ $idx ] = array(
'function' => $function_to_add,
'accepted_args' => $accepted_args
);
unset( $merged_filters[ $tag ] );
if ( doing_filter( $tag ) ) {
global $wp_actions;
if ( ! isset( $wp_actions[ $tag ] ) ) {
$wp_actions[ $tag ] = 1;
}
++$wp_actions[ $tag ];
}
return true;
}
参数解读:四位一体
add_filter()
接收四个参数:
$tag
:钩子的名称,也就是你想要插入代码的“插槽”的名字。例如,'the_content'
是一个常用的钩子,用于修改文章内容。$function_to_add
:你想要执行的函数名或闭包。这是你自定义逻辑的载体。$priority
:优先级,数值越小,优先级越高,越早执行。默认值为 10。$accepted_args
:函数接收的参数数量。WordPress 会根据这个值传递相应数量的参数给你的函数。默认值为 1。
优先级:先来后到有讲究
$priority
参数决定了钩子函数执行的顺序。WordPress 会按照优先级从小到大依次执行钩子函数。这意味着优先级数值小的函数会先执行。
想象一下排队买奶茶,优先级就像你的会员等级,等级高的可以插队先买。
让我们看一个例子:
add_filter( 'the_content', 'my_filter_one', 5 ); // 优先级 5
add_filter( 'the_content', 'my_filter_two', 10 ); // 优先级 10
add_filter( 'the_content', 'my_filter_three', 15 ); // 优先级 15
function my_filter_one( $content ) {
return '<h1>Filter One</h1>' . $content;
}
function my_filter_two( $content ) {
return '<h2>Filter Two</h2>' . $content;
}
function my_filter_three( $content ) {
return '<h3>Filter Three</h3>' . $content;
}
在这个例子中,my_filter_one
会最先执行,然后是 my_filter_two
,最后是 my_filter_three
。最终的文章内容会是:
<h1>Filter One</h1><h2>Filter Two</h2><h3>Filter Three</h3>[原始文章内容]
源码解析:$wp_filter
全局变量
add_filter()
的核心在于操作全局变量 $wp_filter
。这是一个多维数组,用于存储所有注册的钩子函数。
- 第一层键:钩子的名称(
$tag
)。 - 第二层键:优先级(
$priority
)。 - 第三层键:钩子函数的唯一ID,由
_wp_filter_build_unique_id()
函数生成,确保同一个函数不会被重复注册。
每个钩子函数的信息都以数组的形式存储在 $wp_filter
中,包括:
'function'
:要执行的函数名或闭包。'accepted_args'
:函数接收的参数数量。
用表格的形式总结一下 $wp_filter
的结构:
层次 | 键 | 值 |
---|---|---|
第一层 | 钩子名称($tag ) |
数组,包含所有与该钩子相关的优先级层 |
第二层 | 优先级($priority ) |
数组,包含所有与该优先级相关的钩子函数 |
第三层 | 钩子函数唯一ID(_wp_filter_build_unique_id() 生成) |
数组,包含钩子函数的详细信息('function' 和 'accepted_args' ) |
_wp_filter_build_unique_id()
:身份认证官
这个函数的作用是为每个钩子函数生成一个唯一的ID,防止重复注册。它的源码如下:
function _wp_filter_build_unique_id( $tag, $function, $priority ) {
static $filter_id_count = 0;
if ( is_string( $function ) ) {
return $function;
}
if ( is_object( $function ) ) {
// Closures are currently implemented as objects
$function = array( $function, '' );
} else {
$function = (array) $function;
}
if ( is_object( $function[0] ) ) {
return spl_object_hash( $function[0] ) . $function[1];
} elseif ( is_string( $function[0] ) ) {
return $function[0] . '::' . $function[1];
}
}
这个函数会根据函数类型(字符串、对象、数组)生成不同的ID。对于闭包函数,它会使用 spl_object_hash()
函数生成一个唯一的哈希值。
参数数量:量体裁衣
$accepted_args
参数告诉 WordPress 你的函数需要接收多少个参数。WordPress 会根据这个值,从钩子函数传递过来的参数中,选择相应数量的参数传递给你的函数。
例如,'the_content'
钩子默认传递一个参数(文章内容)。如果你想接收这个参数,你需要将 $accepted_args
设置为 1。如果你想接收更多参数,你需要根据实际情况设置 $accepted_args
的值。
让我们看一个例子:
add_filter( 'comment_text', 'my_comment_filter', 10, 2 );
function my_comment_filter( $comment_text, $comment ) {
// $comment_text: 评论内容
// $comment: 评论对象
return '<div>' . $comment_text . '</div>';
}
在这个例子中,my_comment_filter
函数接收两个参数:评论内容($comment_text
)和评论对象($comment
)。我们将 $accepted_args
设置为 2,告诉 WordPress 传递这两个参数给我们的函数。
源码解析:apply_filters()
和 do_action()
的调用
注册钩子函数只是第一步,关键在于 WordPress 在何时、以何种方式调用这些函数。这就要涉及到 apply_filters()
和 do_action()
函数。
apply_filters()
:用于执行过滤器钩子,并返回经过所有钩子函数处理后的值。do_action()
:用于执行动作钩子,不返回值。
让我们看看 apply_filters()
的简化版源码(wp-includes/plugin.php
):
function apply_filters( $tag, $value ) {
global $wp_filter, $merged_filters, $wp_current_filter;
$args = func_get_args();
$hook_name = array_shift( $args );
$value = array_shift( $args );
if ( ! isset( $wp_filter[ $hook_name ] ) ) {
return $value;
}
if ( ! isset( $merged_filters[ $hook_name ] ) ) {
ksort( $wp_filter[ $hook_name ] );
$merged_filters[ $hook_name ] = true;
}
reset( $wp_filter[ $hook_name ] );
do {
foreach ( (array) current( $wp_filter[ $hook_name ] ) as $the_ ) {
if ( ! is_null( $the_['function'] ) ) {
$args[0] = $value;
$value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int) $the_['accepted_args'] ) );
}
}
} while ( next( $wp_filter[ $hook_name ] ) !== false );
return $value;
}
这个函数做了以下几件事:
- 从
$wp_filter
全局变量中获取与$tag
相关的钩子函数。 - 按照优先级对钩子函数进行排序(如果尚未排序)。
- 遍历所有钩子函数,并使用
call_user_func_array()
函数调用它们。 - 将钩子函数的返回值传递给下一个钩子函数,或者作为
apply_filters()
的最终返回值。
do_action()
函数的实现类似,只是它不返回值,而是直接执行钩子函数。
call_user_func_array()
:幕后推手
call_user_func_array()
是一个非常重要的 PHP 函数,它可以动态地调用函数,并传递一个数组作为参数。这使得 WordPress 可以灵活地调用各种类型的钩子函数,而无需知道它们的具体签名。
在 apply_filters()
中,call_user_func_array()
的使用方式如下:
$value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int) $the_['accepted_args'] ) );
$the_['function']
:要调用的函数名或闭包。array_slice( $args, 0, (int) $the_['accepted_args'] )
:要传递给函数的参数数组。array_slice()
函数用于截取$args
数组的一部分,确保只传递$accepted_args
指定数量的参数。
实际案例:修改文章摘要
让我们用一个实际的例子来演示如何使用 add_filter()
修改文章摘要:
add_filter( 'get_the_excerpt', 'my_custom_excerpt', 10, 1 );
function my_custom_excerpt( $excerpt ) {
if ( empty( $excerpt ) ) {
$content = get_the_content();
$excerpt = wp_trim_words( $content, 20, '...' );
}
return '<p>Custom Excerpt: ' . $excerpt . '</p>';
}
在这个例子中,我们使用 add_filter()
注册了一个名为 my_custom_excerpt
的函数,用于修改 'get_the_excerpt'
钩子的返回值。如果文章没有摘要,我们从文章内容中提取前 20 个词作为摘要,并添加一个自定义的前缀。
总结:add_action()
和 add_filter()
的精髓
add_action()
和add_filter()
是WordPress扩展性的基石。- 它们通过操作
$wp_filter
全局变量来注册钩子函数。 $priority
参数决定了钩子函数的执行顺序。$accepted_args
参数决定了传递给钩子函数的参数数量。apply_filters()
和do_action()
函数负责调用已注册的钩子函数。call_user_func_array()
函数用于动态地调用钩子函数。
进阶思考:性能优化
虽然 add_action()
和 add_filter()
非常强大,但过度使用也会影响性能。以下是一些优化建议:
- 避免注册不必要的钩子函数。
- 使用合适的优先级,确保钩子函数在正确的时机执行。
- 尽量减少钩子函数的执行时间。
- 使用缓存来避免重复计算。
结语:掌握核心,玩转WordPress
希望今天的讲座能帮助你更深入地理解 add_action()
和 add_filter()
函数的源码。掌握这些核心概念,你就能更好地自定义WordPress的行为,构建更强大的网站。记住,多动手实践,才能真正理解它们的精髓。下次有机会再和大家一起探讨WordPress的其他有趣源码!感谢大家的参与!