: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
dirname( $metadata['file'] ) . '/' .
remove_block_asset_path_prefix( $metadata['render'] )
* Renders the block on the server.
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
* @return string Returns the block content.
$settings['render_callback'] = static function ( $attributes, $content, $block ) use ( $template_path ) {
$settings = array_merge( $settings, $args );
'editorScript' => 'editor_script_handles',
'script' => 'script_handles',
'viewScript' => 'view_script_handles',
foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$scripts = $metadata[ $metadata_field_name ];
$processed_scripts = array();
if ( is_array( $scripts ) ) {
for ( $index = 0; $index < count( $scripts ); $index++ ) {
$result = register_block_script_handle(
$processed_scripts[] = $result;
$result = register_block_script_handle(
$processed_scripts[] = $result;
$settings[ $settings_field_name ] = $processed_scripts;
'viewScriptModule' => 'view_script_module_ids',
foreach ( $module_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$modules = $metadata[ $metadata_field_name ];
$processed_modules = array();
if ( is_array( $modules ) ) {
for ( $index = 0; $index < count( $modules ); $index++ ) {
$result = register_block_script_module_id(
$processed_modules[] = $result;
$result = register_block_script_module_id(
$processed_modules[] = $result;
$settings[ $settings_field_name ] = $processed_modules;
'editorStyle' => 'editor_style_handles',
'style' => 'style_handles',
'viewStyle' => 'view_style_handles',
foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
if ( ! empty( $settings[ $metadata_field_name ] ) ) {
$metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
$styles = $metadata[ $metadata_field_name ];
$processed_styles = array();
if ( is_array( $styles ) ) {
for ( $index = 0; $index < count( $styles ); $index++ ) {
$result = register_block_style_handle(
$processed_styles[] = $result;
$result = register_block_style_handle(
$processed_styles[] = $result;
$settings[ $settings_field_name ] = $processed_styles;
if ( ! empty( $metadata['blockHooks'] ) ) {
* Map camelCased position string (from block.json) to snake_cased block type position.
$position_mappings = array(
'firstChild' => 'first_child',
'lastChild' => 'last_child',
$settings['block_hooks'] = array();
foreach ( $metadata['blockHooks'] as $anchor_block_name => $position ) {
// Avoid infinite recursion (hooking to itself).
if ( $metadata['name'] === $anchor_block_name ) {
__( 'Cannot hook block to itself.' ),
if ( ! isset( $position_mappings[ $position ] ) ) {
$settings['block_hooks'][ $anchor_block_name ] = $position_mappings[ $position ];
* Filters the settings determined from the block type metadata.
* @param array $settings Array of determined settings for registering a block type.
* @param array $metadata Metadata provided for registering a block type.
$settings = apply_filters( 'block_type_metadata_settings', $settings, $metadata );
$metadata['name'] = ! empty( $settings['name'] ) ? $settings['name'] : $metadata['name'];
return WP_Block_Type_Registry::get_instance()->register(
* Registers a block type. The recommended way is to register a block type using
* the metadata stored in the `block.json` file.
* @since 5.8.0 First parameter now accepts a path to the `block.json` file.
* @param string|WP_Block_Type $block_type Block type name including namespace, or alternatively
* a path to the JSON file with metadata definition for the block,
* or a path to the folder where the `block.json` file is located,
* or a complete WP_Block_Type instance.
* In case a WP_Block_Type is provided, the $args parameter will be ignored.
* @param array $args Optional. Array of block type arguments. Accepts any public property
* of `WP_Block_Type`. See WP_Block_Type::__construct() for information
* on accepted arguments. Default empty array.
* @return WP_Block_Type|false The registered block type on success, or false on failure.
function register_block_type( $block_type, $args = array() ) {
if ( is_string( $block_type ) && file_exists( $block_type ) ) {
return register_block_type_from_metadata( $block_type, $args );
return WP_Block_Type_Registry::get_instance()->register( $block_type, $args );
* Unregisters a block type.
* @param string|WP_Block_Type $name Block type name including namespace, or alternatively
* a complete WP_Block_Type instance.
* @return WP_Block_Type|false The unregistered block type on success, or false on failure.
function unregister_block_type( $name ) {
return WP_Block_Type_Registry::get_instance()->unregister( $name );
* Determines whether a post or content string has blocks.
* This test optimizes for performance rather than strict accuracy, detecting
* the pattern of a block but not validating its structure. For strict accuracy,
* you should use the block parser on post content.
* @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object.
* Defaults to global $post.
* @return bool Whether the post has blocks.
function has_blocks( $post = null ) {
if ( ! is_string( $post ) ) {
$wp_post = get_post( $post );
if ( ! $wp_post instanceof WP_Post ) {
$post = $wp_post->post_content;
return str_contains( (string) $post, '<!-- wp:' );
* Determines whether a $post or a string contains a specific block type.
* This test optimizes for performance rather than strict accuracy, detecting
* whether the block type exists but not validating its structure and not checking
* synced patterns (formerly called reusable blocks). For strict accuracy,
* you should use the block parser on post content.
* @param string $block_name Full block type to look for.
* @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object.
* Defaults to global $post.
* @return bool Whether the post content contains the specified block.
function has_block( $block_name, $post = null ) {
if ( ! has_blocks( $post ) ) {
if ( ! is_string( $post ) ) {
$wp_post = get_post( $post );
if ( $wp_post instanceof WP_Post ) {
$post = $wp_post->post_content;
* Normalize block name to include namespace, if provided as non-namespaced.
* This matches behavior for WordPress 5.0.0 - 5.3.0 in matching blocks by
* their serialized names.
if ( ! str_contains( $block_name, '/' ) ) {
$block_name = 'core/' . $block_name;
// Test for existence of block by its fully qualified name.
$has_block = str_contains( $post, '<!-- wp:' . $block_name . ' ' );
* If the given block name would serialize to a different name, test for
* existence by the serialized form.
$serialized_block_name = strip_core_block_namespace( $block_name );
if ( $serialized_block_name !== $block_name ) {
$has_block = str_contains( $post, '<!-- wp:' . $serialized_block_name . ' ' );
* Returns an array of the names of all registered dynamic block types.
* @return string[] Array of dynamic block names.
function get_dynamic_block_names() {
$dynamic_block_names = array();
$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
foreach ( $block_types as $block_type ) {
if ( $block_type->is_dynamic() ) {
$dynamic_block_names[] = $block_type->name;
return $dynamic_block_names;
* Retrieves block types hooked into the given block, grouped by anchor block type and the relative position.
* @return array[] Array of block types grouped by anchor block type and the relative position.
function get_hooked_blocks() {
$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
$hooked_blocks = array();
foreach ( $block_types as $block_type ) {
if ( ! ( $block_type instanceof WP_Block_Type ) || ! is_array( $block_type->block_hooks ) ) {
foreach ( $block_type->block_hooks as $anchor_block_type => $relative_position ) {
if ( ! isset( $hooked_blocks[ $anchor_block_type ] ) ) {
$hooked_blocks[ $anchor_block_type ] = array();
if ( ! isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] ) ) {
$hooked_blocks[ $anchor_block_type ][ $relative_position ] = array();
$hooked_blocks[ $anchor_block_type ][ $relative_position ][] = $block_type->name;
* Returns the markup for blocks hooked to the given anchor block in a specific relative position.
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
* @param string $relative_position The relative position of the hooked blocks.
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
* @param WP_Block_Template|WP_Post|array $context The block template, template part, or pattern that the anchor block belongs to.
function insert_hooked_blocks( &$parsed_anchor_block, $relative_position, $hooked_blocks, $context ) {
$anchor_block_type = $parsed_anchor_block['blockName'];
$hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
* Filters the list of hooked block types for a given anchor block type and relative position.
* @param string[] $hooked_block_types The list of hooked block types.
* @param string $relative_position The relative position of the hooked blocks.
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
* @param string $anchor_block_type The anchor block type.
* @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type,
* or pattern that the anchor block belongs to.
$hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
foreach ( $hooked_block_types as $hooked_block_type ) {
$parsed_hooked_block = array(
'blockName' => $hooked_block_type,
'innerBlocks' => array(),
'innerContent' => array(),
* Filters the parsed block array for a given hooked block.
* @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block.
* @param string $hooked_block_type The hooked block type name.
* @param string $relative_position The relative position of the hooked block.
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
* @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type,
* or pattern that the anchor block belongs to.
$parsed_hooked_block = apply_filters( 'hooked_block', $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context );
* Filters the parsed block array for a given hooked block.
* The dynamic portion of the hook name, `$hooked_block_type`, refers to the block type name of the specific hooked block.
* @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block.
* @param string $hooked_block_type The hooked block type name.
* @param string $relative_position The relative position of the hooked block.
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
* @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type,
* or pattern that the anchor block belongs to.
$parsed_hooked_block = apply_filters( "hooked_block_{$hooked_block_type}", $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context );
if ( null === $parsed_hooked_block ) {
// It's possible that the filter returned a block of a different type, so we explicitly
// look for the original `$hooked_block_type` in the `ignoredHookedBlocks` metadata.
! isset( $parsed_anchor_block['attrs']['metadata']['ignoredHookedBlocks'] ) ||
! in_array( $hooked_block_type, $parsed_anchor_block['attrs']['metadata']['ignoredHookedBlocks'], true )
$markup .= serialize_block( $parsed_hooked_block );
* Adds a list of hooked block types to an anchor block's ignored hooked block types.
* This function is meant for internal use only.
* @param array $parsed_anchor_block The anchor block, in parsed block array format.
* @param string $relative_position The relative position of the hooked blocks.
* Can be one of 'before', 'after', 'first_child', or 'last_child'.
* @param array $hooked_blocks An array of hooked block types, grouped by anchor block and relative position.
* @param WP_Block_Template|WP_Post|array $context The block template, template part, or pattern that the anchor block belongs to.
* @return string Empty string.
function set_ignored_hooked_blocks_metadata( &$parsed_anchor_block, $relative_position, $hooked_blocks, $context ) {
$anchor_block_type = $parsed_anchor_block['blockName'];
$hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
/** This filter is documented in wp-includes/blocks.php */
$hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
if ( empty( $hooked_block_types ) ) {
foreach ( $hooked_block_types as $index => $hooked_block_type ) {
$parsed_hooked_block = array(
'blockName' => $hooked_block_type,
'innerBlocks' => array(),
'innerContent' => array(),
/** This filter is documented in wp-includes/blocks.php */
$parsed_hooked_block = apply_filters( 'hooked_block', $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context );
/** This filter is documented in wp-includes/blocks.php */
$parsed_hooked_block = apply_filters( "hooked_block_{$hooked_block_type}", $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context );
if ( null === $parsed_hooked_block ) {
unset( $hooked_block_types[ $index ] );
$previously_ignored_hooked_blocks = isset( $parsed_anchor_block['attrs']['metadata']['ignoredHookedBlocks'] )
? $parsed_anchor_block['attrs']['metadata']['ignoredHookedBlocks']
$parsed_anchor_block['attrs']['metadata']['ignoredHookedBlocks'] = array_unique(
$previously_ignored_hooked_blocks,