各位观众老爷们,晚上好!我是今天的主讲人,江湖人称代码老司机。今天咱们不飙车,咱们聊聊 WordPress 里的一个“老司机”级的函数:do_shortcode()
。这玩意儿,看似简单,实则蕴含着 WordPress 的灵魂,是理解 WordPress 如何处理内容的关键。
一、 短代码是个什么鬼?
首先,咱们得明白什么是短代码。如果你是 WordPress 老手,这部分可以直接跳过。但为了照顾新手,我还是啰嗦两句。
短代码,顾名思义,就是简短的代码。它允许你在文章、页面或者小工具中嵌入一些复杂的功能,而无需编写大量的 HTML、CSS 或 JavaScript。你可以把它想象成一个快捷方式,指向了一段预先定义好的代码。
举个栗子:
这个短代码,如果你直接写 HTML,那得写多少图片标签啊!但是用了短代码,一行搞定,WordPress 会自动把 ID 为 1,2,3,4,5 的图片展示成一个画廊。
二、 do_shortcode()
:短代码的“翻译官”
do_shortcode()
函数,就是 WordPress 负责“翻译”这些短代码的。它的作用很简单:扫描一段文本,找到所有的短代码,然后执行它们对应的函数,最后把执行结果替换掉原来的短代码。
用人话来说,它就像一个翻译官,把 “” 翻译成了一堆 HTML 代码,然后替换掉原文中的短代码。
三、 源码剖析:do_shortcode()
的“内心世界”
好了,废话不多说,咱们直接上源码,看看 do_shortcode()
到底是怎么工作的。
首先,咱们找到 wp-includes/shortcodes.php
这个文件。这里面定义了 do_shortcode()
函数。
function do_shortcode( $content, $ignore_html = false ) {
global $shortcode_tags;
if ( false === strpos( $content, '[' ) ) {
return $content;
}
if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
return $content;
}
// 处理 HTML 编码实体,避免误解析
$content = preg_replace_callback( '/<([^>]*?)(?:>|$)/', 'shortcode_split_html_callback', $content );
// 处理短代码
$pattern = get_shortcode_regex();
$content = preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
//还原 HTML 编码实体
$content = preg_replace_callback( '/[/', 'shortcode_normalize_callback', $content );
$content = preg_replace_callback( '/]/', 'shortcode_normalize_callback', $content );
return $content;
}
咱们一步一步来分析:
-
初步判断:
if ( false === strpos( $content, '[' ) ) { return $content; } if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) { return $content; }
这两个
if
语句是用来做初步判断的。第一个判断是:如果内容中没有[
字符,那肯定没有短代码,直接返回原始内容。第二个判断是:如果$shortcode_tags
变量为空或者不是一个数组,那说明没有注册任何短代码,也直接返回原始内容。$shortcode_tags
是一个全局变量,用来存储所有注册的短代码及其对应的处理函数。 -
处理 HTML 编码实体
$content = preg_replace_callback( '/<([^>]*?)(?:>|$)/', 'shortcode_split_html_callback', $content );
这行代码使用正则表达式和回调函数
shortcode_split_html_callback
来处理 HTML 编码实体。 这样做是为了防止 HTML 标签中的尖括号干扰短代码的解析。shortcode_split_html_callback
函数会将 HTML 标签中的尖括号替换为编码实体,例如<
和>
,以便短代码解析器不会错误地将它们识别为短代码的一部分。 -
正则匹配:
$pattern = get_shortcode_regex(); $content = preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
这才是核心代码。首先,
get_shortcode_regex()
函数用来生成一个匹配短代码的正则表达式。然后,preg_replace_callback()
函数使用这个正则表达式来匹配内容中的所有短代码,并对每个匹配到的短代码调用do_shortcode_tag()
函数进行处理。get_shortcode_regex()
函数的代码如下:function get_shortcode_regex() { global $shortcode_tags; $tagnames = array_keys( $shortcode_tags ); $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcodes() // Also, see shortcode_unautop() and shortcode.js. return '\[' // Opening bracket . '(\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]] . "($tagregexp)" // 2: Shortcode name . '(?![\w-])' // Word boundary . '(' // 3: Unroll the attributes . '\s*' . '([^\/?>]+)' // Attributes . '\s*' . ')?' . '(\/?)' // 4: Optional closing slash . '\]' // Closing bracket . '(?:' . '\[' // Opening bracket . '\/' // Slash for closing shortcode . "($tagregexp)" // 5: Closing shortcode name . '\]' // Closing bracket . ')?'; // 6: Optional closing shortcode }
这个函数的作用是根据已经注册的短代码,生成一个复杂的正则表达式。这个正则表达式可以匹配各种形式的短代码,包括自闭合的短代码(
[tag /]
)和带闭合标签的短代码([tag]content[/tag]
)。do_shortcode_tag()
函数的代码如下:function do_shortcode_tag( $matches ) { global $shortcode_tags; // Allow [[foo]] syntax for escaping a tag if ( $matches[1] == '[' && $matches[6] == ']' ) { return substr( $matches[0], 1, -1 ); } $tag = $matches[2]; $attr = shortcode_parse_atts( $matches[3] ); if ( isset( $matches[5] ) ) { // enclosing tag return $matches[1] . call_user_func( $shortcode_tags[$tag], $attr, $matches[5], $tag ) . $matches[6]; } else { // self-closing tag return $matches[1] . call_user_func( $shortcode_tags[$tag], $attr, null, $tag ) . $matches[6]; } }
这个函数的作用是:
- 判断是否是转义的短代码(
[[tag]]
),如果是,则直接返回[tag]
。 - 获取短代码的标签名(
$tag
)和属性($attr
)。 - 判断是否是闭合标签,如果是,则调用对应的处理函数,并传入属性和内容。如果不是,则调用对应的处理函数,并传入属性和
null
作为内容。
shortcode_parse_atts()
函数的作用是解析短代码的属性。function shortcode_parse_atts( $text ) { $atts = array(); $pattern = '/(w+)s*=s*"([^"]*)"(?:s|$)|(w+)s*=s*'([^']*)'(?:s|$)|(w+)s*=s*([^s'"]+)(?:s|$)|"([^"]*)"(?:s|$)|(S+)(?:s|$)/'; $text = preg_replace( "/[x{00a0}x{200b}]+/u", " ", $text ); if ( preg_match_all( $pattern, $text, $match, PREG_SET_ORDER ) ) { foreach ( $match as $m ) { if ( ! empty( $m[1] ) ) $atts[ strtolower( $m[1] ) ] = stripcslashes( $m[2] ); elseif ( ! empty( $m[3] ) ) $atts[ strtolower( $m[3] ) ] = stripcslashes( $m[4] ); elseif ( ! empty( $m[5] ) ) $atts[ strtolower( $m[5] ) ] = stripcslashes( $m[6] ); elseif ( isset( $m[7] ) and $m[7] !== '' ) $atts[] = stripcslashes( $m[7] ); elseif ( isset( $m[8] ) ) $atts[] = stripcslashes( $m[8] ); } } else { $atts = ltrim( $text ); } return $atts; }
这个函数使用正则表达式来解析短代码的属性,并将属性存储在一个数组中。
- 判断是否是转义的短代码(
-
还原 HTML 编码实体
$content = preg_replace_callback( '/[/', 'shortcode_normalize_callback', $content ); $content = preg_replace_callback( '/]/', 'shortcode_normalize_callback', $content );
这两行代码将之前被替换的 HTML 编码实体还原回原始的尖括号。
shortcode_normalize_callback
函数简单地将编码实体替换回对应的字符。
四、 短代码的注册:add_shortcode()
光有 do_shortcode()
函数还不行,你还得告诉 WordPress 哪些短代码需要“翻译”,以及用什么函数来“翻译”。这就需要 add_shortcode()
函数了。
add_shortcode()
函数的作用是注册一个短代码,并将其与一个处理函数关联起来。
add_shortcode( string $tag, callable $callback ): bool
$tag
:短代码的标签名,比如gallery
。$callback
:处理函数,当遇到这个短代码时,会调用这个函数。
举个栗子:
function my_shortcode_handler( $atts, $content = null ) {
$atts = shortcode_atts( array(
'width' => '200',
'height' => '200',
), $atts );
$width = intval( $atts['width'] );
$height = intval( $atts['height'] );
$output = '<img src="http://example.com/image.jpg" width="' . $width . '" height="' . $height . '" />';
return $output;
}
add_shortcode( 'my_image', 'my_shortcode_handler' );
这段代码注册了一个名为 my_image
的短代码。当你在文章中使用 [my_image width="300" height="400"]
时,WordPress 会调用 my_shortcode_handler()
函数,并将 width
和 height
属性传递给它。
shortcode_atts()
函数的作用是将用户传入的属性与默认属性合并。
五、 嵌套短代码:do_shortcode()
的递归调用
do_shortcode()
函数支持嵌套短代码。这意味着你可以在一个短代码的处理函数中再次调用 do_shortcode()
函数,从而实现短代码的嵌套。
举个栗子:
function outer_shortcode_handler( $atts, $content = null ) {
$output = '<div>' . do_shortcode( $content ) . '</div>';
return $output;
}
add_shortcode( 'outer', 'outer_shortcode_handler' );
function inner_shortcode_handler( $atts, $content = null ) {
return '<span>Inner Content</span>';
}
add_shortcode( 'inner', 'inner_shortcode_handler' );
现在,如果你在文章中使用 [outer][inner][/inner][/outer]
,WordPress 会先调用 outer_shortcode_handler()
函数,然后在这个函数中调用 do_shortcode()
函数来处理 [inner][/inner]
短代码。最终的输出结果是 <div><span>Inner Content</span></div>
。
六、 总结:do_shortcode()
的核心流程
为了方便大家理解,我把 do_shortcode()
的核心流程总结成一个表格:
步骤 | 描述 | 涉及的函数 |
---|---|---|
1 | 初步判断:检查内容中是否包含短代码,以及是否注册了短代码。 | strpos() , empty() , is_array() |
2 | 处理 HTML 编码实体:防止 HTML 标签中的尖括号干扰短代码的解析。 | preg_replace_callback() , shortcode_split_html_callback() |
3 | 正则匹配:使用正则表达式匹配内容中的所有短代码。 | get_shortcode_regex() , preg_replace_callback() |
4 | 处理每个匹配到的短代码: | do_shortcode_tag() |
4.1 | 判断是否是转义的短代码,如果是,则直接返回。 | |
4.2 | 解析短代码的属性。 | shortcode_parse_atts() |
4.3 | 调用短代码对应的处理函数,并传入属性和内容。 | call_user_func() |
5 | 还原 HTML 编码实体:将之前被替换的 HTML 编码实体还原回原始的尖括号。 | preg_replace_callback() , shortcode_normalize_callback() |
6 | 返回处理后的内容。 |
七、 注意事项
- 性能问题:
do_shortcode()
函数使用正则表达式来匹配短代码,这可能会对性能产生影响。如果你的网站有很多短代码,或者内容很长,可以考虑使用缓存来提高性能。 - 安全问题: 短代码的处理函数可以执行任意代码,因此需要谨慎处理用户输入的属性,避免安全漏洞。
- 短代码的顺序: 短代码的执行顺序是不确定的,因此不要依赖短代码的执行顺序。
八、 总结
do_shortcode()
函数是 WordPress 的一个核心函数,它允许你在文章、页面或者小工具中嵌入复杂的功能,而无需编写大量的 HTML、CSS 或 JavaScript。理解 do_shortcode()
函数的工作原理,可以帮助你更好地理解 WordPress 的内容处理机制,并编写出更高效、更安全的短代码。
希望今天的讲座对大家有所帮助。如果有任何问题,欢迎在评论区留言。咱们下期再见!