WordPress源码深度解析之:`WordPress`的`shortcode`:`add_shortcode()`和`do_shortcode()`的解析过程。

大家好!欢迎来到今天的WordPress源码深度解析讲座,我是你们的老朋友(虽然可能是第一次见),今天要聊聊WordPress里一个非常实用、但有时候又让人有点摸不着头脑的家伙:shortcode

别怕,shortcode其实就是WordPress提供的一个小工具,让你能在文章、页面甚至小工具里嵌入一些动态内容,而不用写一堆复杂的PHP代码。 今天我们就来扒一扒它的皮,重点解析add_shortcode()do_shortcode()这两个核心函数的工作原理。

Shortcode:让你的文章不再单调

想象一下,你想在文章里插入一个幻灯片,或者一个订阅表单。如果没有shortcode,你可能需要修改主题文件,或者使用复杂的插件。但是有了shortcode,你只需要在文章里写一个像[myslideshow]这样的标签,WordPress就会自动把它替换成幻灯片的HTML代码。是不是很方便?

add_shortcode():注册你的专属标签

首先,我们来看看add_shortcode()函数。它的作用就像给WordPress注册一个新的短代码标签。语法很简单:

add_shortcode( string $tag, callable $callback )
  • $tag: 这是你想要使用的短代码标签的名字,比如'myslideshow''subscribeform'等等。
  • $callback: 这是一个回调函数,当WordPress遇到你的短代码标签时,它会调用这个函数来生成最终的HTML代码。

举个例子,假设我们要创建一个简单的短代码,用来显示当前年份:

function my_current_year_shortcode() {
    return date('Y');
}
add_shortcode('currentyear', 'my_current_year_shortcode');

这段代码的意思是:

  1. 我们定义了一个名为my_current_year_shortcode()的函数,它返回当前的年份。
  2. 我们使用add_shortcode()函数将'currentyear'这个标签与my_current_year_shortcode()函数关联起来。

现在,你就可以在文章里写[currentyear],WordPress会自动把它替换成当前的年份。

深入add_shortcode()源码

add_shortcode()函数本身的代码并不复杂,它主要做的事情就是把标签和回调函数存储到一个全局变量里。我们来看一下它的简化版:

global $shortcode_tags;

if ( ! isset( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
    $shortcode_tags = array();
}

$shortcode_tags[ $tag ] = $callback;

这段代码做了以下几件事:

  1. 声明全局变量 $shortcode_tags$shortcode_tags 是一个数组,用来存储所有注册的短代码标签和对应的回调函数。
  2. 检查 $shortcode_tags 是否已经存在,如果不存在,就初始化一个空数组。
  3. $tag$callback 存储到 $shortcode_tags 数组中。

简单来说,add_shortcode() 就是一个注册表,把你的短代码标签和处理它的函数登记在案。

do_shortcode():找到标签,执行回调

接下来,我们来看看do_shortcode()函数。它的作用是扫描一段文本,找到所有的短代码标签,然后调用相应的回调函数来替换它们。

do_shortcode( string $content ) : string
  • $content: 这是你要扫描的文本,通常是文章的内容。
  • 返回值: 替换了所有短代码标签后的文本。

举个例子,假设你的文章内容是:"今天天气真好![currentyear]年真是个好年份!",那么调用do_shortcode()后,它会变成"今天天气真好!2023年真是个好年份!"(假设现在是2023年)。

深入do_shortcode()源码

do_shortcode() 函数的源码比较复杂,因为它需要处理各种情况,例如嵌套的短代码、带有属性的短代码等等。我们来看一下它的简化版,重点关注它是如何找到标签并执行回调的:

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 );

这段代码做了以下几件事:

  1. 检查全局变量 $shortcode_tags 是否为空,如果为空,说明没有注册任何短代码,直接返回原始文本。
  2. 调用 get_shortcode_regex() 函数获取用于匹配短代码的正则表达式。
  3. 使用 preg_replace_callback() 函数,根据正则表达式查找文本中的短代码标签,并调用 do_shortcode_tag() 函数来处理每一个匹配到的短代码标签。

get_shortcode_regex():正则表达式的艺术

get_shortcode_regex() 函数的作用是生成一个用于匹配短代码的正则表达式。这个正则表达式需要能够匹配各种形式的短代码,例如:

  • [currentyear]
  • [myslideshow id="123"]
  • [subscribeform title="订阅"]内容[/subscribeform]

这个函数的代码比较复杂,我们就不在这里展开了。但是你需要知道的是,它生成了一个强大的正则表达式,可以处理各种复杂的短代码形式。

do_shortcode_tag():真正干活的函数

do_shortcode_tag() 函数是真正干活的函数。它接收 preg_replace_callback() 函数传递过来的匹配结果,然后根据标签名找到对应的回调函数,并执行它。我们来看一下它的简化版:

global $shortcode_tags;

$tag = $matches[2]; // 获取标签名
$atts = shortcode_parse_atts( $matches[3] ); // 解析属性
$content = $matches[5]; // 获取短代码的内容(如果有)

