解释 `the_content()` 函数的源码,它是如何通过过滤器(如 `wpautop`、`shortcode_unautop`)处理文章内容的?

各位观众,各位朋友,欢迎来到今天的“WordPress源码揭秘”讲座!今天咱们要扒开WordPress的心脏——the_content() 函数,看看它是如何把你的文字变成网页上漂亮的排版的。

开场白:the_content(),内容界的扛把子

想象一下,你写了一篇精彩的文章,洋洋洒洒几千字,代码、链接、图片,应有尽有。WordPress怎么把这些东西完美地呈现在页面上呢?答案就是 the_content()。它就像一个魔术师,把原始的内容变成我们看到的最终版本。

the_content() 并非单打独斗,它背后有一群默默奉献的“助手”——过滤器。这些过滤器就像流水线上的工人,各司其职,对文章内容进行处理,比如自动添加段落、移除短代码周围的自动段落等等。

第一幕:the_content() 函数本身

首先,我们来看看 the_content() 函数的代码(简化版):

function the_content( $content = null ) {
    global $post;

    if ( null === $content ) {
        if ( isset( $post->post_content ) ) {
            $content = $post->post_content;
        } else {
            $content = '';
        }
    }

    $content = apply_filters( 'the_content', $content );

    echo wp_kses_post( $content );
}

这段代码做了什么?

  1. 获取内容: 首先,它尝试获取文章的内容。如果 $content 参数为空,它就从全局变量 $post 中获取 post_content 属性。如果 $post 变量不存在或者 post_content 属性为空,那就把 $content 设为空字符串。
  2. 应用过滤器: 关键的一步!apply_filters( 'the_content', $content ) 这行代码就像打开了潘多拉魔盒,把 $content 扔进了一个过滤器链条。每个过滤器都会对 $content 进行处理,然后传递给下一个过滤器。
  3. 输出内容: 最后,wp_kses_post( $content )$content 进行安全过滤,防止恶意代码注入,然后输出到页面上。

第二幕:过滤器大军,各显神通

apply_filters( 'the_content', $content ) 这行代码是整个流程的核心。它告诉 WordPress,对文章内容应用 the_content 这个过滤器。那么,都有哪些过滤器在默默地工作呢?

让我们列举几个重要的家伙:

过滤器 功能
wpautop 自动将连续的换行符转换成 HTML 段落标签 <p> 和换行标签 <br>。让你的文字排版更美观。
shortcode_unautop 移除短代码周围自动添加的段落标签 <p> 和换行标签 <br>。防止短代码被不必要的 HTML 标签包裹。
do_shortcode 解析文章中的短代码,并将其替换为相应的 HTML 代码。让你可以使用短代码来嵌入各种内容,比如视频、图片、表单等等。
convert_chars 将一些特殊字符转换成 HTML 实体。比如,将 < 转换成 &lt;> 转换成 &gt;,防止这些字符被浏览器错误地解析。
wptexturize 将一些普通的文本字符转换成印刷体字符。比如,将单引号 ' 转换成弯引号 ,将双引号 " 转换成弯引号 ,让你的文字看起来更专业。
capital_P_dangit 修正 WordPress 的拼写,确保它总是大写的。这是一个幽默的过滤器,它确保 WordPress 这个词永远不会被拼写成 "wordpress"。
prepend_attachment 在附件内容之前添加附件链接。当你在文章中插入图片时,这个过滤器会在图片之前添加一个指向附件页面的链接。
wp_filter_content_tags 用于过滤文章中的 HTML 标签。它允许你删除或修改文章中的某些 HTML 标签,以提高安全性或改善排版。

深入剖析:wpautop,段落界的扛把子

wpautop 绝对是 the_content 过滤器链条中最重要的一员。它负责将文章中的纯文本转换成带有段落标签的 HTML。没有它,你的文字就会像一团乱麻,挤在一起,难以阅读。

让我们看看 wpautop 函数的核心代码(简化版):

