apply_block_hooks_to_content_from_post_object()
云策文档标注
概述
apply_block_hooks_to_content_from_post_object() 函数用于在文章对象的序列化内容上运行 Block Hooks 算法,特别处理从文章元数据中获取的忽略钩子块信息,确保正确插入作为文章类型对应块的首尾子块的钩子块。
关键要点
- 函数接受三个参数:$content(序列化内容字符串,必需)、$post(WP_Post 对象或 null,可选,默认为 null 时使用当前文章)、$callback(回调函数,可选,默认为 'insert_hooked_blocks')。
- 返回序列化标记字符串,处理内容包括:若无文章对象则回退到 apply_block_hooks_to_content();对非块内容包装为 core/freeform 块;基于文章类型确定包装块类型(如 core/post-content);从文章元数据获取 ignoredHookedBlocks 并设置到包装块属性中。
- 使用过滤器 hooked_block_types 抑制在临时包装块的 before 和 after 位置插入钩子块,以避免重复插入问题。
- 处理完成后移除临时包装块和可能的 core/freeform 包装,返回最终内容。
代码示例
function apply_block_hooks_to_content_from_post_object( $content, $post = null, $callback = 'insert_hooked_blocks' ) {
// 默认使用当前文章
if ( null === $post ) {
$post = get_post();
}
if ( ! $post instanceof WP_Post ) {
return apply_block_hooks_to_content( $content, $post, $callback );
}
// 包装非块内容
if ( ! has_blocks( $content ) ) {
$original_content = $content;
$content_wrapped_in_classic_block = get_comment_delimited_block_content(
'core/freeform',
array(),
$content
);
$content = $content_wrapped_in_classic_block;
}
$attributes = array();
$ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true );
if ( ! empty( $ignored_hooked_blocks ) ) {
$ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true );
$attributes['metadata'] = array(
'ignoredHookedBlocks' => $ignored_hooked_blocks,
);
}
// 确定包装块类型
if ( 'wp_navigation' === $post->post_type ) {
$wrapper_block_type = 'core/navigation';
} elseif ( 'wp_block' === $post->post_type ) {
$wrapper_block_type = 'core/block';
} else {
$wrapper_block_type = 'core/post-content';
}
$content = get_comment_delimited_block_content(
$wrapper_block_type,
$attributes,
$content
);
// 抑制 before/after 位置插入
$suppress_blocks_from_insertion_before_and_after_wrapper_block = static function ( $hooked_block_types, $relative_position, $anchor_block_type ) use ( $wrapper_block_type ) {
if (
$wrapper_block_type === $anchor_block_type &&
in_array( $relative_position, array( 'before', 'after' ), true )
) {
return array();
}
return $hooked_block_types;
};
add_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX, 3 );
$content = apply_block_hooks_to_content( $content, $post, $callback );
remove_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX );
// 移除包装块
$content = remove_serialized_parent_block( $content );
if ( ! empty( $content_wrapped_in_classic_block ) ) {
$content = str_replace( $content_wrapped_in_classic_block, $original_content, $content );
}
return $content;
}注意事项
- 函数在 WordPress 6.8.0 版本引入,主要用于 REST API 响应中插入钩子块,如 insert_hooked_blocks_into_rest_response() 函数所示。
- 处理经典编辑器内容时,会临时包装为 core/freeform 块以确保 Block Hooks 算法能应用,但最终会移除包装以保持内容结构。
- 使用 get_post_meta() 获取文章元数据中的忽略钩子块信息,需确保数据格式正确(JSON 解码)。
- 过滤器 hooked_block_types 的优先级设置为 PHP_INT_MAX,以确保在应用钩子块类型时优先处理抑制逻辑。
原文内容
Run the Block Hooks algorithm on a post object’s content.
Description
This function is different from apply_block_hooks_to_content in that it takes ignored hooked block information from the post’s metadata into account. This ensures that any blocks hooked as first or last child of the block that corresponds to the post type are handled correctly.
Parameters
$contentstringrequired-
Serialized content.
$postWP_Post|nulloptional-
A post object that the content belongs to. If set to
null,get_post()will be called to use the current post as context.
Default:null.Default:
null $callbackcallablerequired-
A function that will be called for each block to generate the markup for a given list of blocks that are hooked to it.
Default:'insert_hooked_blocks'.
Source
function apply_block_hooks_to_content_from_post_object( $content, $post = null, $callback = 'insert_hooked_blocks' ) {
// Default to the current post if no context is provided.
if ( null === $post ) {
$post = get_post();
}
if ( ! $post instanceof WP_Post ) {
return apply_block_hooks_to_content( $content, $post, $callback );
}
/*
* If the content was created using the classic editor or using a single Classic block
* (`core/freeform`), it might not contain any block markup at all.
* However, we still might need to inject hooked blocks in the first child or last child
* positions of the parent block. To be able to apply the Block Hooks algorithm, we wrap
* the content in a `core/freeform` wrapper block.
*/
if ( ! has_blocks( $content ) ) {
$original_content = $content;
$content_wrapped_in_classic_block = get_comment_delimited_block_content(
'core/freeform',
array(),
$content
);
$content = $content_wrapped_in_classic_block;
}
$attributes = array();
// If context is a post object, `ignoredHookedBlocks` information is stored in its post meta.
$ignored_hooked_blocks = get_post_meta( $post->ID, '_wp_ignored_hooked_blocks', true );
if ( ! empty( $ignored_hooked_blocks ) ) {
$ignored_hooked_blocks = json_decode( $ignored_hooked_blocks, true );
$attributes['metadata'] = array(
'ignoredHookedBlocks' => $ignored_hooked_blocks,
);
}
/*
* We need to wrap the content in a temporary wrapper block with that metadata
* so the Block Hooks algorithm can insert blocks that are hooked as first or last child
* of the wrapper block.
* To that end, we need to determine the wrapper block type based on the post type.
*/
if ( 'wp_navigation' === $post->post_type ) {
$wrapper_block_type = 'core/navigation';
} elseif ( 'wp_block' === $post->post_type ) {
$wrapper_block_type = 'core/block';
} else {
$wrapper_block_type = 'core/post-content';
}
$content = get_comment_delimited_block_content(
$wrapper_block_type,
$attributes,
$content
);
/*
* We need to avoid inserting any blocks hooked into the `before` and `after` positions
* of the temporary wrapper block that we create to wrap the content.
* See https://core.trac.wordpress.org/ticket/63287 for more details.
*/
$suppress_blocks_from_insertion_before_and_after_wrapper_block = static function ( $hooked_block_types, $relative_position, $anchor_block_type ) use ( $wrapper_block_type ) {
if (
$wrapper_block_type === $anchor_block_type &&
in_array( $relative_position, array( 'before', 'after' ), true )
) {
return array();
}
return $hooked_block_types;
};
// Apply Block Hooks.
add_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX, 3 );
$content = apply_block_hooks_to_content( $content, $post, $callback );
remove_filter( 'hooked_block_types', $suppress_blocks_from_insertion_before_and_after_wrapper_block, PHP_INT_MAX );
// Finally, we need to remove the temporary wrapper block.
$content = remove_serialized_parent_block( $content );
// If we wrapped the content in a `core/freeform` block, we also need to remove that.
if ( ! empty( $content_wrapped_in_classic_block ) ) {
/*
* We cannot simply use remove_serialized_parent_block() here,
* as that function assumes that the block wrapper is at the top level.
* However, there might now be a hooked block inserted next to it
* (as first or last child of the parent).
*/
$content = str_replace( $content_wrapped_in_classic_block, $original_content, $content );
}
return $content;
}
Changelog
| Version | Description |
|---|---|
| 6.8.0 | Introduced. |