深入解读 WordPress `add_shortcode()` 函数源码:短代码及其处理函数的存储机制。

各位观众,晚上好!我是今天的主讲人,咱们今晚来唠唠 WordPress 里一个相当重要的小家伙——add_shortcode() 函数。别看它名字挺短,作用可不小,它可是短代码(Shortcode)的灵魂人物,负责把你的自定义标签和 PHP 函数联系起来,让你的 WordPress 内容更加灵活。

今天咱们不光要讲怎么用 add_shortcode(),还要深入它的源码,看看它到底是怎么把这些短代码和处理函数“撮合”到一起的,以及背后藏着哪些精巧的设计。准备好了吗?咱们这就开始!

一、什么是短代码 (Shortcode)?

先给不熟悉短代码的观众老爷们简单科普一下。短代码,顾名思义,就是一段简短的代码,通常用方括号包裹,比如 [my_shortcode]。它允许你在 WordPress 的文章、页面或者小工具里嵌入复杂的功能,而无需编写大量的 HTML 或者 PHP 代码。

想象一下,你想要在每篇文章的末尾插入一个自定义的作者信息框,包含作者的头像、姓名和简介。如果没有短代码,你可能需要在每个文章的模板里手动添加这段代码,或者使用插件。但有了短代码,你只需要定义一个 [author_info] 短代码,并在文章中插入它,WordPress 就会自动调用相应的 PHP 函数来生成作者信息框。是不是方便多了?

二、add_shortcode() 的基本用法

add_shortcode() 函数的语法很简单:

add_shortcode( string $tag, callable $callback )
  • $tag: 短代码的名称,也就是方括号里的内容,比如 "my_shortcode"
  • $callback: 一个 PHP 函数或者方法,当 WordPress 遇到这个短代码时,就会调用这个函数来处理它。这个函数必须返回一个字符串,这个字符串将被替换到短代码的位置。

举个栗子:

<?php
/**
 *  这是一个处理 [greeting] 短代码的函数
 */
function greeting_shortcode_handler( $atts, $content = null ) {
  // $atts 是一个关联数组,包含短代码的属性
  // $content 是短代码的内容,如果短代码是封闭的,比如 [greeting]Hello[/greeting],那么 $content 就是 "Hello"

  $name = isset( $atts['name'] ) ? esc_attr( $atts['name'] ) : 'World'; // 如果有 name 属性,就用它,否则用 "World"

  return '<p>Hello, ' . $name . '!</p>';
}

add_shortcode( 'greeting', 'greeting_shortcode_handler' );
?>

这段代码定义了一个名为 greeting 的短代码,当你在文章中使用 [greeting name="Alice"] 时,WordPress 会调用 greeting_shortcode_handler 函数,并将 name="Alice" 作为 $atts 参数传递给它。函数返回 <p>Hello, Alice!</p>,这个字符串会替换掉 [greeting name="Alice"]

三、add_shortcode() 源码剖析

现在,咱们深入 add_shortcode() 的源码,看看它到底是怎么工作的。add_shortcode() 函数位于 wp-includes/shortcodes.php 文件中。

function add_shortcode( $tag, $callback ) {
    global $shortcode_tags;

    if ( is_callable( $callback ) ) {
        $shortcode_tags[ $tag ] = $callback;
    }
}

这段代码的核心就两行:

  1. global $shortcode_tags;:声明 $shortcode_tags 是一个全局变量。
  2. $shortcode_tags[ $tag ] = $callback;:如果 $callback 是一个可调用的函数(或者方法),就把它存储到 $shortcode_tags 数组里,以 $tag 作为键。

关键数据结构:$shortcode_tags

$shortcode_tags 是一个全局数组,它存储了所有已注册的短代码和它们对应的处理函数。它的结构大概是这样的:

$shortcode_tags = array(
  'greeting' => 'greeting_shortcode_handler',
  'another_shortcode' => 'another_handler',
  // ... 更多短代码
);

也就是说,$shortcode_tags 是一个关联数组,键是短代码的名称(比如 "greeting"),值是对应的处理函数(比如 "greeting_shortcode_handler")。

四、短代码的处理流程

当 WordPress 渲染文章内容时,它会调用 do_shortcode() 函数来处理短代码。do_shortcode() 函数会遍历文章内容,查找所有可能的短代码标签,然后根据 $shortcode_tags 数组,找到对应的处理函数,并执行它。

do_shortcode() 函数的源码比较复杂,咱们这里只提取关键部分,进行简化说明:

function do_shortcode( $content ) {
    global $shortcode_tags;

    if ( false === strpos( $content, '[' ) ) {
        return $content; // 如果内容中没有方括号,说明没有短代码,直接返回
    }

    // 1. 构建短代码的正则表达式
    $pattern = get_shortcode_regex();

    // 2. 使用正则表达式匹配短代码
    preg_match_all( '/' . $pattern . '/s', $content, $matches );

    // 3. 遍历所有匹配到的短代码
    if ( ! empty( $matches[0] ) ) {
        for ( $i = 0; $i < count( $matches[0] ); $i++ ) {
            $shortcode = $matches[0][$i]; // 完整的短代码字符串,比如 [greeting name="Alice"]
            $tag = $matches[2][$i]; // 短代码的名称,比如 "greeting"
            $atts_string = $matches[3][$i]; // 短代码的属性字符串,比如 ' name="Alice"'
            $content_inside = $matches[5][$i]; // 封闭短代码的内容,比如 "Hello" (对于 [greeting]Hello[/greeting])

            // 4. 查找对应的处理函数
            if ( isset( $shortcode_tags[ $tag ] ) && is_callable( $shortcode_tags[ $tag ] ) ) {
                $callback = $shortcode_tags[ $tag ];

                // 5. 解析属性字符串为关联数组
                $atts = shortcode_parse_atts( $atts_string );

                // 6. 调用处理函数,并替换短代码
                $replace_with = call_user_func( $callback, $atts, $content_inside, $tag );
                $content = str_replace( $shortcode, $replace_with, $content );
            }
        }
    }

    return $content;
}

