WordPress过滤器(过滤钩子)
过滤器概念:它是一类函数,在WP执行和传递数据过程中,它可以实现在完成某一动作(如输出到浏览器或写入数据库等)之前对数据进行过滤再处理的目的。
过滤器原理:它主要利用一个全局变量$wp_filter,增加过滤函数时使用add_filter()函数给全局变量$wp_filter增加了一个数组元素,这个值存储了钩子名、对应函数及执行优先级等信息,在调用apply_filters()函数使用过滤器时,它通过循环查找出所有跟钩子关联的函数并将其依次作用于待处理的数据上,最后返回处理后的数据。
过滤器使用步骤:
由于PHP代码会经过Zend等引擎翻译,代码中步骤的先后顺序并不重要,所以以下步骤仅为便于理解钩子原理的伪步骤,不具有实际参考意义!
1、创建钩子(可省略):使用apply_filters()函数可以创建一个没有挂载函数的钩子,挂载函数可以通过add_filter()添加,最后再使用apply_filters()调用执行;
2、创建过滤函数:它需要有传入参数即待处理数据(一般为一个参数,也可多个参数),其他与创建普通函数没有任何区别,函数的作用一般用于对传入数据进行处理;
3、挂载函数:即使用add_filter()将函数挂载到指定钩子上;
4、执行过滤器:使用apply_filters()可以依次执行挂载在指定钩子上的所有函数以处理传入数据,最后返回处理后的数据;
过滤函数详解及源码分析:
1、add_filter($tag,$function_to_add,$priority = 10,$accepted_args = 1)
add_filter()作用:该函数用于给指定的过滤钩子$tag添加指定的挂载函数$function_to_add,同时它可以确定挂载函数执行优先级及其可接收参数个数;
add_filter()参数说明:
$tag为钩子名;
$function_to_add为挂载函数名;
可选参数$priority为该挂载函数执行的优先级,默认为10,该数字越小则越早执行,数字相同则按其添加到钩子上的顺序执行,越早添加越早执行;
可选参数$accepted_args确定挂载函数接收的参数个数,默认为1;
add_filter()源码分析:
1
2
3
4
5
6
7
8
9
10
11
|
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
global $wp_filter, $merged_filters;
# 用于声明全局变量$wp_filter和$merged_filters;
$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
# 声明变量$idx并赋值,其值为调用_wp_filter_build_unique_id()函数所得唯一值,关于该函数的源码分析可见下文;
$wp_filter[$tag][$priority][$idx] = array(‘function’ => $function_to_add, ‘accepted_args’ => $accepted_args);
# 为关联数组$wp_filter[$tag][$priority][$idx]赋值,其值为一个关联数组,该数组有两个值,一个为挂载函数名,另一个为该函数接收的参数个数;
unset( $merged_filters[ $tag ] );
# 标记该钩子上新增了函数,更改$wp_filter数组状态,具体作用见apply_filters()函数$merged_filters部分源码说明;
return true;
}
|
_wp_filter_build_unique_id()源码分析:
该函数可创建一个供存储和检索的唯一值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
function _wp_filter_build_unique_id($tag, $function, $priority) {
global $wp_filter;
# 声明全局变量$wp_filter;
static $filter_id_count = 0;
# 声明静态变量$filter_id_count并赋初值为0;
if ( is_string($function) )
return $function;
# 如果$function的值是字符串则返回该字符串;
if ( is_object($function) ) {
$function = array( $function, ” );
} else {
$function = (array) $function;
}
# 如果$function是一个对象则将其放入数组,否则将其强制转换为数组;
if (is_object($function[0])) {
if ( function_exists(‘spl_object_hash’) ) {
return spl_object_hash($function[0]) . $function[1];
} else {
$obj_idx = get_class($function[0]).$function[1];
if ( !isset($function[0]->wp_filter_id) ) {
if ( false === $priority )
return false;
$obj_idx .= isset($wp_filter[$tag][$priority]) ? count((array)$wp_filter[$tag][$priority]) : $filter_id_count;
$function[0]->wp_filter_id = $filter_id_count;
++$filter_id_count;
} else {
$obj_idx .= $function[0]->wp_filter_id;
}
return $obj_idx;
}
# 如果spl_object_hash()函数存在则根据该函数返回一个唯一哈希值,否则使用其他方法返回一个唯一值;
} else if ( is_string($function[0]) ) {
return $function[0].$function[1];
}
# 如果$function[0]是字符串则返回该字符串
}
|
2、apply_filters($tag, $value)
apply_filters()作用:该函数调用挂载在过滤钩子$tag上的所有挂载函数依然以$value为传入参数递归式处理数据,最后返回经所有挂载函数处理过的值;
apply_filters()参数说明:
$tag为钩子名;
$value为过滤钩子上挂载函数的传入参数;
apply_filters()源码分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
function apply_filters($tag, $value) {
global $wp_filter, $merged_filters, $wp_current_filter;
# 声明全局变量,其中$wp_filter存储了所有的过滤器、$merged_filters使用函数合并钩子、$wp_current_filter存储当前过滤器;
$args = array();
# 声明一个空数组$args;
if ( isset($wp_filter[‘all’]) ) {
$wp_current_filter[] = $tag;
$args = func_get_args();
_wp_call_all_hook($args);
}
# 本段代码分析较长,请见代码结束后“解析1”字样;
if ( !isset($wp_filter[$tag]) ) {
if ( isset($wp_filter[‘all’]) )
array_pop($wp_current_filter);
return $value;
}
# 如果当前钩子无挂载函数且存在名为all的钩子则将当前钩子名弹出钩子队列,取前一个钩子名为当前钩子,最后直接返回未经处理的数据;
if ( !isset($wp_filter[‘all’]) )
$wp_current_filter[] = $tag;
# 如果数组中键名不存在键名为all的元素,则当前钩子设置为$tag;
if ( !isset( $merged_filters[ $tag ] ) ) {
ksort($wp_filter[$tag]);
$merged_filters[ $tag ] = true;
}
# 本段代码分析较长,请见代码结束后“解析2”字样;
reset( $wp_filter[ $tag ] );
# 将$wp_filter[ $tag ]数组的指针归于第一个函数;
if ( empty($args) )
$args = func_get_args();
# 将$args变量赋值,其值为apply_filters()函数传入的待处理数据,可以为单值,也可为多值或一个数组;
do {
foreach( (array) current($wp_filter[$tag]) as $the_ )
if ( !is_null($the_[‘function’]) ){
$args[1] = $value;
$value = call_user_func_array($the_[‘function’], array_slice($args, 1, (int) $the_[‘accepted_args’]));
}
} while ( next($wp_filter[$tag]) !== false );
array_pop( $wp_current_filter );
return $value;
}
# 本段代码分析较长,请见代码结束后“解析3”字样;
|
解析1:如果$wp_filter数组中有名为all的钩子则调用PHP函数func_get_args()来获取当前函数所有参数组成的数组,获取参数后通过_wp_call_all_hook()函数调用钩子名为all的所有filter,关于该函数的源码分析见下文;
解析2:这段代码主要用于对$wp_filter[$tag]根据键名(主要为函数优先级等数字)排序,以得到一个过滤函数(挂载函数)的执行先后顺序的排序,而$merged_filters变量主要用于判断是否需要重新对数组进行排序,若没有新增函数等,则if判断false不再重新排序,若新增了函数则由于
add_filter()函数中有代码unset( $merged_filters[ $tag ] ),所以isset()函数为判断为false而整个if则判断为true,然后执行重新排序函数。这样做可以节省服务器CPU资源,有利于程序优化。
解析3:这段代码就是该函数的精髓了,首先将当前钩子的值(包括过滤函数名和其参数个数)赋给数组$the_,然后通过循环将钩子上所有的过滤函数一个个执行,并且每个函数执行后得到的值会借助参数$args传递给下一个函数,直到所有函数执行完成,最后将这个钩子名弹出“当前钩子”队列并返回最后得到的值;其中,call_user_func_array(func,arg)为PHP函数,主要用于把参数值arg传递给函数func去执行;array_slice(args,star,length)也是PHP函数,主要用于返回数组args中从star开始的length个值,在本代码中,若apply_filters()函数仅接受一个参数值,就相当于把这一个值传给过滤函数,若有两个参数则,则会把这两个值都传给过滤函数;
_wp_call_all_hook()源码分析:
该函数调用名为all的钩子并执行挂载在其上的所有函数;
1
2
3
4
5
6
7
8
9
|
function _wp_call_all_hook($args) {
global $wp_filter;
reset( $wp_filter[‘all’] );
do {
foreach( (array) current($wp_filter[‘all’]) as $the_ )
if ( !is_null($the_[‘function’]) )
call_user_func_array($the_[‘function’], $args);
} while ( next($wp_filter[‘all’]) !== false );
}
|
从apply_filter()源码可以看到当有名为all的钩子存在时,$args获取了当前钩子名和要处理的值,而本函数将这这些参数都放入名为all的钩子上挂载的函数进行处理。至于名为all的钩子是否存在,以及该钩子上挂载了什么函数,我就不知道了