稍有接触过 WordPress 主题或插件制作修改的朋友,对 WordPress 的Hook机制应该不陌生,但通常刚接触WordPress Hook 的新手,对其运作原理可能会有点混乱或模糊。本文针对 WordPress Hook 运作大致做个简单的说明,而预设读者是理解基本的 PHP function 语法及运作,但对 WordPress Hook 机制不是很明白。

Hook机制里登场的角色

先从“登场角色”的个别说明开始:

WordPress核心

指的是 WordPress 内建的程式码架构,提供 WordPress 主要的基本功能。

Hook

也许你早已听说,Hook 本身虽是钩子的意思,但直译又有点奇怪,所以一般通常都不直译它,而是直接称它 Hook 。WordPress 的 Hook 也可以想像成“钩子”,这些“钩子”会埋在 WordPress 网站中特定几处的程式码中,埋进去时使用的语法,其“标示位置”的意义比较大,没有实质运作的内容。当程式执行到有埋 Hook 的地方时,它会找出所有对应到自己的 Hook Function (也就是所有“钩到”该 Hook 的 hook function),并一一执行。

因此若没有针对此 Hook 去“加入”要钩上去的 Hook Function,执行到此什么也不会做。因此,它等于是WordPress核心预留一个执行的机会给未来想要加入定制功能的开发者。

Hook Function

Hook Function 里会有实质运作的内容,即是实作了一些定制功能,可能是存取 DB、增加 HTML code、执行其他函式等等。我们在 Hook Function 里写好所需的功能后,就可以利用“加入至对应 Hook”的语法,把 Hook Function自已钩到该 Hook 上,使得该 Hook 被执行到时,也会连带执行自己。

Hook机制是如何运作的?

举个例子,我们拿 wp_head 及 wp_footer 这两个内建的hook来说明,wp_head 这个 hook 就是用来埋在负责输出标签的程式码中,而 wp_footer 就是用来埋在输出页尾的程式码中 (定义于 wp-includes/general-template.php,用 wp_head() 及 wp_footer() 包装起来)。这两个 hook,主要都是在布景档案中使用的,常见会出现在 header.php 及 footer.php 中。

请看下面的情境示例图,我们把 wp_head 及 wp_footer 看成是”钩子“,而别的 hook functions 就能来鈎住它:

我们马上来写一个简单的例子。我们要写一个 hook function,就叫它 print_sth(),然後把它钩上 wp_head 这个hook。因为 wp_head() 的内容实际上就只有 do_action( 'wp_head' );  这一行内容,而 wp_footer() 的内容也只有 do_action('wp_footer');所以我们直接把 do_action 的语法换到图上去,比较容易做说明,因此示意图变成:

如此,只要执行到输出 header.php 时,就会执行到 wp_head(),就如同执行到 do_action( 'wp_head' ); 此时WP核心会去找所有”钩上”wp_head 这个 hook 的 hook function,於是就找到我们写的 print_sth() ,然後就执行它,所以结果它做的事就会出现在网站上,也完成了”定制”的动作:

简单的说,Hook 机制就是:WP 核心或其他插件、主题 提供想定制功能的人一个置入定制程式码(Hook Function)到特定的执行时间点(Hook)的机会。

WordPress的Action Hook与Filter Hook

WordPress中的 Hook 有两种,分别是”Action Hook“及”Filter Hook“,我们刚才举例的 wp_head 及wp_footer 都是属於 Action Hook。不过,一开始你可以先把这两种 Hook 看成是一样的东西,只是 Filter 多了一点点不同的特色,接着说明。

Action Hook

WP核心 (或主题、插件)在做它们该做的事时,如果执行到有埋 action hook 的程式码 (即是 do_action 语法) 时,会去找寻对应到的 hook functions,进而执行这些 hook functions(即那些透过 add_action() 来加入的 hook functions),藉此完成定制功能。WP核心并不期待 Action Hook functions 会有回传值,所以这里的 hook function 只被视为一个”独立切出来运作的功能“。

