各位观众老爷,晚上好!今天咱们来聊聊WordPress里一个重量级的函数——add_filter()
。 这玩意儿,说是WordPress插件的灵魂都不为过。 掌握了它,你就掌握了修改WordPress核心功能的钥匙,能让你的网站变得独一无二,骚气十足。
一、什么是Filter? 别跟我说滤镜啊!
首先,我们要搞清楚什么是Filter。 别跟我扯照片滤镜,那个是美颜用的,我们这是改变网站行为用的!
在WordPress的世界里,Filter就像一个检查站。 WordPress的核心代码在执行到某些关键节点的时候,会触发一个Filter。 这个时候,所有注册到这个Filter上的函数(也就是你通过add_filter()
添加的函数)都会被依次调用,它们可以修改WordPress传递给Filter的数据,然后再把修改后的数据传回给WordPress。
简单来说,就是:
- WordPress运行到某个地方,想问问大家意见(触发Filter)。
- 注册到这个Filter的函数们纷纷发表意见,修改数据。
- WordPress最终采纳修改后的数据,继续运行。
举个例子,假设WordPress要显示文章的标题,它会触发一个名为the_title
的Filter。 你的插件可以通过add_filter('the_title', 'my_custom_title')
来注册一个名为my_custom_title
的函数。 这个函数就可以修改文章标题,比如给标题加上一个前缀或者后缀,或者直接把标题改成“秘密”。
二、add_filter()
函数: 插件的魔法棒
add_filter()
函数是把你的自定义函数注册到WordPress Filter上的关键。 它的语法如下:
add_filter( string $tag, callable $function_to_add, int $priority = 10, int $accepted_args = 1 )
$tag
(string, required): Filter的名称,也就是WordPress定义的“检查站”的名字。比如the_title
、the_content
、wp_mail
等等。 你要修改哪个地方,就填哪个Filter的名字。$function_to_add
(callable, required): 你要注册的函数名。 这个函数就是你的自定义逻辑,用来修改数据的。$priority
(int, optional): 优先级。 数字越小,优先级越高。 如果有多个函数注册到同一个Filter上,优先级高的函数会先执行。 默认值是10。 想象一下,一群大臣给皇帝提意见,优先级高的先说。$accepted_args
(int, optional): 你的函数接收的参数个数。 也就是WordPress会传递给你的函数多少个参数。 默认值是1。
三、源码剖析: 看看幕后发生了什么
为了更深入地理解add_filter()
,我们来扒一扒它的源码,看看WordPress内部是怎么实现的。 (简化版,忽略了一些错误处理和兼容性代码)
// 大概的实现,实际源码更复杂
function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
global $wp_filter, $wp_actions;
// 确保 $wp_filter 存在
if ( ! isset( $wp_filter[ $tag ] ) ) {
$wp_filter[ $tag ] = array();
}
// 确保优先级存在
if ( ! isset( $wp_filter[ $tag ][ $priority ] ) ) {
$wp_filter[ $tag ][ $priority ] = array();
}
// 创建一个唯一的标识符,避免重复添加同一个函数
$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
);
// 排序,保证优先级生效
ksort( $wp_filter[ $tag ] );
// 如果是新的action,记录一下
if ( ! isset( $wp_actions[ $tag ] ) ) {
$wp_actions[ $tag ] = 0;
}
return true;
}
// 生成唯一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];
}
return false;
}
简单解释一下:
-
global $wp_filter
:$wp_filter
是一个全局数组,用来存储所有的Filter。 它的结构是这样的:$wp_filter = array( 'filter_name' => array( priority => array( 'unique_id' => array( 'function' => 'function_name', 'accepted_args' => 1 ) ) ) );
filter_name
:Filter的名字,比如the_title
。priority
:优先级,数字越小优先级越高。unique_id
:函数的唯一标识符,防止重复添加同一个函数。function
:要执行的函数名。accepted_args
:函数接收的参数个数。
-
检查Filter和优先级是否存在: 如果Filter或优先级不存在,就创建它们。
-
生成唯一ID: 使用
_wp_filter_build_unique_id()
函数生成一个唯一的ID,用来区分不同的函数。 -
存储函数信息: 把函数名和参数个数存储到
$wp_filter
数组中。 -
排序: 使用
ksort()
函数对优先级进行排序,保证优先级高的函数先执行。 -
记录Action: 如果是新的action,记录一下。 (Action和Filter类似,但是Action不修改数据,只是执行一些操作。 后面会讲到。)
总的来说,add_filter()
函数的作用就是把你的函数信息存储到$wp_filter
全局数组中,供WordPress在需要的时候调用。
四、apply_filters()
函数: 触发Filter的开关
apply_filters()
函数是真正触发Filter的地方。 它的语法如下:
apply_filters( string $tag, mixed $value, mixed ...$args )
$tag
(string, required): Filter的名称,和add_filter()
中的$tag
要对应。$value
(mixed, required): 要传递给Filter的初始值。 也就是WordPress想要修改的数据。...$args
(mixed, optional): 其他要传递给Filter的参数。
看个例子:
$title = '原始标题';
$title = apply_filters( 'the_title', $title );
echo $title;
在这个例子中,apply_filters('the_title', $title)
会触发the_title
这个Filter。 所有注册到the_title
上的函数都会被依次调用,它们可以修改$title
的值,最终apply_filters()
会返回修改后的$title
。
源码剖析:
// 大概的实现,实际源码更复杂
function apply_filters( $tag, $value, ...$args ) {
global $wp_filter, $wp_current_filter;
// 如果没有注册任何函数到这个Filter上,直接返回原始值
if ( ! isset( $wp_filter[ $tag ] ) ) {
return $value;
}
// 记录当前的Filter,用于调试
$wp_current_filter[] = $tag;
// 获取所有注册到这个Filter上的函数
$filters = $wp_filter[ $tag ];
// 对优先级进行排序
ksort( $filters );
// 遍历所有函数,依次调用
foreach ( $filters as $priority => $functions ) {
foreach ( $functions as $function ) {
// 根据 accepted_args 的值,传递不同数量的参数
$num_args = $function['accepted_args'];
switch ( $num_args ) {
case 1:
$value = call_user_func( $function['function'], $value );
break;
case 2:
$value = call_user_func( $function['function'], $value, $args[0] );
break;
case 3:
$value = call_user_func( $function['function'], $value, $args[0], $args[1] );
break;
default:
$value = call_user_func_array( $function['function'], array_slice( array_merge( array( $value ), $args ), 0, $num_args ) );
break;
}
}
}
// 移除当前的Filter
array_pop( $wp_current_filter );
return $value;
}
简单解释一下:
global $wp_filter
: 和add_filter()
一样,需要访问全局的$wp_filter
数组。- 检查Filter是否存在: 如果没有注册任何函数到这个Filter上,直接返回原始值。
- 记录当前的Filter: 记录当前的Filter,用于调试。
- 获取所有函数: 从
$wp_filter
数组中获取所有注册到这个Filter上的函数。 - 排序: 对优先级进行排序,保证优先级高的函数先执行。
- 遍历函数: 遍历所有函数,依次调用。
- 传递参数: 根据
accepted_args
的值,传递不同数量的参数给函数。 - 移除当前的Filter: 移除当前的Filter,用于调试。
- 返回最终值: 返回经过所有函数修改后的最终值。
总的来说,apply_filters()
函数的作用就是从$wp_filter
数组中找到所有注册到指定Filter上的函数,然后依次调用它们,并把修改后的值返回。
五、Action: 只执行,不修改
除了Filter,WordPress还有Action。 Action和Filter很像,但是Action不修改数据,只是执行一些操作。
比如,你想在文章发布之后发送一封邮件,就可以注册一个Action到publish_post
这个钩子上。
add_action()
和do_action()
分别用来注册Action和触发Action。 它们的用法和add_filter()
和apply_filters()
非常相似,这里就不再赘述了。
六、实战演练: 修改文章标题
现在,我们来写一个简单的插件,修改文章标题,给标题加上一个前缀“【骚气】”。
<?php
/**
* Plugin Name: 骚气标题插件
* Plugin URI: https://www.example.com/
* Description: 给文章标题加上一个前缀“【骚气】”。
* Version: 1.0.0
* Author: 骚气程序员
* Author URI: https://www.example.com/
*/
// 注册一个Filter到the_title上
add_filter('the_title', 'add_sauqi_prefix');
// 自定义函数,给标题加上前缀
function add_sauqi_prefix($title) {
return '【骚气】' . $title;
}
把这段代码保存为sauqi-title.php
,上传到wp-content/plugins/
目录下,然后在WordPress后台激活这个插件。 刷新一下你的文章页面,看看是不是所有文章的标题都加上了“【骚气】”前缀?
七、优先级: 谁说了算?
如果多个插件都注册到同一个Filter上,谁说了算? 这就要看优先级了。 优先级数字越小,优先级越高。
比如,你有两个插件,一个插件给文章标题加上前缀“【骚气】”,另一个插件给文章标题加上后缀“(已修改)”。
// 插件1:骚气标题插件
add_filter('the_title', 'add_sauqi_prefix', 10);
function add_sauqi_prefix($title) {
return '【骚气】' . $title;
}
// 插件2:修改后缀插件
add_filter('the_title', 'add_suffix', 20);
function add_suffix($title) {
return $title . '(已修改)';
}
在这个例子中,add_sauqi_prefix
的优先级是10,add_suffix
的优先级是20。 因此,add_sauqi_prefix
会先执行,然后add_suffix
再执行。 最终的文章标题会是“【骚气】原始标题(已修改)”。
如果你想让add_suffix
先执行,只需要把它的优先级设置为小于10的数字即可。
八、参数个数: 要多少给多少
accepted_args
参数决定了WordPress会传递给你的函数多少个参数。 默认值是1,也就是只传递第一个参数(也就是apply_filters()
中的$value
)。
如果你的函数需要接收更多的参数,就需要设置accepted_args
的值。
比如,the_content
这个Filter会传递两个参数:文章内容和文章对象。 如果你想在你的函数中访问文章对象,就需要把accepted_args
设置为2。
add_filter('the_content', 'add_post_id', 10, 2);
function add_post_id($content, $post) {
return $content . '<p>文章ID:' . $post->ID . '</p>';
}
在这个例子中,add_post_id
函数接收两个参数:$content
(文章内容)和$post
(文章对象)。 通过$post->ID
可以访问文章的ID。
九、一些常用的Filter和Action
| 钩子类型 | 钩子名称 | 描述 and so on…
**十、 调试技巧: 让BUG无处遁形**
* **`doing_filter()`和`doing_action()`:** 这两个函数可以用来判断当前是否正在执行某个Filter或Action。
* **`current_filter()`和`current_action()`:** 这两个函数可以用来获取当前正在执行的Filter或Action的名称。
* **`remove_filter()`和`remove_action()`:** 这两个函数可以用来移除已经注册的Filter或Action。
**十一、注意事项: 别玩脱了!**
* **不要修改核心代码:** 永远不要直接修改WordPress的核心代码。 所有的修改都应该通过插件或者主题来实现。
* **谨慎使用Filter和Action:** 过度使用Filter和Action可能会导致性能问题。
* **注意优先级:** 确保你的函数的优先级是正确的。
* **测试你的代码:** 在发布你的插件之前,一定要进行充分的测试。
**十二、总结: 骚起来吧!**
`add_filter()`函数是WordPress插件开发中最核心的函数之一。 掌握了它,你就可以修改WordPress的核心功能,让你的网站变得独一无二,骚气十足。 但是,也要注意谨慎使用,避免玩脱了。
希望今天的讲座对大家有所帮助。 谢谢大家!