if ( isset( $shortcode_tags[ $tag ] ) ) {
    $callback = $shortcode_tags[ $tag ]; // 获取回调函数
    if ( is_callable( $callback ) ) {
        return call_user_func( $callback, $atts, $content, $tag ); // 执行回调函数
    }
}

return $matches[0]; // 如果没有找到对应的回调函数,则返回原始的短代码标签

这段代码做了以下几件事:

  1. $matches 数组中获取标签名、属性和内容。$matches 数组是 preg_replace_callback() 函数传递过来的匹配结果。
  2. 使用 shortcode_parse_atts() 函数解析属性。
  3. $shortcode_tags 数组中查找与标签名对应的回调函数。
  4. 如果找到了回调函数,并且它是一个可调用的函数,则使用 call_user_func() 函数执行它,并将属性、内容和标签名作为参数传递给它。
  5. 如果找不到对应的回调函数,则返回原始的短代码标签。

Shortcode的属性(Attributes)

Shortcode可以带属性,就像HTML标签一样。例如:[myslideshow id="123" width="600"]。在你的回调函数中,你可以通过 $atts 参数来访问这些属性。

function my_slideshow_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'id'    => '1',
            'width' => '400',
        ),
        $atts,
        'myslideshow'
    );

    $id = intval( $atts['id'] );
    $width = intval( $atts['width'] );

    // 根据 $id 和 $width 生成幻灯片的 HTML 代码
    $html = '<div class="slideshow" style="width: ' . $width . 'px;">';
    $html .= '幻灯片ID:' . $id;
    $html .= '</div>';

    return $html;
}
add_shortcode('myslideshow', 'my_slideshow_shortcode');

在这个例子中,我们使用了 shortcode_atts() 函数来定义默认的属性值,并且将用户传入的属性值合并到默认值中。shortcode_atts() 函数可以确保你的代码在没有指定属性值时也能正常工作。

嵌套的Shortcode

Shortcode可以嵌套,就像HTML标签一样。例如:

[outer]
  [inner]Inner Content[/inner]
[/outer]

要处理嵌套的shortcode,你需要在你的回调函数中递归调用do_shortcode()函数。

function outer_shortcode( $atts, $content = null ) {
    $content = do_shortcode($content); // 处理嵌套的 shortcode
    return '<div class="outer">' . $content . '</div>';
}
add_shortcode('outer', 'outer_shortcode');

function inner_shortcode( $atts, $content = null ) {
    return '<div class="inner">' . $content . '</div>';
}
add_shortcode('inner', 'inner_shortcode');

在这个例子中,outer_shortcode() 函数调用了 do_shortcode() 函数来处理 $content 变量中的嵌套的 shortcode。

Shortcode的优先级

如果你注册了多个 shortcode,它们可能会发生冲突。WordPress 允许你通过调整 shortcode 的优先级来解决冲突。但是默认情况下,所有 shortcode 的优先级都是 10。你可以使用 remove_shortcode() 函数来移除一个 shortcode,然后再使用 add_shortcode() 函数来重新注册它,并指定一个新的优先级。

Shortcode的安全问题

Shortcode 可能会带来安全问题,因为它们允许用户在文章中执行代码。为了避免安全问题,你应该始终对用户输入进行验证和过滤,并且避免在 shortcode 中执行敏感操作。

总结

add_shortcode()do_shortcode() 是 WordPress 短代码功能的核心。add_shortcode() 函数用于注册短代码标签和对应的回调函数,而 do_shortcode() 函数用于扫描文本,找到短代码标签,并调用相应的回调函数来替换它们。了解这两个函数的工作原理,可以帮助你更好地使用 WordPress 的短代码功能,并且避免一些常见的错误。

一个表格总结 add_shortcode() 和 do_shortcode()

函数名 作用 参数 返回值
add_shortcode() 注册一个短代码标签和对应的回调函数 $tag (string): 短代码标签名,例如 ‘currentyear’。
$callback (callable): 处理短代码的回调函数。这个函数接收三个参数:
$atts (array): 短代码的属性,如果没有属性,则为空数组。
$content (string): 短代码的内容,如果有的话。如果没有内容,则为 null。
$tag (string): 短代码的标签名。
do_shortcode() 扫描文本,找到短代码标签,并调用相应的回调函数来替换它们 $content (string): 要扫描的文本。 $content (string): 替换了所有短代码标签后的文本。如果没有找到任何短代码标签,则返回原始的文本。

一些使用建议

  • 命名规范: 给你的短代码标签起一个有意义的名字,避免和其他插件或主题的短代码冲突。
  • 属性默认值: 使用 shortcode_atts() 函数来定义属性的默认值,确保你的代码在没有指定属性值时也能正常工作。
  • 安全第一: 对用户输入进行验证和过滤,避免安全问题。
  • 性能优化: 避免在短代码中执行耗时的操作,例如数据库查询。

好了,今天的讲座就到这里。希望通过今天的讲解,大家对 WordPress 的 shortcode 有了更深入的了解。记住,shortcode 是一个强大的工具,但是也要谨慎使用。 下次有机会再和大家分享其他的WordPress源码知识! 祝大家编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注