这两天给网站换了一个不错的“文章目录”插件:ezTOC,然后无意中发现有些标题无法被正确跳转。查了半天,发现是跟 WordPress 的 wptexturize() 函数有关系。
一、WordPress 中的钩子与过滤器
钩子(hook)是 WordPress 中一种用来修改代码执行逻辑的机制。
1、首先在原来的代码中,使用 apply_filters($hook_name, ...)
或者 do_action($hook_name, ...)
,埋入一个钩子 hook_name,表示在此处执行这个 hook_name 关联的函数。
2、WordPress 自己,或者插件开发者,或者用户本身,可以通过 add_filter($hook_name, $callback, $priority, ...)
或 add_action($hook_name, $callback, $priority, ...)
向钩子 hook_name 注册自定义的 callback 函数(就叫它钩子函数吧)。一个钩子 hook 可以关联多个钩子函数。priority 表示这个函数在钩子中的执行优先级。然后当原来的代码执行到 apply_filters($hook_name)
或者 do_cation($hook_name)
时候,就会自动调用已经注册到 hook_name 下的所有 callback 函数。
WordPress 提供了两种类型的钩子,过滤器钩子(filter)与动作钩子(action)。二者的机制是一样的,只是目的有所不同。可以简单的理解为过滤器 filter 更倾向于对数据的修改、过滤(需要返回数据);动作 action 则倾向于对执行流程的修改(不需要返回数据)。
过滤器(filter)主要会用到的方法:
add_filter($hook_name, $callback, $priority, ...)
给过滤器钩子 hook_name 增加一个钩子函数 callback。
apply_filters($hook_name, ...)
执行过滤器钩子 hook_name。
remove_filter($hook_name, $callback)
从过滤器钩子 hook_name 下移除 callback 函数。
has_filter( string $hook_name, $callback)
判断过滤器钩子 hook_name 下面是否注册有 callback 函数。
动作(action) 同样的,主要有 add_action()
, do_action()
, remove_action(),
has_action()
这几个函数。
二、字符转义函数 wptexturize()
WordPress 内置函数 wptexturize($text)
会将文本中的特定字符进行转换,比如将横杠「-」转换成「–」,将三个横杠「—」转换成「—」,将三个点「…」转换成「…」。
但让我觉得有点烦人的就是,WordPress 默认会对几乎所有的文本进行 wptexturize 转义。可以在 wordpress/wp-includes/default-filters.php 文件中看到,WordPress 将 wptexturize()
函数注册到了很多过滤器钩子中。比如 the_content 钩子,是用来返回文章内容的,the_title 钩子,是用来返回文章标题的。
三、禁用 wptexturize
但是实际上,我是并不希望 WordPress 对我的内容进行转义的。所以我需要想办法把 WordPress 的这个默认行为禁用掉。
3.1 普遍但不太安全的禁用方式 ✗
早期的禁用方式,大家普遍都是采用 remove_filter()
函数,将 wptexturize()
从过滤器钩子中去掉。我也是如此。(甚至现在用中文关键字搜索 “wordpress 字符 转义”,出来的结果还几乎都是这种方法!)
在自定义主题的 functions.php,添加如下代码:
// 取消文章内容转义 remove_filter('the_content', 'wptexturize'); // 取消文章摘要转义 remove_filter('the_excerpt', 'wptexturize'); // 取消评论转义 remove_filter('comment_text', 'wptexturize');
直到这几天换了一个“文章目录”插件 ezTOC,发现它在生成目录的时候,对于一些标题中存在特殊符号的,会无法跳转。查了半天,才发现原来是跟我禁用 wptexturize()
冲突了。
因为它在匹配标题(heading)的时候,默认会应用 wptexturize()
进行转义。
然后在生成目录时候它使用的就是转义后的文本。并且会再次匹配文章中的 heading,将 heading 加上锚点。(类似将「<h1>这是标题</h1>」转换成「<h1><span class=’ezTOC’ id=’anchor_1′>这是标题</span></h>」这样子)
而我们文章实际输出的 heading 是取消转义的。这时候如果有一个标题里面有横杠「-」,它在最终生成标题锚点的时候就会失败。
3.2 更安全的禁用方式 ✓
查看 wptexturize()
的说明文档或者源码,会发现它其实提供了 $run_texturize = apply_filters( 'run_wptexturize', $run_texturize )
这个开关来允许全局禁用 texturize。
这时候,我们只需要在自定义主题的 functions.php 文件中使用如下代码:
// 全局禁用 wptexturize 转义 // __return_false 是 WordPress 内置的一个便捷函数,直接返回 false。 add_filter('run_wptexturize', '__return_false');
这样,不论是 WordPress 还是第三方插件,在任意地方调用 wptexturize()
方法,都不会进行转义了。
3.3 针对特定标签禁用 wptexturize
此外,wptexturize()
函数体中还有两个钩子可以用来禁用指定的 HTML 标签和 shortcode 标签([shortcode] 是 WordPress 自己提供的标签格式,用来供给第三方插件进行自动替换的)。
禁用指定的 HTML 标签:
apply_filters( 'no_texturize_tags', string[] $default_no_texturize_tags )
禁用指定的 [shortcode] 标签:
apply_filters( 'no_texturize_shortcodes', string[] $default_no_texturize_shortcodes )
如果只想针对某些特定 HTML 标签禁用 wptexturize,那么可以在 functions.php 中使用如下代码:
add_filter( 'no_texturize_tags', 'my_no_texturzie_tags' ); function my_no_texturzie_tags( $tags ) { $tags[] = 'blockquote'; // <blockquote> 标签 $tags[] = 'h1'; // <h1> 标签 return $tags; }