WP核心做它该做的事,你做你想做的事,做完就各自结束。

Filter Hook

跟 Action Hook 一样,WP核心 (或主题、插件)在做它们该做的事时,如果执行到有埋 filter hook 的程式码 (即是apply_filters语法) 时,就会去找寻对应的 hook functions ,进而执行这些 hook functions(即那些透过add_filter() 来加入的 hook functions ),藉此完成定制功能。与 Action Hook 不同之处是,所有”鈎上“ Filter Hook 的 hook functions 通常都会接收到参数,而WP核心会期待你拿到它提供的参数,并做完你想做的事后,要回传(return)一个值,让WP核心再利用你回传的值来接着完成它该做的事。

透过你的干涉,修改了WP核心丢给你的参数,WP核心再接着拿你改过的参数,继续完成它该做的事,此动作就像”过滤“的动作,因而得名 filter。

比较Action Hook与Filter Hook的操作语法

比较一下两种 Hook 在埋进某处程式码时所用的语法,假设我们在某处 (可能是在输出页首的程式码处,或输出文章标题、文章内容、侧边栏…等地方,要”出现定制效果“的地方)埋下这两种 hook:

  1. /*--------------- Action Hook ---------------*/
  2. // 埋下一个名叫'do_more'的action hook
  3.  
  4. do_action('do_more');
  5.  
  6. /*--------------- Filter Hook ---------------*/
  7. // 埋下一个名叫'get_special'的filter hook,注意它会有回传值
  8.  
  9. $c = apply_filters('get_special',$a, $b);

然后我们可以在某处 (可能是其他插件、functions.php 等处,要”实现定制功能“的地方)添加对应的 hook function:

  1. /*--------------- Action Hook Function---------------*/
  2. // 增加要钩上'do_more'这个hook的hook function,
  3. // 并为此hook function取名叫more_func。
  4. // 第一个参数是hook名称、第二个是hook function名称
  5.  
  6. add_action('do_more', 'more_func');
  7.  
  8. // 添加more_func的內容,不需回传值
  9.  
  10. function more_func()
  11. {
  12.     echo 'do more thing...';
  13. }
  14.  
  15. /*--------------- Filter Hook Function ---------------*/
  16. // 增加要钩上'get_special' hook的hook function,
  17. //并为此hook function取名叫special_func。
  18. // 参数1是hook名称、参数2是hook function名称
  19. // 参数3是Priority(优先序)、参数4是hook function参数的数目
  20.  
  21. add_filter('get_special', 'special_func', 10, 2);
  22.  
  23. // 添加special_func的內容,需要給它回传值
  24.  
  25. function special_func($a, $b)
  26. {
  27.     $c = $a.' & '.$b; //做一些事,例如把两个参数连接起來
  28.     return $c; //回传值
  29. }

所以其实两种 Hook 的运作方式几乎一样,只差在增加 Action Hook 函式不需回传值,而增加 Filter Hook function时,你必须要回传一个值。所以 Filter Hook 函式通常都有提供参数,让想定制的人可以取得它,处理后再回传。

但如果有一个 Filter Hook,它没有任何 hook function 有去钩它,它该怎么取得回传值?答案是,直接拿第一个它给的参数,以上面的例子来说,它会直接拿 $a 丢进 $c 。另外,其实我们也可以把 filter 写的跟 action 一样,只要不回传值就行,但 action hook 就没办法”模仿“ filter hook,因为无法取得回传值。

Hook Function的优先序(Priority)

如果有很多地方(主题或者插件的 functions.php)都 添加同一个 hook ,会怎么决定出现顺序?答案很显然是可以透过 Hook Function 的 Priority 参数来作优先序的设定:

就像我们刚才说明的例子中,我们使用 add_filter 加入 special_func 时设定的优先序是 10 ,这也是 Priority 参数的预设值。如果你希望它能优先被执行,就设定小于 10 的数字,反之,就设个 100、500 之类的,让它延后被执行。

