分析 WordPress `the_content` 过滤器在 Gutenberg 中的源码实现:如何将区块数据转换为 HTML 输出。

各位观众老爷们,晚上好! 今天咱们来聊聊 WordPress 里那个神秘又强大的 the_content 过滤器,以及它在古腾堡(Gutenberg)时代是如何把那些花里胡哨的区块数据变成我们能在浏览器里看到的漂亮 HTML 的。 这可不是什么魔法,背后都是代码堆砌出来的,咱们一起扒开它的皮,看看里面到底藏了些啥。

1. the_content 过滤器:老兵不死,只是换了战场

首先,咱们得搞清楚 the_content 过滤器是个什么玩意儿。 在 WordPress 的世界里,它就像一个中间人,负责在文章内容被显示之前,对内容进行各种各样的处理。 以前,我们用它来干嘛? 比如自动给文章加 p 标签、把短代码转换成 HTML、处理 oEmbed 嵌入内容等等。

// 举个栗子,以前我们可能这么用:
add_filter( 'the_content', 'my_custom_content_filter' );

function my_custom_content_filter( $content ) {
  // 在内容前后加点料
  $content = '<div class="my-custom-wrapper">' . $content . '</div>';
  return $content;
}

现在到了古腾堡时代,文章内容不再是简单的 HTML 文本了,而是一堆 JSON 格式的区块数据。 那 the_content 过滤器还能用吗? 当然能! 它只是换了个战场,从处理 HTML 变成了处理区块数据,最终目标还是生成 HTML。

2. 区块数据:从文本到 JSON 的华丽转身

古腾堡的核心思想就是“区块”。 每个区块代表文章的一个组成部分,比如一个段落、一张图片、一个标题等等。 这些区块的信息都以 JSON 格式存储在 post_content 字段里。

举个例子,一个简单的段落区块可能是这样的:

{
  "blockName": "core/paragraph",
  "attrs": {
    "content": "这是一个段落。",
    "align": "center"
  },
  "innerBlocks": [],
  "innerHTML": "n<p class="has-text-align-center">这是一个段落。</p>n",
  "innerContent": [
    "n<p class="has-text-align-center">这是一个段落。</p>n"
  ]
}
  • blockName: 区块的名称,比如 core/paragraph 表示段落区块。
  • attrs: 区块的属性,比如段落的文本内容、对齐方式等等。
  • innerBlocks: 嵌套的区块,比如一个列表区块里可能包含多个列表项区块。
  • innerHTML: 渲染后的 HTML,这个在保存时可能就存在。
  • innerContent: 渲染后的 HTML 组成的数组,和innerHTML类似。

这些 JSON 数据就是古腾堡的“积木”,WordPress 要做的就是把这些“积木”搭成我们看到的文章。

3. do_blocks() 函数:区块渲染的总指挥

do_blocks() 函数是古腾堡区块渲染的核心。 它负责解析 post_content 里的区块数据,然后把每个区块转换成 HTML。

// 源码位置:wp-includes/blocks.php

function do_blocks( $content ) {
    if ( ! has_blocks( $content ) ) {
        return $content;
    }

    $blocks = parse_blocks( $content );

    if ( empty( $blocks ) ) {
        return '';
    }

    $output = '';

    foreach ( $blocks as $block ) {
        $output .= render_block( $block );
    }

    return $output;
}

代码解读:

  1. has_blocks( $content ): 检查内容是否包含区块。 如果没有,就直接返回原始内容。
  2. parse_blocks( $content ): 解析内容,把 JSON 格式的区块数据转换成 PHP 数组。
  3. render_block( $block ): 渲染单个区块,生成 HTML。

4. parse_blocks() 函数:把 JSON 变成 PHP 数组

parse_blocks() 函数负责把 post_content 里的 JSON 格式的区块数据转换成 PHP 数组,方便后续处理。

// 源码位置:wp-includes/blocks.php

function parse_blocks( $content ) {
    if ( ! is_string( $content ) ) {
        return array();
    }

    $blocks = array();

    $parser = new WP_Block_Parser();

    $blocks = $parser->parse( $content );

    return $blocks;
}

代码解读:

  1. 创建一个 WP_Block_Parser 类的实例。
  2. 调用 WP_Block_Parser::parse() 方法解析内容,返回一个包含所有区块信息的 PHP 数组。

WP_Block_Parser 类是 WordPress 专门用来解析区块数据的,它会根据区块的语法规则,把 JSON 数据转换成 PHP 数组。 这个过程比较复杂,涉及到正则表达式匹配、状态机转换等等,咱们就不深入研究了。

5. render_block() 函数:把区块变成 HTML

render_block() 函数是区块渲染的关键。 它负责根据区块的类型,调用相应的渲染函数,生成 HTML。

// 源码位置:wp-includes/blocks.php

function render_block( $block ) {
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );

    if ( ! $block_type ) {
        return '';
    }

    if ( ! empty( $block_type->render_callback ) ) {
        $output = call_user_func( $block_type->render_callback, $block['attrs'], $block['content'], $block );
    } else {
        $output = '';
    }

    return $output;
}

代码解读:

  1. WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ): 获取区块类型的信息。 每个区块类型都有一个对应的渲染函数,负责把区块数据转换成 HTML。
  2. call_user_func( $block_type->render_callback, $block['attrs'], $block['content'], $block ): 调用区块类型的渲染函数,生成 HTML。

