各位代码爱好者,大家好!我是你们今天的短代码解剖师,准备好了吗?咱们要深入 do_shortcode()
这个WordPress世界里的小小却强大的函数,看看它如何像一位精明的厨师,将那些看似简单的短代码标签,烹饪成美味的功能大餐。
第一幕:短代码的舞台
想象一下,短代码就像舞台上的演员,它们在文章或页面内容中占据着位置,等待着被激活,然后执行特定的任务。do_shortcode()
就是那个舞台监督,它负责寻找这些演员,并指导他们完成表演。
首先,我们需要明确什么是短代码。简单来说,短代码就是用方括号包裹起来的标签,例如 或
[contact-form]
。这些标签可以接受参数,例如 。WordPress允许开发者注册自己的短代码,并将它们与特定的PHP函数关联起来。当
do_shortcode()
遇到一个短代码时,它会调用与之关联的函数,并将短代码的属性传递给这个函数。
第二幕:do_shortcode()
的源代码剖析
让我们来看看 do_shortcode()
的源代码(基于 WordPress 6.4.3,为了简化讲解,我省略了一些不常用的功能和注释):
function do_shortcode( $content ) {
global $shortcode_tags;
if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
return $content;
}
$pattern = get_shortcode_regex();
return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
}
这段代码看起来很简单,但它却隐藏着很多细节。让我们一步一步地拆解它:
-
检查短代码标签是否存在:
global $shortcode_tags; if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) { return $content; }
这段代码首先声明
$shortcode_tags
为全局变量。$shortcode_tags
是一个关联数组,它存储了所有已注册的短代码标签及其对应的处理函数。如果这个数组为空或不是数组,那么说明没有任何短代码被注册,函数直接返回原始的内容,不做任何处理。这就像如果舞台上没有演员,舞台监督就直接回家了。 -
构建正则表达式:
$pattern = get_shortcode_regex();
get_shortcode_regex()
函数负责生成一个正则表达式,用于匹配短代码标签。这个正则表达式非常复杂,但它的作用是识别各种形式的短代码,包括带属性的短代码、自闭合短代码等等。我们稍后会详细分析get_shortcode_regex()
的工作原理。 -
执行替换:
return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
这行代码是
do_shortcode()
的核心。它使用preg_replace_callback()
函数,将所有匹配到的短代码标签替换为它们经过处理后的内容。preg_replace_callback()
函数接受三个参数:"/$pattern/s"
: 这是用于匹配短代码的正则表达式。s
修饰符表示点号 (.
) 可以匹配换行符,这对于处理包含换行符的短代码内容非常重要。'do_shortcode_tag'
: 这是一个回调函数,当正则表达式匹配到一个短代码时,preg_replace_callback()
会调用这个函数来处理匹配到的短代码。$content
: 这是要进行短代码处理的原始内容。
preg_replace_callback()
会在$content
中查找所有匹配$pattern
的短代码,然后对每一个匹配到的短代码,调用do_shortcode_tag()
函数进行处理,最后将$content
中所有匹配到的短代码替换为do_shortcode_tag()
函数返回的结果。
第三幕:get_shortcode_regex()
的正则表达式魔法
get_shortcode_regex()
函数是构建用于匹配短代码的正则表达式的关键。 让我们看一下简化后的代码:
function get_shortcode_regex( $tagnames = null ) {
global $shortcode_tags;
if ( empty( $tagnames ) ) {
$tagnames = array_keys( $shortcode_tags );
}
$tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) );
// WARNING! Do not change this regex without changing do_shortcode_tag() and the shortcode parsing.
return
'\[' // Opening bracket
. '(\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]]
. "($tagregexp)" // 2: Shortcode name
. '(?![\w-])' // Word boundary
. '(' // 3: Unroll the loop: Inside the opening shortcode tag
. '[^\]\/]*' // Not a closing bracket or forward slash
. '(?:'
. '\/(?!\])' // Not a closing bracket
. '[^\]\/]*' // Not a closing bracket or forward slash
. ')*?'
. ')'
. '(?:'
. '(\/)' // 4: Self closing tag ...
. '\]' // ... and closing bracket
. '|'
. '\]' // Closing bracket
. '(?:'
. '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags
. '[^\[]*+' // Not an opening bracket
. '(?:'
. '\[(?!\/\2\])' // An opening bracket not followed by the closing shortcode tag
. '[^\[]*+' // Not an opening bracket
. ')*+'
. ')'
. '\[\/\2\]' // Closing shortcode tag
. ')?'
. ')'
. '(\]?)'; // 6: Optional second closing brocket for escaping shortcodes: [[tag]]
}
这段代码的作用是根据已注册的短代码标签,生成一个正则表达式。这个正则表达式可以匹配各种形式的短代码,包括:
[tag]
:简单的短代码[tag attribute="value"]
:带有属性的短代码[tag]content[/tag]
:带有内容的短代码[tag /]
:自闭合短代码[[tag]]
:被转义的短代码(不会被解析)
让我们来分解一下这个正则表达式:
\[
: 匹配一个左方括号。(\[?)
: 匹配一个可选的左方括号。用于处理转义的短代码,例如[[tag]]
。($tagregexp)
: 匹配短代码的标签名。$tagregexp
是一个由所有已注册的短代码标签组成的正则表达式,例如(gallery|contact-form)
。(?![\w-])
: 确保短代码标签名后面不是字母、数字或短划线。这是一个词语边界的判断。([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)
: 匹配短代码的属性。(?:(\/)\]|\](?:([^\[]*(?:\[(?!\/\2\])[^\[]*)*)\[\/\2\])?)
: 匹配短代码的闭合标签。(\]?)
: 匹配一个可选的右方括号。用于处理转义的短代码,例如[[tag]]
。
这个正则表达式非常复杂,但它的作用是准确地识别各种形式的短代码。理解这个正则表达式对于理解 do_shortcode()
的工作原理至关重要。
第四幕:do_shortcode_tag()
的幕后工作
do_shortcode_tag()
函数是真正执行短代码逻辑的地方。当 preg_replace_callback()
找到一个匹配的短代码时,它会调用 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 ( empty( $shortcode_tags[ $tag ] ) ) {
return $matches[0];
}
return call_user_func( $shortcode_tags[ $tag ], $attr, $matches[5], $tag );
}
让我们一步一步地分析这段代码:
-
处理转义的短代码:
if ( $matches[1] == '[' && $matches[6] == ']' ) { return substr( $matches[0], 1, -1 ); }
这段代码检查是否是转义的短代码,例如
[[tag]]
。如果是,它会移除外层的方括号,并返回转义后的内容。 -
提取短代码标签和属性:
$tag = $matches[2]; $attr = shortcode_parse_atts( $matches[3] );
这段代码从
$matches
数组中提取短代码的标签名和属性。$matches
数组是preg_replace_callback()
函数传递给do_shortcode_tag()
函数的,它包含了正则表达式匹配到的所有信息。$matches[2]
包含了短代码的标签名,$matches[3]
包含了短代码的属性字符串。shortcode_parse_atts()
函数负责解析属性字符串,将其转换为一个关联数组。 -
检查短代码是否已注册:
if ( empty( $shortcode_tags[ $tag ] ) ) { return $matches[0]; }
这段代码检查短代码的标签名是否在
$shortcode_tags
数组中存在。如果不存在,说明这个短代码没有被注册,函数直接返回原始的短代码标签,不做任何处理。 -
调用短代码的处理函数:
return call_user_func( $shortcode_tags[ $tag ], $attr, $matches[5], $tag );
这行代码是
do_shortcode_tag()
的核心。它使用call_user_func()
函数调用与短代码标签关联的处理函数。$shortcode_tags[ $tag ]
包含了处理函数的名称。call_user_func()
函数接受一个回调函数和一组参数,它会调用这个回调函数,并将参数传递给它。传递给处理函数的参数包括:
$attr
: 短代码的属性数组。$matches[5]
: 短代码的内容(如果存在)。$tag
: 短代码的标签名。
处理函数会根据这些参数,执行特定的逻辑,并返回处理后的内容。
do_shortcode_tag()
函数会将这个内容返回给preg_replace_callback()
函数,然后preg_replace_callback()
函数会将原始的短代码标签替换为处理后的内容。
第五幕:shortcode_parse_atts()
的属性解析
shortcode_parse_atts()
函数负责将短代码的属性字符串解析为一个关联数组。例如,对于短代码 ,
shortcode_parse_atts()
函数会将属性字符串 ids="1,2,3" columns="4"
解析为以下数组:
array(
'ids' => '1,2,3',
'columns' => '4'
)
让我们看一下 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|$)|(w+)(?:s|$)/';
$text = trim( $text );
if ( empty( $text ) ) {
return $atts;
}
preg_match_all( $pattern, $text, $match, PREG_SET_ORDER );
if ( empty( $match ) ) {
return $atts;
}
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] ) && $m[7] !== '' ) {
$atts[] = stripcslashes( $m[7] );
} elseif ( isset( $m[9] ) ) {
$atts[] = stripcslashes( $m[9] );
} elseif ( isset( $m[10] ) ) {
$atts[] = strtolower( $m[10] );
}
}
return $atts;
}
这段代码使用正则表达式来匹配各种形式的属性,包括:
attribute="value"
attribute='value'
attribute=value
"value"
'value'
attribute
preg_match_all()
函数会将所有匹配到的属性信息存储在 $match
数组中。然后,代码遍历 $match
数组,将属性名和属性值添加到 $atts
数组中。stripcslashes()
函数用于移除属性值中的反斜杠。
第六幕:一个完整的例子
让我们通过一个完整的例子来演示 do_shortcode()
的工作原理。
假设我们定义了一个名为 [greeting]
的短代码,它的作用是显示一个问候语。我们使用以下代码注册这个短代码:
function greeting_shortcode( $atts, $content = null ) {
$atts = shortcode_atts(
array(
'name' => 'World',
),
$atts,
'greeting'
);
return 'Hello, ' . esc_html( $atts['name'] ) . '!';
}
add_shortcode( 'greeting', 'greeting_shortcode' );
这段代码首先定义了一个名为 greeting_shortcode()
的函数,这个函数是 [greeting]
短代码的处理函数。然后,它使用 add_shortcode()
函数将 [greeting]
短代码与 greeting_shortcode()
函数关联起来。
greeting_shortcode()
函数接受两个参数:
$atts
: 短代码的属性数组。$content
: 短代码的内容(如果存在)。
在这个例子中,我们使用了 shortcode_atts()
函数来定义短代码的默认属性。shortcode_atts()
函数接受三个参数:
$defaults
: 默认属性数组。$atts
: 短代码的属性数组。$shortcode
: 短代码的标签名。
shortcode_atts()
函数会将 $atts
数组与 $defaults
数组合并,并返回合并后的数组。如果 $atts
数组中没有包含 $defaults
数组中的属性,那么 $defaults
数组中的属性将被添加到 $atts
数组中。
在这个例子中,我们定义了一个名为 name
的默认属性,它的值为 World
。这意味着,如果我们在使用 [greeting]
短代码时没有指定 name
属性,那么 name
属性的值将默认为 World
。
greeting_shortcode()
函数的最后一行代码使用 esc_html()
函数来转义 name
属性的值,以防止 XSS 攻击。然后,它返回一个包含问候语的字符串。
现在,假设我们在文章中使用以下短代码:
[greeting name="John"]
当 WordPress 解析这篇文章时,它会调用 do_shortcode()
函数来处理短代码。do_shortcode()
函数会找到 [greeting name="John"]
短代码,并调用 greeting_shortcode()
函数。
greeting_shortcode()
函数会接收到以下参数:
$atts = array(
'name' => 'John'
);
$content = null;
greeting_shortcode()
函数会使用 shortcode_atts()
函数将 $atts
数组与默认属性数组合并。由于 $atts
数组中已经包含了 name
属性,因此 shortcode_atts()
函数不会修改 $atts
数组。
然后,greeting_shortcode()
函数会使用 esc_html()
函数来转义 name
属性的值,并将转义后的值插入到问候语字符串中。最后,greeting_shortcode()
函数会返回以下字符串:
Hello, John!
do_shortcode()
函数会将原始的短代码 [greeting name="John"]
替换为 Hello, John!
,并在文章中显示这个问候语。
第七幕:总结与彩蛋
do_shortcode()
函数是 WordPress 中一个非常重要的函数,它负责解析和执行短代码。理解 do_shortcode()
函数的工作原理对于开发 WordPress 插件和主题至关重要。
希望通过今天的讲解,大家对 do_shortcode()
函数有了更深入的了解。记住,短代码就像舞台上的演员,do_shortcode()
就像舞台监督,它们一起为我们呈现精彩的功能表演。下次遇到复杂的短代码问题时,不妨回到这篇文章,重新梳理一下思路,相信你一定能够找到解决方案。
彩蛋:
remove_all_shortcodes()
函数可以移除所有已注册的短代码。strip_shortcodes()
函数可以移除字符串中的所有短代码标签,但不执行它们。- 短代码可以嵌套使用,但需要注意避免无限循环。
感谢大家的聆听,希望今天的讲座对您有所帮助!