function wpautop( $pee, $br = true ) {
    $pre_tags = array( 'pre', 'script', 'style', 'textarea' );

    if ( trim($pee) === '' )
        return '';

    $pee = $pee . "nn"; // Force a newline since wpautop() can't handle lone <p>'s on their own.

    // Protect pre|script|style|textarea tags
    $pee = preg_replace_callback( '/<(pre|script|style|textarea)[^>]*>(.*?)</(pre|script|style|textarea)>/ims', 'wpautop_preserve_newline', $pee );

    $pee = str_replace( array( "rn", "r" ), "n", $pee );
    $pee = preg_replace( "/nn+/", "nn", $pee );
    $pee = preg_replace( '/([<>])n|n([<>])/', '$1$2', $pee );

    $pee = preg_replace( "/n*n/", "</p>n<p>", $pee );
    $pee = preg_replace( '/<p>s*?</p>/', '', $pee );

    $pee = preg_replace( '/<p>s*(<div)/i', '$1', $pee );
    $pee = preg_replace( '/</div>s*</p>/i', '</div>', $pee );
    $pee = preg_replace( '/</p>s*(</div)/i', '$1', $pee );
    $pee = preg_replace( '/</p>s*(<blockquote)/i', '$1', $pee );
    $pee = preg_replace( '/</blockquote>s*</p>/i', '</blockquote>', $pee );
    $pee = preg_replace( '/<p>s*(<ul)/i', '$1', $pee );
    $pee = preg_replace( '/</ul>s*</p>/i', '</ul>', $pee );
    $pee = preg_replace( '/<p>s*(<ol)/i', '$1', $pee );
    $pee = preg_replace( '/</ol>s*</p>/i', '</ol>', $pee );
    $pee = preg_replace( '/<p>s*(<table)/i', '$1', $pee );
    $pee = preg_replace( '/</table>s*</p>/i', '</table>', $pee );

    $pee = preg_replace( '/<p>s*<hr />s*</p>/i', '<hr />', $pee );

    // If option to convert new lines is on...
    if ( $br ) {
        $pee = preg_replace_callback('/<(script|style).*?</\1>/s', 'wpautop_newline_preserve_whitespace', $pee);
        $pee = preg_replace( '|(?<!<br />)s*n|', "<br />n", $pee ); // optionally make line breaks
        $pee = str_replace( '<WPPreserveNewline />', "n", $pee );
    }

    $pee = preg_replace( '!(</(?:table|thead|tfoot|caption)>)<br />!', '$1', $pee );
    $pee = preg_replace( '!(<(?:ul|ol|dl)>)<br />!', '$1', $pee );
    $pee = preg_replace( '!<br />(</(?:p|li|div|dl|dt|dd|td|th|pre|caption|blockquote)>)!', '$1', $pee );
    $pee = preg_replace( "|n</p>$|", '</p>', $pee );

    if ( 'br2nl' == wp_kses_version() ) {
        $pee = preg_replace('/<br/ ?>s*<br/ ?>/', "nn", $pee);
    }

    $pee = preg_replace( '/<(/?(?:address|article|aside|audio|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|h[1-6]|header|hgroup|hr|li|main|nav|noscript|ol|output|p|pre|section|table|td|tfoot|th|thead|tr|ul|video)[^>]*)s*>/iU', "n\0", $pee );

    return $pee;
}

这段代码看起来有点吓人,但其实它的核心逻辑很简单:

  1. 保护特殊标签: wpautop 首先会保护 <pre>, <script>, <style>, <textarea> 这些标签内的内容,避免被错误地处理。这些标签内的内容通常需要保持原样,不能随意添加段落标签。
  2. 处理换行符: wpautop 会将 Windows 风格的换行符 rn 和 Mac 风格的换行符 r 统一转换成 Linux 风格的换行符 n。然后,它会将两个或多个连续的换行符替换成两个换行符。
  3. 添加段落标签: wpautop 会将两个换行符 nn 替换成 </p>n<p>,从而将文本分割成段落。
  4. 清理无效段落: wpautop 会移除空的段落标签 <p>s*?</p>,以及段落标签内部的 <div>, <blockquote>, <ul>, <ol>, <table> 等标签。
  5. 处理单行换行符: 如果 $br 参数为 true(默认值),wpautop 会将单个换行符 n 替换成 <br />n,从而实现换行的效果。
  6. 清理多余的 <br /> 标签: wpautop 会移除表格、列表、段落等标签之前的 <br /> 标签,以及段落标签之后的换行符。

