wordpress知识库
网站首页 > 知识库 > wordpress知识 >

add_filter()、apply_filters()代码解析

2019/10/05

首先这两个函数是wordpress的一对钩子函数,无论是主题开发还是wordpress插件开发都会很频繁的使用到这一对钩子函数。

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()源码分析:

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()源码分析:

该函数可创建一个供存储和检索的唯一值。

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()源码分析:

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 ;
}

解析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的钩子并执行挂载在其上的所有函数;

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的钩子上挂载的函数进行处理。