WordPress HOOK机制原理及代码分析

WordPress强大的插件机制让我们可以自由扩展功能。网上对插件的使用以及开发方法都有大量资料可以查询。
今天我们就分析一下四个主要函数的代码,包括:
add_action、do_action、add_filter、apply_action。

一、add_action和add_filter

为什么把这两个函数放在一起讲呢?其实我们看看add_action函数的定义(图一)就可以知道,其实跟add_filter是同一个函数,执行的是相同的操作。只是把action和filter区分开,让开发者能更好地开发插件而设置的。

code

1

2

3

4

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)

{

    return add_filter($tag, $function_to_add, $priority, $accepted_args);

}

现在我们再看看add_filter的函数定义:

code

1

2

3

4

5

6

7

8

function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {

    global $wp_filter, $merged_filters;

    $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);

    unset( $merged_filters[ $tag ] );

    return true;

}

函数的第一行就是定义$wp_filter和$merged_filters。$wp_filter是一个多维数组,保存了目前所有的已注册在挂钩上的函数的信息。把它扩展开可以看到这样子的结构:

这里我们以两个挂钩作为例子来讲解。很明显它的结构是这样子的:

code

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

$wp_filter=array(

    ‘挂钩名‘=>array(

        ‘优先级‘=>array(

            ‘函数1‘=>array(

                ‘function‘=>"函数名",

                ‘accepted_args‘=>"函数接受的参数数量"

            ),

            ‘对象‘=>array(

                ‘function‘=>array(

                    ‘0‘=>‘对象的引用‘,

                    ‘1‘=>‘对象上的方法‘

                ),

                ‘accepted_args‘=>"方法接受的参数数量"

            )

        )

    )

);

那么$merged_filter是什么呢?其实在这个函数里并没有体现出它的作用。但是如果我们结合do_action函数里的代码。

code

1

2

3

4

if ( !isset( $merged_filters[ $tag ] ) ) {

    ksort($wp_filter[$tag]);

    $merged_filters[ $tag ] = true;

}

我们可以知道,当相应的挂钩下的函数被调用的时候,它会对这些函数进行优先级排序,当排完序的时候,$merged_filters下对应的函数就
会被设置为true。而当我们在相应挂钩下添加一个函数的时候,它在$merged_filters数组下的值会被删除。说白了,它就是一个标识,用来说
明这个标识下的函数是否已经经过优先级排序。
我们继续分析代码。

code

1

$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);

这句代码里的函数我们就不展开去读了,我们只需要知道它返回的是下面这部分的内容就行了。

code

01

02

03

04

05

06

07

08

09

10

11

‘函数1‘=>array(

    ‘function‘=>"函数名",

    ‘accepted_args‘=>"函数接受的参数数量"

),

‘对象‘=>array(

    ‘function‘=>array(

        ‘0‘=>‘对象的引用‘,

        ‘1‘=>‘对象上的方法‘

    ),

    ‘accepted_args‘=>"方法接受的参数数量"

)

接下来的这句代码也很简单,就是把函数配置到挂钩上面去而已。

code

1

$wp_filter[$tag][$priority][$idx] = array(‘function‘ => $function_to_add, ‘accepted_args‘ => $accepted_args);

因为这里插入了一条函数,可能改变了它下面的函数的执行优先级,所以这里的标识要删除。

code

1

unset( $merged_filters[ $tag ] );

二、do_action

函数定义:

code

01

02

03

04

05

06

07

08

09

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

42

43

44

45

46

47

48

function do_action($tag, $arg = ‘‘) {

    global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;

    if ( ! isset($wp_actions) )

        $wp_actions = array();

    if ( ! isset($wp_actions[$tag]) )

        $wp_actions[$tag] = 1;

    else

        ++$wp_actions[$tag];

    $wp_current_filter[] = $tag;

    if ( isset($wp_filter[‘all‘]) ) {

        $all_args = func_get_args();

        _wp_call_all_hook($all_args);

    }

    if ( !isset($wp_filter[$tag]) ) {

        array_pop($wp_current_filter);

        return;

    }

    $args = array();

    if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) ) // array(&$this)

        $args[] =& $arg[0];//参数是个数组,并且只有一个元素,并且这个元素不为空,而且是一个对象。

    else

        $args[] = $arg;

    for ( $a = 2; $a < func_num_args(); $a++ )

        $args[] = func_get_arg($a);

    // Sort

    if ( !isset( $merged_filters[ $tag ] ) ) {

        ksort($wp_filter[$tag]);

        $merged_filters[ $tag ] = true;

    }

    reset( $wp_filter[ $tag ] );

    do {

        foreach ( (array) current($wp_filter[$tag]) as $the_ )

            if ( !is_null($the_[‘function‘]) )

                call_user_func_array($the_[‘function‘], array_slice($args, 0, (int) $the_[‘accepted_args‘]));

    } while ( next($wp_filter[$tag]) !== false );

    array_pop($wp_current_filter);

}

第一条语句定义了几个全局函数:$wp_filter,$merged_filters,$wp_actions,$wp_current_filter。
前两个变量在前面已经做了说明了,$wp_actions是记录action被调用的次数,$wp_current_filter是记录当前使用的action的信息,它是一个堆栈结构,当出现递归调用的时候就非常有用了。
系统会先记录这个action的调用次数。然后再把当前调用的挂钩记录起来。查找有没有通用的挂钩函数,有的话就执行。接着检查有没有指定的挂钩函数,没有的话就把$wp_current_filter里相应的元素弹出,并把跳出函数返回。
如果挂钩下有相应的函数的话,那么先把要传递给函数的参数放到数组里面,再进行优先级排序,最后再一一执行。最后还是要把$wp_current_filter里相应的元素弹出。