总而言之,wpautop 的目标就是将纯文本转换成带有段落标签和换行符的 HTML,让你的文字排版更美观。

深入剖析:shortcode_unautop,短代码的守护者

短代码是 WordPress 中一种非常方便的功能,可以让你在文章中嵌入各种复杂的内容。但是,wpautop 可能会在短代码周围自动添加段落标签,导致短代码的输出结果出现问题。这时,shortcode_unautop 就派上用场了。

让我们看看 shortcode_unautop 函数的核心代码(简化版):

function shortcode_unautop( $pee ) {
    global $shortcode_tags;

    if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
        return $pee;
    }

    $tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );

    $rep = preg_replace_callback(
        '/<p>s*([' . $tagregexp . '.*?])s*</p>/s',
        'shortcode_unautop_callback',
        $pee
    );

    return $rep;
}

这段代码做了什么?

  1. 检查短代码: shortcode_unautop 首先会检查是否存在已注册的短代码。如果没有,它就直接返回原始内容,不做任何处理。
  2. 构建正则表达式: shortcode_unautop 会根据已注册的短代码,构建一个正则表达式,用于匹配短代码。
  3. 移除段落标签: shortcode_unautop 会使用正则表达式,查找被段落标签包裹的短代码,并将这些段落标签移除。

简单来说,shortcode_unautop 的作用就是移除短代码周围自动添加的段落标签,防止短代码的输出结果被破坏。

第三幕:过滤器链条的奥秘

现在,我们已经了解了 the_content() 函数和几个重要的过滤器。那么,这些过滤器是如何协同工作的呢?

答案就在 apply_filters() 函数中。apply_filters() 函数会将 $content 传递给第一个过滤器,然后将第一个过滤器的输出结果传递给第二个过滤器,以此类推,直到所有过滤器都处理完毕。

过滤器的执行顺序非常重要。通常情况下,WordPress 会按照过滤器被添加的顺序执行它们。但是,你也可以通过指定优先级来改变过滤器的执行顺序。

例如,你可以使用 add_filter() 函数来添加一个过滤器:

add_filter( 'the_content', 'my_custom_filter', 10 );

其中,'the_content' 表示要过滤的内容,'my_custom_filter' 表示过滤器的函数名,10 表示过滤器的优先级。优先级越小,过滤器越先执行。

实战演练:自定义 the_content 过滤器

现在,让我们来编写一个自定义的 the_content 过滤器,来体验一下过滤器的强大之处。

假设我们想要在文章内容的末尾添加一个版权声明。我们可以这样做:

function add_copyright_notice( $content ) {
    $copyright_notice = '<p>Copyright © 2023 My Website. All rights reserved.</p>';
    $content .= $copyright_notice;
    return $content;
}
add_filter( 'the_content', 'add_copyright_notice' );

这段代码定义了一个名为 add_copyright_notice 的函数,它接受一个参数 $content,表示文章的内容。函数在 $content 的末尾添加了一个版权声明,然后返回修改后的 $content

然后,我们使用 add_filter() 函数将 add_copyright_notice 函数注册为 the_content 过滤器。这样,每次调用 the_content() 函数时,add_copyright_notice 函数都会被执行,并在文章内容的末尾添加版权声明。

总结:the_content(),内容呈现的幕后英雄

the_content() 函数是 WordPress 中一个非常重要的函数,它负责将文章的内容呈现给用户。the_content() 函数通过过滤器链条对文章内容进行处理,包括自动添加段落、移除短代码周围的自动段落等等。

掌握 the_content() 函数的工作原理,可以帮助你更好地理解 WordPress 的内容处理机制,并可以让你自定义 the_content 过滤器,来实现各种各样的功能。

希望今天的讲座对你有所帮助!下次再见!

发表回复

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