各位技术爱好者,大家好!今天咱们来聊聊 WordPress 源码里两个挺有意思的函数:doing_action()
和 doing_filter()
。这俩哥们儿,可以说是 WordPress 钩子机制里的“侦察兵”,专门负责摸清底细,看看当前代码是不是正儿八经地在某个 Action 或者 Filter 钩子的“地盘”里执行。
一、钩子机制:WordPress 的“神经系统”
在深入 doing_action()
和 doing_filter()
之前,咱们先简单回顾一下 WordPress 的钩子机制。简单来说,它就像 WordPress 这具庞大身躯里的“神经系统”,允许开发者在不修改核心代码的情况下,插入自己的代码,扩展或修改 WordPress 的功能。
钩子分为两种主要类型:
- Action (动作钩子): 允许你在特定事件发生时执行自定义代码。比如,文章发布后、主题加载前等等。
- Filter (过滤器钩子): 允许你修改 WordPress 的数据。比如,文章内容、标题等等。
有了钩子,WordPress 才能如此灵活和可扩展,各种插件和主题才能百花齐放。
二、doing_action()
:Action 钩子的“探测器”
doing_action()
函数的作用是:判断当前代码是否正在执行某个特定的 Action 钩子。如果正在执行,它会返回 true
;否则,返回 false
。 如果没有传入参数,则判断是否在执行任何 Action 钩子。
咱们来看看 doing_action()
的源码 (基于 WordPress 最新版):
/**
* Check if any action is currently being run.
*
* @since 2.5.0
*
* @param string|null $action Optional. The name of the action to check for.
* @return bool True if the action is currently being run, false otherwise.
*/
function doing_action( $action = null ) {
global $wp_current_filter;
if ( null === $action ) {
return ! empty( $wp_current_filter );
}
return in_array( $action, $wp_current_filter, true );
}
这段代码的核心逻辑其实很简单:
-
全局变量
$wp_current_filter
: 这是 WordPress 维护的一个全局数组,用来记录当前正在执行的 Action 和 Filter 钩子的名称。 每次执行 Action 或 Filter 时,钩子的名称都会被添加到这个数组里;执行完毕后,又会被移除。 所以,$wp_current_filter
就像一个“执行栈”,记录着当前代码的“调用链”。 -
判断
$action
是否为空:-
如果
$action
为空 (null
),那么doing_action()
就简单地判断$wp_current_filter
是否为空。如果$wp_current_filter
不为空,说明当前正在执行某个 Action 或 Filter,返回true
;否则,返回false
。 -
如果
$action
不为空,那么doing_action()
就会在$wp_current_filter
数组里查找是否存在$action
这个元素。如果存在,说明当前正在执行名为$action
的 Action 钩子,返回true
;否则,返回false
。in_array
函数的第三个参数为true
,代表严格类型检查。
-
举个例子:
假设我们有如下代码:
add_action( 'wp_footer', 'my_footer_function' );
function my_footer_function() {
if ( doing_action( 'wp_footer' ) ) {
echo '<p>正在执行 wp_footer 钩子!</p>';
} else {
echo '<p>没有执行 wp_footer 钩子!</p>';
}
if ( doing_action() ) {
echo '<p>正在执行某个 Action 钩子!</p>';
} else {
echo '<p>没有执行任何 Action 钩子!</p>';
}
}
这段代码的意思是:当 wp_footer
钩子被触发时,执行 my_footer_function
函数。在这个函数里,我们用 doing_action('wp_footer')
来判断当前是否正在执行 wp_footer
钩子,并用 doing_action()
来判断是否正在执行任何Action钩子。
显然,在 my_footer_function
函数内部,doing_action('wp_footer')
和 doing_action()
都会返回 true
,所以会输出:
<p>正在执行 wp_footer 钩子!</p>
<p>正在执行某个 Action 钩子!</p>
三、doing_filter()
:Filter 钩子的“探测器”
doing_filter()
函数的作用与 doing_action()
类似,只不过它是用来判断当前代码是否正在执行某个特定的 Filter 钩子。 如果正在执行,它会返回 true
;否则,返回 false
。如果没有传入参数,则判断是否在执行任何Filter钩子。
咱们来看看 doing_filter()
的源码 (基于 WordPress 最新版):
/**
* Check if any filter is currently being run.
*
* @since 3.9.0
*
* @param string|null $filter Optional. The name of the filter to check for.
* @return bool True if the filter is currently being run, false otherwise.
*/
function doing_filter( $filter = null ) {
global $wp_current_filter;
if ( null === $filter ) {
return ! empty( $wp_current_filter );
}
return in_array( $filter, $wp_current_filter, true );
}
是不是觉得和 doing_action()
的源码几乎一模一样? 没错,它们的核心逻辑完全相同,都是通过检查全局变量 $wp_current_filter
来判断当前是否正在执行指定的钩子。
举个例子:
假设我们有如下代码:
add_filter( 'the_content', 'my_content_filter' );
function my_content_filter( $content ) {
if ( doing_filter( 'the_content' ) ) {
$content .= '<p>正在通过 the_content 过滤器!</p>';
} else {
$content .= '<p>没有通过 the_content 过滤器!</p>';
}
if ( doing_filter() ) {
$content .= '<p>正在通过某个 Filter 过滤器!</p>';
} else {
$content .= '<p>没有通过任何 Filter 过滤器!</p>';
}
return $content;
}
这段代码的意思是:当 the_content
过滤器被应用时,执行 my_content_filter
函数。在这个函数里,我们用 doing_filter('the_content')
来判断当前是否正在执行 the_content
过滤器,并用 doing_filter()
来判断是否正在执行任何Filter钩子。
显然,在 my_content_filter
函数内部,doing_filter('the_content')
和 doing_filter()
都会返回 true
,所以文章内容会追加:
<p>正在通过 the_content 过滤器!</p>
<p>正在通过某个 Filter 过滤器!</p>
四、doing_action()
和 doing_filter()
的应用场景
那么,doing_action()
和 doing_filter()
到底有什么用呢? 它们主要用于以下几种场景:
-
避免无限循环: 在某些情况下,如果在 Action 或 Filter 钩子的处理函数里又触发了同一个钩子,可能会导致无限循环。
doing_action()
和doing_filter()
可以用来检测这种情况,避免程序崩溃。add_action( 'save_post', 'my_save_post_function' ); function my_save_post_function( $post_id ) { // 防止无限循环 if ( doing_action( 'save_post' ) ) { return; } // ... 其他处理逻辑 ... }
-
条件执行代码: 有时候,我们希望某些代码只在特定的 Action 或 Filter 钩子被触发时才执行。
doing_action()
和doing_filter()
可以用来判断当前是否满足条件。add_action( 'wp_footer', 'my_footer_function' ); function my_footer_function() { // 只在 wp_footer 钩子触发时才执行 if ( doing_action( 'wp_footer' ) ) { echo '<p>版权信息</p>'; } }
-
调试和排错: 在调试 WordPress 插件或主题时,
doing_action()
和doing_filter()
可以帮助我们确认代码是否按照预期在特定的钩子里执行。add_action( 'wp_footer', 'my_footer_function' ); function my_footer_function() { // 调试信息 if ( doing_action( 'wp_footer' ) ) { error_log( 'my_footer_function 正在执行' ); } }
-
处理嵌套钩子的情况: 有时候,一个钩子的处理函数内部会触发另一个钩子。
doing_action()
和doing_filter()
可以用来区分当前处于哪个钩子的上下文中。add_action('init', 'outer_action'); add_action('inner_action', 'inner_action_callback'); function outer_action() { echo "Outer Action started. Doing action: " . (doing_action('init') ? 'init' : 'none') . "<br>"; do_action('inner_action'); echo "Outer Action finished. Doing action: " . (doing_action('init') ? 'init' : 'none') . "<br>"; } function inner_action_callback() { echo "Inner Action callback started. Doing action: " . (doing_action('inner_action') ? 'inner_action' : 'none') . " and init: " . (doing_action('init') ? 'init' : 'none') . "<br>"; echo "Inner Action callback finished. Doing action: " . (doing_action('inner_action') ? 'inner_action' : 'none') . " and init: " . (doing_action('init') ? 'init' : 'none') . "<br>"; } // Output: // Outer Action started. Doing action: init // Inner Action callback started. Doing action: inner_action and init: init // Inner Action callback finished. Doing action: inner_action and init: init // Outer Action finished. Doing action: init
这个例子清晰地展示了嵌套钩子执行时,
doing_action()
如何帮助我们跟踪当前执行的上下文。 在inner_action_callback
中,我们可以同时检测到inner_action
和init
正在被执行。
五、注意事项
doing_action()
和doing_filter()
只能判断当前代码是否 正在 执行指定的钩子。 如果钩子已经执行完毕,或者尚未开始执行,它们都会返回false
。- 由于
$wp_current_filter
是一个全局变量,因此在并发环境下(例如,使用多线程或异步任务),可能会出现竞争条件,导致doing_action()
和doing_filter()
的结果不准确。 在这种情况下,需要采取适当的同步措施来保证数据的完整性。虽然 WordPress 本身很少涉及多线程,但在一些高级应用场景下,需要注意这个问题。 - 避免过度使用
doing_action()
和doing_filter()
。 过度依赖这些函数可能会导致代码逻辑过于复杂,可读性降低。 应该尽量使用更明确的方式来控制代码的执行流程,例如,通过传递参数、使用类方法等等。
六、总结
doing_action()
和 doing_filter()
是 WordPress 钩子机制中非常有用的工具,可以帮助我们判断当前代码是否正在执行特定的 Action 或 Filter 钩子。 掌握了它们的用法,可以让我们更好地理解 WordPress 的运行机制,编写更健壮、更灵活的插件和主题。
下面用一个表格总结一下这两个函数:
函数名称 | 作用 | 参数 | 返回值 |
---|---|---|---|
doing_action() |
判断当前代码是否正在执行某个 Action 钩子。 | 可选,Action 钩子的名称 (字符串)。如果为空,则判断是否正在执行任何 Action 钩子。 | 如果正在执行指定的 Action 钩子或任何 Action 钩子,则返回 true ;否则,返回 false 。 |
doing_filter() |
判断当前代码是否正在执行某个 Filter 钩子。 | 可选,Filter 钩子的名称 (字符串)。如果为空,则判断是否正在执行任何 Filter 钩子。 | 如果正在执行指定的 Filter 钩子或任何 Filter 钩子,则返回 true ;否则,返回 false 。 |
希望今天的讲解能帮助大家更好地理解 doing_action()
和 doing_filter()
这两个函数。 如果大家还有什么疑问,欢迎随时提问。 感谢大家的聆听!