各位观众老爷们,早上好!我是你们的老朋友,今天咱们来聊聊 WordPress 里的一个神奇函数——do_action()
。这玩意儿啊,看起来不起眼,但却是 WordPress 插件和主题开发的灵魂所在。它就像一个交通枢纽,把不同的功能模块连接起来,让你的代码像乐高积木一样灵活组合。
准备好了吗?咱们这就开讲!
1. do_action()
究竟是个啥?
简单来说,do_action()
就是一个“钩子”函数。它会在 WordPress 执行的某个特定时刻,触发你预先定义好的函数。这些预先定义好的函数,我们称之为“钩子函数”。
你可以把它想象成一个“事件发布者”。WordPress 在执行代码的时候,会时不时地喊一声:“嘿,有没有人想在这个时候做点啥?” do_action()
就负责喊这一嗓子。如果你之前注册了一个钩子函数,说:“嘿,老子想在这个时候执行!”,那么 do_action()
就会找到你,执行你的函数。
2. do_action()
的基本用法
do_action()
接受至少一个参数:钩子的名称。
do_action( 'my_custom_action' );
上面这行代码的意思是:WordPress 在执行到这里的时候,会触发名为 my_custom_action
的钩子。
你还可以传递额外的参数给钩子函数:
do_action( 'my_custom_action', $arg1, $arg2, $arg3 );
这些参数会被传递给所有注册到 my_custom_action
上的钩子函数。
3. add_action()
:注册你的钩子函数
光有 do_action()
还不够,你还需要使用 add_action()
函数来注册你的钩子函数。add_action()
告诉 WordPress:“嘿,当 do_action()
喊 my_custom_action
的时候,执行我这个函数!”
function my_custom_function( $arg1, $arg2, $arg3 ) {
// 在这里写你的代码
echo "Arg1: " . $arg1 . "<br>";
echo "Arg2: " . $arg2 . "<br>";
echo "Arg3: " . $arg3 . "<br>";
}
add_action( 'my_custom_action', 'my_custom_function', 10, 3 );
'my_custom_action'
:钩子的名称,和do_action()
里的名字对应。'my_custom_function'
:你的钩子函数的名称。10
:优先级,数字越小,优先级越高。3
:钩子函数接受的参数个数。必须和do_action()
传递的参数个数一致。
4. 源码剖析:do_action()
的内部运作
好了,铺垫了这么多,终于要进入正题了。咱们来扒一扒 do_action()
的源码,看看它是怎么工作的。
do_action()
函数定义在 wp-includes/plugin.php
文件中。它的简化版代码如下:
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 ];
}
// Do 'all' actions first.
if ( isset( $wp_filter['all'] ) ) {
$wp_current_filter[] = $hook_name;
_wp_call_all_hook( $args );
}
if ( ! isset( $wp_filter[ $hook_name ] ) ) {
do_action_ref_array( $hook_name, $args );
return;
}
if ( ! in_array( $hook_name, $wp_current_filter, true ) ) {
$wp_current_filter[] = $hook_name;
}
// Sort.
if ( $wp_filter[ $hook_name ] instanceof WP_Hook ) {
$wp_filter[ $hook_name ]->do_action( $args );
} else {
ksort( $wp_filter[ $hook_name ] );
do_action_ref_array( $hook_name, $args );
}
array_pop( $wp_current_filter );
}
看起来有点复杂,别怕,咱们一步一步来。
-
global $wp_filter, $wp_actions, $wp_current_filter;
这行代码声明了三个全局变量:
$wp_filter
、$wp_actions
和$wp_current_filter
。-
$wp_filter
:这是一个超级重要的数组,它存储了所有注册的钩子函数。它的结构是这样的:$wp_filter = array( 'hook_name' => array( priority => array( 'function_name' => array( 'function' => 'the_function_to_execute', 'accepted_args' => 'number_of_accepted_arguments' ), 'function_name2' => array( 'function' => 'another_function_to_execute', 'accepted_args' => 'number_of_accepted_arguments' ) ), priority2 => array( // ... more functions for this priority ) ), 'another_hook_name' => array( // ... functions for another hook ) );
简单来说,
$wp_filter
是一个多维数组,第一层键是钩子的名称,第二层键是优先级,第三层键是钩子函数的名称,值是一个包含了函数名和接受参数个数的数组。 -
$wp_actions
:这是一个计数器,记录了每个钩子被触发的次数。 -
$wp_current_filter
:这是一个数组,记录了当前正在执行的钩子。用于防止循环调用。
-
-
if ( ! isset( $wp_actions[ $hook_name ] ) ) { ... } else { ... }
这段代码用于增加
$wp_actions
中对应钩子的计数器。 -
if ( isset( $wp_filter['all'] ) ) { ... }
这是一个特殊的情况,如果注册了名为
all
的钩子,那么在执行任何其他钩子之前,会先执行all
钩子上的函数。 -
if ( ! isset( $wp_filter[ $hook_name ] ) ) { ... }
如果没有任何函数注册到这个钩子上,就直接返回。
-
if ( ! in_array( $hook_name, $wp_current_filter, true ) ) { ... }
防止循环调用。如果当前钩子已经在
$wp_current_filter
中,说明已经执行过了,就跳过。 -
if ( $wp_filter[ $hook_name ] instanceof WP_Hook ) { ... } else { ... }
这是 WordPress 5.1 引入的
WP_Hook
类,用于更高效地管理钩子函数。如果$wp_filter[ $hook_name ]
是一个WP_Hook
对象,就调用它的do_action()
方法。否则,就使用ksort()
函数对$wp_filter[ $hook_name ]
数组按照优先级进行排序,然后调用do_action_ref_array()
函数。 -
array_pop( $wp_current_filter );
执行完当前钩子后,从
$wp_current_filter
中移除。
5. do_action_ref_array()
:真正执行钩子函数
do_action()
函数本身并没有直接执行钩子函数,而是调用了 do_action_ref_array()
函数。do_action_ref_array()
才是真正负责执行钩子函数的核心。
它的简化版代码如下:
function do_action_ref_array( $hook_name, $args ) {
global $wp_filter, $wp_actions, $wp_current_filter;
if ( ! isset( $wp_filter[ $hook_name ] ) ) {
return;
}
if ( empty( $wp_filter[ $hook_name ] ) ) {
return;
}
foreach ( (array) current( $wp_filter[ $hook_name ] ) as $the_ ) {
if ( ! is_array( $the_ ) ) {
continue;
}
foreach ( $the_ as $key => $function ) {
if ( is_string( $function['function'] ) ) {
$hook_name_t = explode( '::', $function['function'] );
if ( isset( $hook_name_t[1] ) ) {
$function['function'] = array( $hook_name_t[0], $hook_name_t[1] );
}
}
$accepted_args = (int) $function['accepted_args'];
if ( $accepted_args ) {
$args = array_slice( $args, 0, $accepted_args );
}
$value = call_user_func_array( $function['function'], $args );
if ( false === $value ) {
return;
}
}
}
}
-
if ( ! isset( $wp_filter[ $hook_name ] ) ) { ... }
和
do_action()
一样,如果没有任何函数注册到这个钩子上,就直接返回。 -
foreach ( (array) current( $wp_filter[ $hook_name ] ) as $the_ ) { ... }
这个循环遍历
$wp_filter[ $hook_name ]
数组中的所有优先级。current()
函数用于获取数组的第一个元素(即优先级最低的函数列表)。 -
foreach ( $the_ as $key => $function ) { ... }
这个循环遍历当前优先级下的所有钩子函数。
-
if ( is_string( $function['function'] ) ) { ... }
如果钩子函数是一个字符串,就尝试将其解析为类方法。例如,
'My_Class::my_method'
。 -
$accepted_args = (int) $function['accepted_args'];
获取钩子函数接受的参数个数。
-
if ( $accepted_args ) { ... }
如果钩子函数接受参数,就使用
array_slice()
函数截取do_action()
传递的参数。 -
$value = call_user_func_array( $function['function'], $args );
这就是关键的一行代码! 使用
call_user_func_array()
函数调用钩子函数。call_user_func_array()
允许你使用一个数组作为参数列表来调用一个函数。$function['function']
:要调用的函数名。$args
:传递给函数的参数数组。
call_user_func_array()
会根据$function['function']
的值,找到对应的函数,然后将$args
数组中的元素作为参数传递给这个函数。 -
if ( false === $value ) { ... }
如果钩子函数返回
false
,就停止执行后续的钩子函数。
6. 总结:do_action()
的工作流程
咱们来总结一下 do_action()
的工作流程:
do_action()
被调用,传递钩子的名称和参数。do_action()
检查$wp_filter
数组,看是否有函数注册到这个钩子上。- 如果没有函数注册,就直接返回。
- 如果有函数注册,就对
$wp_filter
数组按照优先级进行排序。 do_action()
调用do_action_ref_array()
函数。do_action_ref_array()
遍历$wp_filter
数组,找到所有注册到这个钩子上的函数。- 对于每一个找到的函数,
do_action_ref_array()
使用call_user_func_array()
函数调用它,并将do_action()
传递的参数传递给它。 - 如果钩子函数返回
false
,就停止执行后续的钩子函数。
7. 举个栗子:主题的 wp_head
钩子
WordPress 主题的 wp_head
钩子是一个非常常用的钩子。它允许你在 <head>
标签中插入自定义的代码,例如 CSS 样式、JavaScript 脚本等。
add_action( 'wp_head', 'my_custom_head_function' );
function my_custom_head_function() {
echo '<style>body { background-color: #f0f0f0; }</style>';
echo '<script>console.log("Hello from wp_head!");</script>';
}
这段代码会在 <head>
标签中插入一段 CSS 样式和一个 JavaScript 脚本。
当 WordPress 执行到 wp_head
钩子时,它会调用 do_action( 'wp_head' );
。do_action()
函数会找到所有注册到 wp_head
钩子上的函数,并使用 call_user_func_array()
函数调用它们。
8. 表格总结
为了方便大家理解,咱们用一个表格来总结一下:
函数 | 作用 | 参数 | 返回值 |
---|---|---|---|
do_action() |
触发一个钩子,执行所有注册到该钩子上的函数。 | 钩子名称 (string), 传递给钩子函数的参数 (mixed) | 无 |
add_action() |
注册一个函数到指定的钩子上。 | 钩子名称 (string), 钩子函数名 (string), 优先级 (int, 默认为 10), 接受的参数个数 (int, 默认为 1) | 无 |
call_user_func_array() |
调用一个函数,并将一个数组作为参数传递给它。 | 函数名 (string), 参数数组 (array) | 函数的返回值 |
$wp_filter |
全局变量,存储了所有注册的钩子函数。 | 无 | 数组 |
9. 总结的总结:灵魂画风
总而言之,do_action()
就像一个媒婆,负责把 WordPress 的各个功能模块连接起来。它通过 $wp_filter
数组找到所有想要参与某个“事件”的函数,然后使用 call_user_func_array()
函数把它们“撮合”在一起,让它们协同工作。
希望今天的讲座能帮助大家更深入地理解 do_action()
函数。掌握了它,你就能更好地开发 WordPress 插件和主题,让你的代码更加灵活和强大!
下次再见!