您的位置:首页 > 其它

Hook机制里登场的角色

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





Hook机制里登场的角色

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

WordPress核心

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

Hook

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

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

HookFunction

HookFunction里会有实质运作的内容,即是实作了一些定制功能,可能是存取DB、增加HTMLcode、执行其他函式等等。我们在HookFunction里写好所需的功能后,就可以利用“加入至对应Hook”的语法,把HookFunction自已钩到该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看成是”钩子“,而别的hookfunctions就能来鈎住它:





我们马上来写一个简单的例子。我们要写一个hookfunction,就叫它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的hookfunction,於是就找到我们写的print_sth(),然後就执行它,所以结果它做的事就会出现在网站上,也完成了”定制”的动作:





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

WordPress的ActionHook与FilterHook

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

ActionHook

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

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

FilterHook

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

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

比较ActionHook与FilterHook的操作语法

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

/*---------------ActionHook---------------*/


//埋下一个名叫'do_more'的actionhook




do_action('do_more');




/*---------------FilterHook---------------*/


//埋下一个名叫'get_special'的filterhook,注意它会有回传值




$c=apply_filters('get_special',$a,$b);


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

/*---------------ActionHookFunction---------------*/


//增加要钩上'do_more'这个hook的hookfunction,


//并为此hookfunction取名叫more_func。


//第一个参数是hook名称、第二个是hookfunction名称




add_action('do_more','more_func');




//添加more_func的內容,不需回传值




functionmore_func()


{


echo'domorething...';


}




/*---------------FilterHookFunction---------------*/


//增加要钩上'get_special'hook的hookfunction,


//并为此hookfunction取名叫special_func。


//参数1是hook名称、参数2是hookfunction名称


//参数3是Priority(优先序)、参数4是hookfunction参数的数目




add_filter('get_special','special_func',10,2);




//添加special_func的內容,需要給它回传值




functionspecial_func($a,$b)


{


$c=$a.'&'.$b;//做一些事,例如把两个参数连接起來


return$c;//回传值


}


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

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

HookFunction的优先序(Priority)

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

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

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

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

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

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

//列出所有的hookfunction及其priority




functionlist_hooked_functions($tag=false)


{


global$wp_filter;




if($tag)


{


$hook[$tag]=$wp_filter[$tag];


if(!is_array($hook[$tag]))


{


trigger_error("Nothingfoundfor'$tag'hook",E_USER_WARNING);


return;


}


}


else


{


$hook=$wp_filter;


ksort($hook);


}




echo'<pre>';


foreach($hookas$tag=>$priority)


{


echo"<br/>>>>>>\t<strong>$tag</strong><br/>";


ksort($priority);


foreach($priorityas$priority=>$function)


{


echo$priority;


foreach($functionas$name=>$properties)echo"\t$name<br/>";


}


}


echo'</pre>';


return;


}


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


>>>>>wp_head

1wp_enqueue_scripts

2feed_links

3feed_links_extra

8wp_print_styles

9wp_print_head_scripts

10rsd_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/,倡萌整编。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
章节导航