简单概括一下 do_shortcode() 的流程:

  1. 检查是否存在短代码: 如果文章内容中没有 [ 符号,就直接返回,避免不必要的处理。
  2. 构建正则表达式: 使用 get_shortcode_regex() 函数生成一个用于匹配短代码的正则表达式。这个正则表达式可以匹配各种形式的短代码,包括自闭合的和封闭的。
  3. 匹配短代码: 使用 preg_match_all() 函数,根据正则表达式,在文章内容中查找所有匹配的短代码。
  4. 遍历匹配结果: 遍历所有匹配到的短代码,提取短代码的名称、属性和内容。
  5. 查找处理函数:$shortcode_tags 数组中查找与短代码名称对应的处理函数。
  6. 解析属性: 使用 shortcode_parse_atts() 函数将属性字符串解析为关联数组。
  7. 调用处理函数: 使用 call_user_func() 函数调用处理函数,并将属性数组和内容作为参数传递给它。
  8. 替换短代码: 将文章内容中的短代码替换为处理函数返回的字符串。

五、短代码属性的处理:shortcode_parse_atts()

shortcode_parse_atts() 函数负责将短代码的属性字符串解析为关联数组。例如,对于 [greeting name="Alice" age=30],它会返回:

array(
  'name' => 'Alice',
  'age' => '30'
)

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( "/[x00-x20x7f-xff]+/", ' ', $text );
    $text = trim( $text );
    if ( empty( $text ) ) {
        return $atts;
    }
    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 ( ! empty( $m[7] ) ) {
            $atts[] = stripcslashes( $m[7] );
        } elseif ( isset( $m[8] ) ) {
            $atts[] = stripcslashes( $m[8] );
        }
    }
    return $atts;
}

这个函数使用正则表达式来匹配属性,并处理各种引号和转义情况,最终返回一个关联数组,方便短代码的处理函数使用。

六、短代码的进阶用法

除了基本用法,短代码还有一些更高级的用法,比如:

  • 嵌套短代码: 允许在一个短代码中使用另一个短代码。例如:

    [outer_shortcode]
      [inner_shortcode]
        Content
      [/inner_shortcode]
    [/outer_shortcode]

    要处理嵌套短代码,需要在处理函数中递归调用 do_shortcode() 函数。

  • 自闭合短代码: 像 HTML 标签一样,短代码也可以是自闭合的,比如 [my_shortcode /]

  • 短代码属性的默认值: 可以在处理函数中为属性设置默认值,如果短代码中没有指定该属性,就使用默认值。

  • 移除短代码: 可以使用 remove_shortcode() 函数移除已注册的短代码。

七、使用场景案例分析

为了更好地理解短代码的用法,我们来看几个实际的使用场景:

场景 短代码名称 处理函数 描述
显示当前年份 current_year current_year_shortcode_handler 在文章中插入当前年份。
显示友情链接 friend_links friend_links_shortcode_handler 显示一组友情链接,可以指定链接的数量和排序方式。
显示自定义的广告位 ad_space ad_space_shortcode_handler 根据指定的广告位 ID,显示对应的广告。
创建可折叠的内容区域(手风琴) accordionitem accordion_shortcode_handleritem_shortcode_handler 使用嵌套的短代码,创建一个可折叠的内容区域,每个 item 代表一个折叠项。
显示特定分类的文章列表 category_posts category_posts_shortcode_handler 显示指定分类的文章列表,可以指定文章的数量、排序方式和显示格式。

八、总结与建议

add_shortcode() 函数是 WordPress 中一个非常重要的函数,它允许开发者自定义标签,并在文章、页面或者小工具中嵌入复杂的功能。通过深入理解 add_shortcode() 的源码和短代码的处理流程,我们可以更好地利用短代码来扩展 WordPress 的功能,提高开发效率。

几点建议:

  • 避免滥用短代码: 短代码虽然方便,但是过多的短代码会影响文章的可读性和性能。尽量将常用的功能封装成插件,而不是使用大量的短代码。
  • 注意短代码的安全性: 短代码的处理函数应该进行输入验证和过滤,防止 XSS 攻击。
  • 使用有意义的短代码名称: 短代码的名称应该具有描述性,方便用户理解和使用。
  • 为短代码提供详细的文档: 编写清晰的文档,说明短代码的用法和属性,方便用户使用。

今天的讲座就到这里。希望大家通过今天的学习,对 WordPress 的 add_shortcode() 函数有了更深入的了解。感谢大家的观看!如果有什么问题,欢迎在评论区留言,咱们一起探讨。下次再见!

发表回复

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