但其实这里有个隐含的冲突问题。

以 wp_head 这个 hook 为例,如果我写了一个插件,希望透过 wp_head 来输出”增加 a.css 档案“的 HTML 语法,而 a.css 会重新设定 body 元素的样式,所以我希望它可以最后才被汇入,不要被其他 css 档干扰,于是我将 Priority 设为 900,但我怎么知道 Priority 900 够不够大?若某个WP网站,它除了安装我的插件,也安装了其他插件,而其他插件刚好也重新设定body元素的样式,然后把 Priority 设为 950,此时我写的插件在处理 body 样式时就出事了,于是就跟其他插件冲突了。

所以此时我们需要了解的是:我的WP网站可能装了很多插件,我怎么知道同一个 Hook 被加了多少 Hook Function,而每个 Hook Function 的 Priority 被设定为多少?

答案是,我们可以透过 $wp_filters 这个 global 变数来取得所有 hook 的信息,像是如下的 function:

  1. // 列出所有的hook function及其priority
  2.  
  3. function list_hooked_functions($tag=false)
  4. {
  5.     global $wp_filter;
  6.  
  7.     if ($tag) 
  8.     {
  9.         $hook[$tag]=$wp_filter[$tag];
  10.         if (!is_array($hook[$tag])) 
  11.         {
  12.             trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
  13.             return;
  14.         }
  15.     }
  16.     else
  17.     {
  18.         $hook=$wp_filter;
  19.         ksort($hook);
  20.     }
  21.  
  22.     echo '<pre>';
  23.     foreach($hook as $tag => $priority)
  24.     {
  25.         echo "<br />>>>>>\t<strong>$tag</strong><br />";
  26.         ksort($priority);
  27.         foreach($priority as $priority => $function)
  28.         {
  29.             echo $priority;
  30.             foreach($function as $name => $properties) echo "\t$name<br />";
  31.         }
  32.     }
  33.     echo '</pre>';
  34.     return;
  35. }

当我们呼叫 list_hooked_functions('wp_head'); 时,就会列出 wp_head 这个 Hook 所钩住的所有 hook function,可以看到 priority 10 之后有好几个都没有数字,因为它们都没有特别指定 priority,所以都是 10 ,包括我们刚才写的 print_sth 也在其中:

>>>>>    wp_head

1 wp_enqueue_scripts

2 feed_links

3 feed_links_extra

8 wp_print_styles

9 wp_print_head_scripts

10 rsd_link

wlwmanifest_link

index_rel_link

parent_post_rel_link

start_post_rel_link

adjacent_posts_rel_link_wp_head

locale_stylesheet

wp_generator

rel_canonical

wp_shortlink_wp_head

print_sth

wp_admin_bar_header

_admin_bar_bump_cb

所以,冲突很难提早避免,但发生冲突时,可以预先思考有没有可能是因为 priority 的设定,导致结果跟预期不符合。

原文出自:http://www.mrmu.com.tw/2011/10/10/wordpress-hook/ ,倡萌整编。

 
 