三、apply_filter

函数定义:

code

01

02

03

04

05

06

07

08

09

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

42

function apply_filters($tag, $value) {

    global $wp_filter, $merged_filters, $wp_current_filter;

    $args = array();

    $wp_current_filter[] = $tag;

    // Do ‘all‘ actions first

    if ( isset($wp_filter[‘all‘]) ) {

        $args = func_get_args();

        _wp_call_all_hook($args);

    }

    //如果钩子上没有这个函数,那么把函数挂到这个钩子上去。

    if ( !isset($wp_filter[$tag]) ) {

        array_pop($wp_current_filter);

        return $value;

    }

    // Sort 对挂钩上面的函数根据优先级进行排序,将$merged_filters[$tag]设置为真

    if ( !isset( $merged_filters[ $tag ] ) ) {

        ksort($wp_filter[$tag]);

        $merged_filters[ $tag ] = true;

    }

    //把数组指针重新定回首部

    reset( $wp_filter[ $tag ] );

    //获得参数

    if ( empty($args) )

        $args = func_get_args();

//    对挂钩上的每一个函数进行处理

    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;

}

仔细一看,你会发现它的代码跟do_action差不多。是的!它跟do_action有几点区别:
1、它并不记录该挂钩运行的次数。
2、由于它传入的都是一个字符串类型的参数,所以它的参数存储比较简单。
3、处理完所有函数后,会把最终处理结果返回。

reference from : http://caord.ynot.cn/?p=467

时间: 2024-10-18 13:02:57

WordPress HOOK机制原理及代码分析的相关文章

免费的Lucene 原理与代码分析完整版下载

Lucene是一个基于Java的高效的全文检索库.那么什么是全文检索,为什么需要全文检索?目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结构的或者有限长度的数据,比如数据库,元数据等.非结构化数据则是不定长或者没有固定格式的数据,如图片,邮件,文档等.还有一种较少的分类为半结构化数据,如XML,HTML等,在一定程度上我们可以将其按照结构化数据来处理,也可以抽取纯文本按照非结构化数据来处理.非结构化数据又称为全文数据.,对其搜索主要有两种

Mesos原理与代码分析(4) Mesos Master的启动之三

3. ModuleManager::load(flags.modules.get())如果有参数--modules或者--modules_dir=dirpath,则会将路径中的so文件load进来 ? 代码中加载模块的代码如下 ? 对应的命令行参数如下: ? ? 都可以写什么Module呢? ? 首先是Allocator ? 默认是内置的Hierarchical Dominant Resource Fairness allocator ? 要写一个自己的Allocator: 通过--module

Mesos原理与代码分析(5): Mesos Master的启动之四

? 5. Create an instance of allocator. ? 代码如下 ? Mesos源码中默认的Allocator,即HierarchicalDRFAllocator的位置在$MESOS_HOME/src/master/allocator/mesos/hierarchical.hpp,而DRF中对每个Framework排序的Sorter位于$MESOS_HOME/src/master/allocator/sorter/drf/sorter.cpp,可以查看其源码了解它的工作原

Openvswitch原理与代码分析(1):总体架构

一.Opevswitch总体架构 Openvswitch的架构网上有如下的图表示: 每个模块都有不同的功能 ovs-vswitchd 为主要模块,实现交换机的守护进程daemon 在Openvswitch所在的服务器进行ps aux可以看到以下的进程 root 1008 0.1 0.8 242948 31712 ? S<Ll Aug06 32:17 ovs-vswitchd unix:/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err

Mesos原理与代码分析(2): Mesos Master的启动之一

Mesos Master的启动参数如下: /usr/sbin/mesos-master --zk=zk://127.0.0.1:2181/mesos --port=5050 --log_dir=/var/log/mesos --hostname=192.168.56.101 --hostname_lookup=false --ip=192.168.56.101 --quorum=1 --registry=replicated_log --work_dir=/var/lib/mesos/maste

Openvswitch原理与代码分析(6):用户态流表flow table的操作

当内核无法查找到流表项的时候,则会通过upcall来调用用户态ovs-vswtichd中的flow table. 会调用ofproto-dpif-upcall.c中的udpif_upcall_handler函数. static void * udpif_upcall_handler(void *arg) { ????struct handler *handler = arg; ????struct udpif *udpif = handler->udpif; ? ????while (!latc

Mesos原理与代码分析(3): Mesos Master的启动之二

2. process::firewall::install(move(rules));如果有参数--firewall_rules则会添加规则 ? 对应的代码如下: // Initialize firewall rules. if (flags.firewall_rules.isSome()) { vector<Owned<FirewallRule>> rules; ? const Firewall firewall = flags.firewall_rules.get(); ? i

Lucene原理与代码分析解读笔记

Lucene是一个基于Java的高效的全文检索库. 那么什么是全文检索,为什么需要全文检索? 目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结构的或者有限长度的数据,比如数据库,元数据等.非结构化数据则是不定长或者没有固定格式的数据,如图片,邮件,文档等.还有一种较少的分类为半结构化数据,如XML,HTML等,在一定程度上我们可以将其按照结构化数据来处理,也可以抽取纯文本按照非结构化数据来处理. 非结构化数据又称为全文数据.,对其搜索主要

Openvswitch原理与代码分析(3): openvswitch内核模块的加载

在datapath/datapath.c中会调用module_init(dp_init);来初始化内核模块. static int __init dp_init(void){   int err;    BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb));    pr_info("Open vSwitch switching datapath %s\n", VERSION);