apply_block_hooks_to_content()
云策文档标注
概述
apply_block_hooks_to_content() 函数用于在给定内容上运行钩子块算法,自动插入或管理钩子块。它处理序列化内容,根据上下文和回调函数生成最终标记。
关键要点
- 函数接受三个参数:$content(必需,序列化内容)、$context(可选,块模板、模板部分、文章对象或模式,默认为 null 时使用当前文章)、$callback(必需,用于生成钩子块标记的回调函数,默认为 'insert_hooked_blocks')。
- 返回序列化标记字符串,通过遍历和序列化块来应用钩子块逻辑。
- 自动处理钩子块的多实例支持,避免重复插入不支持多实例的块。
- 使用 get_hooked_blocks() 获取钩子块,并通过 traverse_and_serialize_blocks() 结合前后访问器函数处理内容。
代码示例
function apply_block_hooks_to_content( $content, $context = null, $callback = 'insert_hooked_blocks' ) {
// Default to the current post if no context is provided.
if ( null === $context ) {
$context = get_post();
}
$hooked_blocks = get_hooked_blocks();
$before_block_visitor = '_inject_theme_attribute_in_template_part_block';
$after_block_visitor = null;
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $context, $callback );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $context, $callback );
}
// ... 处理多实例逻辑和过滤 ...
$content = traverse_and_serialize_blocks(
parse_blocks( $content ),
$before_block_visitor,
$after_block_visitor
);
return $content;
}注意事项
- 函数在 WordPress 6.6.0 中引入,后续版本有更新:6.7.0 添加了主题属性注入到模板部分块,6.8.0 使 $context 参数默认为 null 以使用当前文章。
- 依赖多个辅助函数如 get_hooked_blocks()、traverse_and_serialize_blocks() 等,确保这些函数在环境中可用。
- 钩子块算法考虑了 block_has_support() 检查,以处理块的多实例支持,避免内容重复。
原文内容
Runs the hooked blocks algorithm on the given content.
Parameters
$contentstringrequired-
Serialized content.
$contextWP_Block_Template|WP_Post|array|nulloptional-
A block template, template part, post object, or pattern that the blocks belong 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( $content, $context = null, $callback = 'insert_hooked_blocks' ) {
// Default to the current post if no context is provided.
if ( null === $context ) {
$context = get_post();
}
$hooked_blocks = get_hooked_blocks();
$before_block_visitor = '_inject_theme_attribute_in_template_part_block';
$after_block_visitor = null;
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $context, $callback );
$after_block_visitor = make_after_block_visitor( $hooked_blocks, $context, $callback );
}
$block_allows_multiple_instances = array();
/*
* Remove hooked blocks from `$hooked_block_types` if they have `multiple` set to false and
* are already present in `$content`.
*/
foreach ( $hooked_blocks as $anchor_block_type => $relative_positions ) {
foreach ( $relative_positions as $relative_position => $hooked_block_types ) {
foreach ( $hooked_block_types as $index => $hooked_block_type ) {
$hooked_block_type_definition =
WP_Block_Type_Registry::get_instance()->get_registered( $hooked_block_type );
$block_allows_multiple_instances[ $hooked_block_type ] =
block_has_support( $hooked_block_type_definition, 'multiple', true );
if (
! $block_allows_multiple_instances[ $hooked_block_type ] &&
has_block( $hooked_block_type, $content )
) {
unset( $hooked_blocks[ $anchor_block_type ][ $relative_position ][ $index ] );
}
}
if ( empty( $hooked_blocks[ $anchor_block_type ][ $relative_position ] ) ) {
unset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] );
}
}
if ( empty( $hooked_blocks[ $anchor_block_type ] ) ) {
unset( $hooked_blocks[ $anchor_block_type ] );
}
}
/*
* We also need to cover the case where the hooked block is not present in
* `$content` at first and we're allowed to insert it once -- but not again.
*/
$suppress_single_instance_blocks = static function ( $hooked_block_types ) use ( &$block_allows_multiple_instances, $content ) {
static $single_instance_blocks_present_in_content = array();
foreach ( $hooked_block_types as $index => $hooked_block_type ) {
if ( ! isset( $block_allows_multiple_instances[ $hooked_block_type ] ) ) {
$hooked_block_type_definition =
WP_Block_Type_Registry::get_instance()->get_registered( $hooked_block_type );
$block_allows_multiple_instances[ $hooked_block_type ] =
block_has_support( $hooked_block_type_definition, 'multiple', true );
}
if ( $block_allows_multiple_instances[ $hooked_block_type ] ) {
continue;
}
// The block doesn't allow multiple instances, so we need to check if it's already present.
if (
in_array( $hooked_block_type, $single_instance_blocks_present_in_content, true ) ||
has_block( $hooked_block_type, $content )
) {
unset( $hooked_block_types[ $index ] );
} else {
// We can insert the block once, but need to remember not to insert it again.
$single_instance_blocks_present_in_content[] = $hooked_block_type;
}
}
return $hooked_block_types;
};
add_filter( 'hooked_block_types', $suppress_single_instance_blocks, PHP_INT_MAX );
$content = traverse_and_serialize_blocks(
parse_blocks( $content ),
$before_block_visitor,
$after_block_visitor
);
remove_filter( 'hooked_block_types', $suppress_single_instance_blocks, PHP_INT_MAX );
return $content;
}