Hook机制里登场的角色的更多相关文章

  1. 黄聪:WordPress 的 Hook 机制与原理(add_action、add_filter)

    稍有接触过 WordPress 主题或插件制作修改的朋友,对 WordPress 的Hook机制应该不陌生,但通常刚接触WordPress Hook 的新手,对其运作原理可能会有点混乱或模糊.本文针对 ...

  2. Android插件化原理解析——Hook机制之动态代理

    转自 http://weishu.me/2016/01/28/understand-plugin-framework-proxy-hook/ 使用代理机制进行API Hook进而达到方法增强是框架的常 ...

  3. Libco Hook 机制浅析

    Libco Hook 机制浅析 之前的文章里我们提到过 Libco 有一套 Hook 机制,可以通过协程的让出(yield)原语将系统的阻塞系统调用改造为非阻塞的,这篇文章我们将深入解析 Hook 机 ...

  4. 【repost】JS中的hook机制

    hook机制也就是钩子机制,由表驱动实现,常用来处理多种特殊情况的处理.我们预定义了一些钩子,在常用的代码逻辑中去适配一些特殊的事件,这样可以让我们少些很多if else语句.举个高考加分的例子,比如 ...

  5. 插件开发之360 DroidPlugin源码分析(二)Hook机制

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52124397 前言:新插件的开发,可以说是为插件开发者带来了福音,虽然还很多坑要填补, ...

  6. PostgreSQL的hook机制初步学习

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面:PostgreSQL内部结构与源代码研究索引页    回到顶级页面:PostgreSQL索引页 本文的目的一是为了备忘,二是为了抛砖引玉,希望 ...

  7. 浅谈svn的hook机制

    一.什么是钩子 所谓svn的hook机制,就是用户在管理数据仓库的时候,当特定的事件发生时,相应的hook会被调用,hook 其实就相当于特定事件的处理函数. 当前 Subversion 提供了5种可 ...

  8. Nova中的Hook机制

    Nova的代码中支持Hook机制,也就是在某些函数的前后,可以加入自己的代码逻辑.Hook代码可以完全独立于Nova开发,本质上使用setuptools的entry points机制.K版本的Open ...

  9. thinkphp5 自动注册Hook机制钩子扩展

    Hook.php 文件已更新1.修复在linux环境下类的 \ 在basename 下无法获取到类名的问题2.修复linux 环境下无法使用hook::call 调用失败问题 请先安装thinkphp ...

随机推荐

  1. Production环境中iptables常用参数配置

    production环境中iptables常用参数配置 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我相信在实际生产环境中有很多运维的兄弟跟我一样,很少用到iptables的这个 ...

  2. 个人训练记录-赛码"bestcoder"杯中国大学生程序设计冠军赛

    A.Movie 题意是给n个线段,要求求出是否存在三个不相交的线段,是的话输出yes,否则输出no.根据贪心的想法,可以先找出右端点r'最小的线段,他是三条线段中最左的那条,再找出左端点l'最大的线段 ...

  3. 00Linux学习及角色定义

    一.嵌入式Linux学习顺序 二.Linux架构 三.Linux工程师角色划分 四. 嵌入式应用工程师工作内容与所需知识点 从图 1可以知道, 嵌入式应用工程师主要从事与产品相关的嵌入式 Linux ...

  4. Overview of the Oppia codebase(Oppia代码库总览)

    Oppia is built with Google App Engine. Its backend is written in Python, and its frontend is written ...

  5. final发布评论

    在刚刚过去的final发布会上,各小组都展示了自己团队两个月来的团队成果.以下是我对各小组的发布 过程和产品展示的评论. 1.金州勇士团队 金州勇士团队的作品是在线考试系统.从产品的角度来说,相比于b ...

  6. HTML5&CSS3练习笔记(一)

    属性选择器的用法 格式:[属性/^/*/$=值] 1.[attr=val] 匹配指定值的元素 <div> <div id="section1"> 完全匹配元 ...

  7. myeclipse 开发环境下,提示 String cannot be resolved to a type

    从SVN上下载项目,所有代码都有下来,在本地编译,java类总是报出n多红叉,惨不忍睹,String cannot be resolved to a type都出来了,以为是jar包未被加载,于是pr ...

  8. Java 邮件发送

    <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId&g ...

  9. C语言 结构体指针赋值 incompatible types when assigning to type 'char[20]' from type 'char *'

    strcpy(pstudent->name, "guo zhao wei "); 为什么错误,该怎么写,(红色行)     追问 为什么不能直接赋值啊, 追答 用char n ...

  10. 【转载】Log4j详细使用教程

    本文由林炳文Evankaka创作,出处http://blog.csdn.net/evankaka 日志是应用软件中不可缺少的部分,Apache的开源项目Log4j是一个功能强大的日志组件,提供方便的日 ...