重点来了:渲染函数

每个区块类型都有一个对应的渲染函数,这个函数决定了区块最终会生成什么样的 HTML。 WordPress 自带了很多内置的区块类型,比如段落、标题、图片等等,每个区块类型都有一个默认的渲染函数。

但是,我们也可以自定义区块类型,并提供自己的渲染函数。 这样就可以实现更灵活的区块渲染逻辑。

举个例子,假设我们自定义了一个名为 my-custom/greeting 的区块类型,它的渲染函数可能是这样的:

function render_greeting_block( $attributes, $content ) {
  $name = isset( $attributes['name'] ) ? $attributes['name'] : 'World';
  $message = 'Hello, ' . esc_html( $name ) . '!';
  return '<div class="greeting-block">' . $message . '</div>';
}

// 注册区块类型
register_block_type(
  'my-custom/greeting',
  array(
    'render_callback' => 'render_greeting_block',
    'attributes' => array(
      'name' => array(
        'type' => 'string',
        'default' => 'World',
      ),
    ),
  )
);

这段代码做了两件事:

  1. 定义了一个名为 render_greeting_block 的渲染函数,它接收区块的属性和内容,生成一个包含问候语的 HTML。
  2. 注册了一个名为 my-custom/greeting 的区块类型,并把 render_greeting_block 函数指定为它的渲染函数。

这样,当 WordPress 遇到 my-custom/greeting 区块时,就会调用 render_greeting_block 函数,生成相应的 HTML。

6. the_content 过滤器与区块渲染的衔接

现在,我们把上面讲的知识点串起来,看看 the_content 过滤器是如何与区块渲染衔接的。

  1. 当 WordPress 需要显示文章内容时,会调用 apply_filters( 'the_content', $content ) 函数,触发 the_content 过滤器。
  2. WordPress 默认会把 do_blocks() 函数添加到 the_content 过滤器里。
  3. do_blocks() 函数会解析文章内容里的区块数据,并把每个区块转换成 HTML。
  4. do_blocks() 函数会把所有区块的 HTML 拼接起来,作为最终的文章内容返回。
  5. WordPress 把最终的文章内容显示在页面上。

可以用一个表格来总结这个过程:

步骤 函数/操作 作用
1 apply_filters( 'the_content', $content ) 触发 the_content 过滤器
2 do_blocks() 解析区块数据,渲染区块
3 parse_blocks() 把 JSON 格式的区块数据转换成 PHP 数组
4 render_block() 根据区块类型,调用相应的渲染函数,生成 HTML
5 区块渲染函数 把区块数据转换成 HTML
6 WordPress 显示文章内容 把所有区块的 HTML 拼接起来,显示在页面上

7. 自定义 the_content 过滤器:更上一层楼

虽然 WordPress 已经提供了默认的区块渲染机制,但我们仍然可以自定义 the_content 过滤器,对区块渲染过程进行更精细的控制。

比如,我们可以添加自己的过滤器,在区块渲染前后做一些处理:

add_filter( 'the_content', 'my_custom_block_filter', 10, 1 );

function my_custom_block_filter( $content ) {
  // 在区块渲染前做一些处理
  $content = '<div class="my-custom-content-wrapper">' . $content . '</div>';

  // 调用 do_blocks() 函数,渲染区块
  $content = do_blocks( $content );

  // 在区块渲染后做一些处理
  $content = $content . '<div class="my-custom-content-footer"></div>';

  return $content;
}

这段代码做了三件事:

  1. 在区块渲染前,给文章内容添加一个 my-custom-content-wrapper 的 div 容器。
  2. 调用 do_blocks() 函数,渲染区块。
  3. 在区块渲染后,给文章内容添加一个 my-custom-content-footer 的 div 容器。

这样,我们就可以在区块渲染前后做一些自定义的处理,比如添加 CSS 样式、添加 JavaScript 代码等等。

8. 注意事项

  • has_blocks() 函数可以用来判断文章内容是否包含区块。 如果不包含区块,就没必要调用 do_blocks() 函数,可以提高性能。
  • render_block() 函数会缓存区块的 HTML,避免重复渲染。
  • 自定义区块类型时,一定要提供渲染函数,否则区块将无法显示。
  • 自定义 the_content 过滤器时,要注意过滤器的优先级。 默认情况下,do_blocks() 函数的优先级是 9,所以我们的过滤器应该设置更高的优先级,才能在 do_blocks() 函数之前或之后执行。

9. 总结

今天咱们一起扒了扒 WordPress the_content 过滤器在古腾堡中的源码实现,主要讲了以下几个方面:

  • the_content 过滤器的作用和原理。
  • 区块数据的格式和特点。
  • do_blocks()parse_blocks()render_block() 函数的作用和实现。
  • 如何自定义区块类型和渲染函数。
  • 如何自定义 the_content 过滤器,对区块渲染过程进行更精细的控制。

希望今天的讲座能帮助大家更深入地理解 WordPress 古腾堡的区块渲染机制。 其实,古腾堡的源码并没有想象中那么复杂,只要我们耐心一点,一步一步地分析,就能掌握它的核心原理。

好了,今天的讲座就到这里,谢谢大家! 祝大家编码愉快!

发表回复

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