register_block_type_from_metadata

The timeline below displays how wordpress function register_block_type_from_metadata has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.5

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields.
 * @since 5.9.0 Added support for `variations` and `viewScript` fields.
 * @since 6.1.0 Added support for `render` field.
 * @since 6.3.0 Added `selectors` field.
 * @since 6.4.0 Added support for `blockHooks` field.
 * @since 6.5.0 Added support for `allowedBlocks`, `viewScriptModule`, and `viewStyle` fields.
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 *                               If providing the path to a JSON file, the filename must end with `block.json`.
 * @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_from_metadata($file_or_folder, $args = array())
{
    /*
     * Get an array of metadata from a PHP file.
     * This improves performance for core blocks as it's only necessary to read a single PHP file
     * instead of reading a JSON file per-block, and then decoding from JSON to PHP.
     * Using a static variable ensures that the metadata is only read once per request.
     */
    static $core_blocks_meta;
    if (!$core_blocks_meta) {
        $core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
    }
    $metadata_file = (!str_ends_with($file_or_folder, 'block.json')) ? trailingslashit($file_or_folder) . 'block.json' : $file_or_folder;
    $is_core_block = str_starts_with($file_or_folder, ABSPATH . WPINC);
    // If the block is not a core block, the metadata file must exist.
    $metadata_file_exists = $is_core_block || file_exists($metadata_file);
    if (!$metadata_file_exists && empty($args['name'])) {
        return false;
    }
    // Try to get metadata from the static cache for core blocks.
    $metadata = array();
    if ($is_core_block) {
        $core_block_name = str_replace(ABSPATH . WPINC . '/blocks/', '', $file_or_folder);
        if (!empty($core_blocks_meta[$core_block_name])) {
            $metadata = $core_blocks_meta[$core_block_name];
        }
    }
    // If metadata is not found in the static cache, read it from the file.
    if ($metadata_file_exists && empty($metadata)) {
        $metadata = wp_json_file_decode($metadata_file, array('associative' => true));
    }
    if (!is_array($metadata) || empty($metadata['name']) && empty($args['name'])) {
        return false;
    }
    $metadata['file'] = $metadata_file_exists ? wp_normalize_path(realpath($metadata_file)) : null;
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && str_starts_with($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (current_theme_supports('wp-block-styles') && wp_should_load_separate_core_block_assets()) {
            $metadata['style'] = (array) $metadata['style'];
            $metadata['style'][] = "wp-block-{$block_name}-theme";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('apiVersion' => 'api_version', 'name' => 'name', 'title' => 'title', 'category' => 'category', 'parent' => 'parent', 'ancestor' => 'ancestor', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'selectors' => 'selectors', 'supports' => 'supports', 'styles' => 'styles', 'variations' => 'variations', 'example' => 'example', 'allowedBlocks' => 'allowed_blocks');
    $textdomain = (!empty($metadata['textdomain'])) ? $metadata['textdomain'] : null;
    $i18n_schema = get_block_metadata_i18n_schema();
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
            if ($metadata_file_exists && $textdomain && isset($i18n_schema->{$key})) {
                $settings[$mapped_key] = translate_settings_using_i18n_schema($i18n_schema->{$key}, $settings[$key], $textdomain);
            }
        }
    }
    if (!empty($metadata['render'])) {
        $template_path = wp_normalize_path(realpath(dirname($metadata['file']) . '/' . remove_block_asset_path_prefix($metadata['render'])));
        if ($template_path) {
            /**
             * Renders the block on the server.
             *
             * @since 6.1.0
             *
             * @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) {
                ob_start();
                require $template_path;
                return ob_get_clean();
            };
        }
    }
    $settings = array_merge($settings, $args);
    $script_fields = array('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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_scripts[] = $result;
                    }
                }
            } else {
                $result = register_block_script_handle($metadata, $metadata_field_name);
                if ($result) {
                    $processed_scripts[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_scripts;
        }
    }
    $module_fields = array('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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_modules[] = $result;
                    }
                }
            } else {
                $result = register_block_script_module_id($metadata, $metadata_field_name);
                if ($result) {
                    $processed_modules[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_modules;
        }
    }
    $style_fields = array('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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_styles[] = $result;
                    }
                }
            } else {
                $result = register_block_style_handle($metadata, $metadata_field_name);
                if ($result) {
                    $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.
         *
         * @var array
         */
        $position_mappings = array('before' => 'before', 'after' => 'after', '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) {
                _doing_it_wrong(__METHOD__, __('Cannot hook block to itself.'), '6.4.0');
                continue;
            }
            if (!isset($position_mappings[$position])) {
                continue;
            }
            $settings['block_hooks'][$anchor_block_name] = $position_mappings[$position];
        }
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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($metadata['name'], $settings);
}

WordPress Version: 6.4

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields.
 * @since 5.9.0 Added support for `variations` and `viewScript` fields.
 * @since 6.1.0 Added support for `render` field.
 * @since 6.3.0 Added `selectors` field.
 * @since 6.4.0 Added support for `blockHooks` field.
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 *                               If providing the path to a JSON file, the filename must end with `block.json`.
 * @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_from_metadata($file_or_folder, $args = array())
{
    /*
     * Get an array of metadata from a PHP file.
     * This improves performance for core blocks as it's only necessary to read a single PHP file
     * instead of reading a JSON file per-block, and then decoding from JSON to PHP.
     * Using a static variable ensures that the metadata is only read once per request.
     */
    static $core_blocks_meta;
    if (!$core_blocks_meta) {
        $core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
    }
    $metadata_file = (!str_ends_with($file_or_folder, 'block.json')) ? trailingslashit($file_or_folder) . 'block.json' : $file_or_folder;
    $is_core_block = str_starts_with($file_or_folder, ABSPATH . WPINC);
    if (!$is_core_block && !file_exists($metadata_file)) {
        return false;
    }
    // Try to get metadata from the static cache for core blocks.
    $metadata = false;
    if ($is_core_block) {
        $core_block_name = str_replace(ABSPATH . WPINC . '/blocks/', '', $file_or_folder);
        if (!empty($core_blocks_meta[$core_block_name])) {
            $metadata = $core_blocks_meta[$core_block_name];
        }
    }
    // If metadata is not found in the static cache, read it from the file.
    if (!$metadata) {
        $metadata = wp_json_file_decode($metadata_file, array('associative' => true));
    }
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = wp_normalize_path(realpath($metadata_file));
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && str_starts_with($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (current_theme_supports('wp-block-styles') && wp_should_load_separate_core_block_assets()) {
            $metadata['style'] = (array) $metadata['style'];
            $metadata['style'][] = "wp-block-{$block_name}-theme";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('apiVersion' => 'api_version', 'title' => 'title', 'category' => 'category', 'parent' => 'parent', 'ancestor' => 'ancestor', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'selectors' => 'selectors', 'supports' => 'supports', 'styles' => 'styles', 'variations' => 'variations', 'example' => 'example');
    $textdomain = (!empty($metadata['textdomain'])) ? $metadata['textdomain'] : null;
    $i18n_schema = get_block_metadata_i18n_schema();
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
            if ($textdomain && isset($i18n_schema->{$key})) {
                $settings[$mapped_key] = translate_settings_using_i18n_schema($i18n_schema->{$key}, $settings[$key], $textdomain);
            }
        }
    }
    $script_fields = array('editorScript' => 'editor_script_handles', 'script' => 'script_handles', 'viewScript' => 'view_script_handles');
    foreach ($script_fields as $metadata_field_name => $settings_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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_scripts[] = $result;
                    }
                }
            } else {
                $result = register_block_script_handle($metadata, $metadata_field_name);
                if ($result) {
                    $processed_scripts[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_scripts;
        }
    }
    $style_fields = array('editorStyle' => 'editor_style_handles', 'style' => 'style_handles');
    foreach ($style_fields as $metadata_field_name => $settings_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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_styles[] = $result;
                    }
                }
            } else {
                $result = register_block_style_handle($metadata, $metadata_field_name);
                if ($result) {
                    $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.
         *
         * @var array
         */
        $position_mappings = array('before' => 'before', 'after' => 'after', '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) {
                _doing_it_wrong(__METHOD__, __('Cannot hook block to itself.'), '6.4.0');
                continue;
            }
            if (!isset($position_mappings[$position])) {
                continue;
            }
            $settings['block_hooks'][$anchor_block_name] = $position_mappings[$position];
        }
    }
    if (!empty($metadata['render'])) {
        $template_path = wp_normalize_path(realpath(dirname($metadata['file']) . '/' . remove_block_asset_path_prefix($metadata['render'])));
        if ($template_path) {
            /**
             * Renders the block on the server.
             *
             * @since 6.1.0
             *
             * @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) {
                ob_start();
                require $template_path;
                return ob_get_clean();
            };
        }
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return WP_Block_Type_Registry::get_instance()->register($metadata['name'], $settings);
}

WordPress Version: 6.3

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields.
 * @since 5.9.0 Added support for `variations` and `viewScript` fields.
 * @since 6.1.0 Added support for `render` field.
 * @since 6.3.0 Added `selectors` field.
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 *                               If providing the path to a JSON file, the filename must end with `block.json`.
 * @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_from_metadata($file_or_folder, $args = array())
{
    /*
     * Get an array of metadata from a PHP file.
     * This improves performance for core blocks as it's only necessary to read a single PHP file
     * instead of reading a JSON file per-block, and then decoding from JSON to PHP.
     * Using a static variable ensures that the metadata is only read once per request.
     */
    static $core_blocks_meta;
    if (!$core_blocks_meta) {
        $core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
    }
    $metadata_file = (!str_ends_with($file_or_folder, 'block.json')) ? trailingslashit($file_or_folder) . 'block.json' : $file_or_folder;
    $is_core_block = str_starts_with($file_or_folder, ABSPATH . WPINC);
    if (!$is_core_block && !file_exists($metadata_file)) {
        return false;
    }
    // Try to get metadata from the static cache for core blocks.
    $metadata = false;
    if ($is_core_block) {
        $core_block_name = str_replace(ABSPATH . WPINC . '/blocks/', '', $file_or_folder);
        if (!empty($core_blocks_meta[$core_block_name])) {
            $metadata = $core_blocks_meta[$core_block_name];
        }
    }
    // If metadata is not found in the static cache, read it from the file.
    if (!$metadata) {
        $metadata = wp_json_file_decode($metadata_file, array('associative' => true));
    }
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = wp_normalize_path(realpath($metadata_file));
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && str_starts_with($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (current_theme_supports('wp-block-styles') && wp_should_load_separate_core_block_assets()) {
            $metadata['style'] = (array) $metadata['style'];
            $metadata['style'][] = "wp-block-{$block_name}-theme";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('apiVersion' => 'api_version', 'title' => 'title', 'category' => 'category', 'parent' => 'parent', 'ancestor' => 'ancestor', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'selectors' => 'selectors', 'supports' => 'supports', 'styles' => 'styles', 'variations' => 'variations', 'example' => 'example');
    $textdomain = (!empty($metadata['textdomain'])) ? $metadata['textdomain'] : null;
    $i18n_schema = get_block_metadata_i18n_schema();
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
            if ($textdomain && isset($i18n_schema->{$key})) {
                $settings[$mapped_key] = translate_settings_using_i18n_schema($i18n_schema->{$key}, $settings[$key], $textdomain);
            }
        }
    }
    $script_fields = array('editorScript' => 'editor_script_handles', 'script' => 'script_handles', 'viewScript' => 'view_script_handles');
    foreach ($script_fields as $metadata_field_name => $settings_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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_scripts[] = $result;
                    }
                }
            } else {
                $result = register_block_script_handle($metadata, $metadata_field_name);
                if ($result) {
                    $processed_scripts[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_scripts;
        }
    }
    $style_fields = array('editorStyle' => 'editor_style_handles', 'style' => 'style_handles');
    foreach ($style_fields as $metadata_field_name => $settings_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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_styles[] = $result;
                    }
                }
            } else {
                $result = register_block_style_handle($metadata, $metadata_field_name);
                if ($result) {
                    $processed_styles[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_styles;
        }
    }
    if (!empty($metadata['render'])) {
        $template_path = wp_normalize_path(realpath(dirname($metadata['file']) . '/' . remove_block_asset_path_prefix($metadata['render'])));
        if ($template_path) {
            /**
             * Renders the block on the server.
             *
             * @since 6.1.0
             *
             * @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) {
                // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
                ob_start();
                require $template_path;
                return ob_get_clean();
            };
        }
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return WP_Block_Type_Registry::get_instance()->register($metadata['name'], $settings);
}

WordPress Version: 6.1

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields.
 * @since 5.9.0 Added support for `variations` and `viewScript` fields.
 * @since 6.1.0 Added support for `render` field.
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 *                               If providing the path to a JSON file, the filename must end with `block.json`.
 * @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_from_metadata($file_or_folder, $args = array())
{
    /*
     * Get an array of metadata from a PHP file.
     * This improves performance for core blocks as it's only necessary to read a single PHP file
     * instead of reading a JSON file per-block, and then decoding from JSON to PHP.
     * Using a static variable ensures that the metadata is only read once per request.
     */
    static $core_blocks_meta;
    if (!$core_blocks_meta) {
        $core_blocks_meta = include_once ABSPATH . WPINC . '/blocks/blocks-json.php';
    }
    $metadata_file = (!str_ends_with($file_or_folder, 'block.json')) ? trailingslashit($file_or_folder) . 'block.json' : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    // Try to get metadata from the static cache for core blocks.
    $metadata = false;
    if (str_starts_with($file_or_folder, ABSPATH . WPINC)) {
        $core_block_name = str_replace(ABSPATH . WPINC . '/blocks/', '', $file_or_folder);
        if (!empty($core_blocks_meta[$core_block_name])) {
            $metadata = $core_blocks_meta[$core_block_name];
        }
    }
    // If metadata is not found in the static cache, read it from the file.
    if (!$metadata) {
        $metadata = wp_json_file_decode($metadata_file, array('associative' => true));
    }
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = wp_normalize_path(realpath($metadata_file));
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && 0 === strpos($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('apiVersion' => 'api_version', 'title' => 'title', 'category' => 'category', 'parent' => 'parent', 'ancestor' => 'ancestor', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'variations' => 'variations', 'example' => 'example');
    $textdomain = (!empty($metadata['textdomain'])) ? $metadata['textdomain'] : null;
    $i18n_schema = get_block_metadata_i18n_schema();
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
            if ($textdomain && isset($i18n_schema->{$key})) {
                $settings[$mapped_key] = translate_settings_using_i18n_schema($i18n_schema->{$key}, $settings[$key], $textdomain);
            }
        }
    }
    $script_fields = array('editorScript' => 'editor_script_handles', 'script' => 'script_handles', 'viewScript' => 'view_script_handles');
    foreach ($script_fields as $metadata_field_name => $settings_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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_scripts[] = $result;
                    }
                }
            } else {
                $result = register_block_script_handle($metadata, $metadata_field_name);
                if ($result) {
                    $processed_scripts[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_scripts;
        }
    }
    $style_fields = array('editorStyle' => 'editor_style_handles', 'style' => 'style_handles');
    foreach ($style_fields as $metadata_field_name => $settings_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($metadata, $metadata_field_name, $index);
                    if ($result) {
                        $processed_styles[] = $result;
                    }
                }
            } else {
                $result = register_block_style_handle($metadata, $metadata_field_name);
                if ($result) {
                    $processed_styles[] = $result;
                }
            }
            $settings[$settings_field_name] = $processed_styles;
        }
    }
    if (!empty($metadata['render'])) {
        $template_path = wp_normalize_path(realpath(dirname($metadata['file']) . '/' . remove_block_asset_path_prefix($metadata['render'])));
        if ($template_path) {
            /**
             * Renders the block on the server.
             *
             * @since 6.1.0
             *
             * @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'] = function ($attributes, $content, $block) use ($template_path) {
                // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
                ob_start();
                require $template_path;
                return ob_get_clean();
            };
        }
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return WP_Block_Type_Registry::get_instance()->register($metadata['name'], $settings);
}

WordPress Version: 9.3

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields.
 * @since 5.9.0 Added support for `variations` and `viewScript` fields.
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 *                               If providing the path to a JSON file, the filename must end with `block.json`.
 * @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_from_metadata($file_or_folder, $args = array())
{
    $filename = 'block.json';
    $metadata_file = (substr($file_or_folder, -strlen($filename)) !== $filename) ? trailingslashit($file_or_folder) . $filename : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    $metadata = wp_json_file_decode($metadata_file, array('associative' => true));
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = wp_normalize_path(realpath($metadata_file));
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && 0 === strpos($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('apiVersion' => 'api_version', 'title' => 'title', 'category' => 'category', 'parent' => 'parent', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'variations' => 'variations', 'example' => 'example');
    $textdomain = (!empty($metadata['textdomain'])) ? $metadata['textdomain'] : null;
    $i18n_schema = get_block_metadata_i18n_schema();
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
            if ($textdomain && isset($i18n_schema->{$key})) {
                $settings[$mapped_key] = translate_settings_using_i18n_schema($i18n_schema->{$key}, $settings[$key], $textdomain);
            }
        }
    }
    if (!empty($metadata['editorScript'])) {
        $settings['editor_script'] = register_block_script_handle($metadata, 'editorScript');
    }
    if (!empty($metadata['script'])) {
        $settings['script'] = register_block_script_handle($metadata, 'script');
    }
    if (!empty($metadata['viewScript'])) {
        $settings['view_script'] = register_block_script_handle($metadata, 'viewScript');
    }
    if (!empty($metadata['editorStyle'])) {
        $settings['editor_style'] = register_block_style_handle($metadata, 'editorStyle');
    }
    if (!empty($metadata['style'])) {
        $settings['style'] = register_block_style_handle($metadata, 'style');
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return WP_Block_Type_Registry::get_instance()->register($metadata['name'], $settings);
}

WordPress Version: 5.9

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 * @since 5.7.0 Added support for `textdomain` field and i18n handling for all translatable fields.
 * @since 5.9.0 Added support for `variations` and `viewScript` fields.
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 *                               If providing the path to a JSON file, the filename must end with `block.json`.
 * @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_from_metadata($file_or_folder, $args = array())
{
    $filename = 'block.json';
    $metadata_file = (substr($file_or_folder, -strlen($filename)) !== $filename) ? trailingslashit($file_or_folder) . $filename : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    $metadata = wp_json_file_decode($metadata_file, array('associative' => true));
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = wp_normalize_path($metadata_file);
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && 0 === strpos($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('apiVersion' => 'api_version', 'title' => 'title', 'category' => 'category', 'parent' => 'parent', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'variations' => 'variations', 'example' => 'example');
    $textdomain = (!empty($metadata['textdomain'])) ? $metadata['textdomain'] : null;
    $i18n_schema = get_block_metadata_i18n_schema();
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
            if ($textdomain && isset($i18n_schema->{$key})) {
                $settings[$mapped_key] = translate_settings_using_i18n_schema($i18n_schema->{$key}, $settings[$key], $textdomain);
            }
        }
    }
    if (!empty($metadata['editorScript'])) {
        $settings['editor_script'] = register_block_script_handle($metadata, 'editorScript');
    }
    if (!empty($metadata['script'])) {
        $settings['script'] = register_block_script_handle($metadata, 'script');
    }
    if (!empty($metadata['viewScript'])) {
        $settings['view_script'] = register_block_script_handle($metadata, 'viewScript');
    }
    if (!empty($metadata['editorStyle'])) {
        $settings['editor_style'] = register_block_style_handle($metadata, 'editorStyle');
    }
    if (!empty($metadata['style'])) {
        $settings['style'] = register_block_style_handle($metadata, 'style');
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return WP_Block_Type_Registry::get_instance()->register($metadata['name'], $settings);
}

WordPress Version: 5.8

/**
 * Registers a block type from the metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 * @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_from_metadata($file_or_folder, $args = array())
{
    $filename = 'block.json';
    $metadata_file = (substr($file_or_folder, -strlen($filename)) !== $filename) ? trailingslashit($file_or_folder) . $filename : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    $metadata = json_decode(file_get_contents($metadata_file), true);
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = $metadata_file;
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    // Add `style` and `editor_style` for core blocks if missing.
    if (!empty($metadata['name']) && 0 === strpos($metadata['name'], 'core/')) {
        $block_name = str_replace('core/', '', $metadata['name']);
        if (!isset($metadata['style'])) {
            $metadata['style'] = "wp-block-{$block_name}";
        }
        if (!isset($metadata['editorStyle'])) {
            $metadata['editorStyle'] = "wp-block-{$block_name}-editor";
        }
    }
    $settings = array();
    $property_mappings = array('title' => 'title', 'category' => 'category', 'parent' => 'parent', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'example' => 'example', 'apiVersion' => 'api_version');
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $value = $metadata[$key];
            if (empty($metadata['textdomain'])) {
                $settings[$mapped_key] = $value;
                continue;
            }
            $textdomain = $metadata['textdomain'];
            switch ($key) {
                case 'title':
                case 'description':
                    // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralDomain
                    $settings[$mapped_key] = translate_with_gettext_context($value, sprintf('block %s', $key), $textdomain);
                    break;
                case 'keywords':
                    $settings[$mapped_key] = array();
                    if (!is_array($value)) {
                        continue 2;
                    }
                    foreach ($value as $keyword) {
                        // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
                        $settings[$mapped_key][] = translate_with_gettext_context($keyword, 'block keyword', $textdomain);
                    }
                    break;
                case 'styles':
                    $settings[$mapped_key] = array();
                    if (!is_array($value)) {
                        continue 2;
                    }
                    foreach ($value as $style) {
                        if (!empty($style['label'])) {
                            // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
                            $style['label'] = translate_with_gettext_context($style['label'], 'block style label', $textdomain);
                        }
                        $settings[$mapped_key][] = $style;
                    }
                    break;
                default:
                    $settings[$mapped_key] = $value;
            }
        }
    }
    if (!empty($metadata['editorScript'])) {
        $settings['editor_script'] = register_block_script_handle($metadata, 'editorScript');
    }
    if (!empty($metadata['script'])) {
        $settings['script'] = register_block_script_handle($metadata, 'script');
    }
    if (!empty($metadata['editorStyle'])) {
        $settings['editor_style'] = register_block_style_handle($metadata, 'editorStyle');
    }
    if (!empty($metadata['style'])) {
        $settings['style'] = register_block_style_handle($metadata, 'style');
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return WP_Block_Type_Registry::get_instance()->register($metadata['name'], $settings);
}

WordPress Version: 5.7

/**
 * Registers a block type from metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 * @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_from_metadata($file_or_folder, $args = array())
{
    $filename = 'block.json';
    $metadata_file = (substr($file_or_folder, -strlen($filename)) !== $filename) ? trailingslashit($file_or_folder) . $filename : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    $metadata = json_decode(file_get_contents($metadata_file), true);
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = $metadata_file;
    /**
     * Filters the metadata provided for registering a block type.
     *
     * @since 5.7.0
     *
     * @param array $metadata Metadata for registering a block type.
     */
    $metadata = apply_filters('block_type_metadata', $metadata);
    $settings = array();
    $property_mappings = array('title' => 'title', 'category' => 'category', 'parent' => 'parent', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'example' => 'example', 'apiVersion' => 'api_version');
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $value = $metadata[$key];
            if (empty($metadata['textdomain'])) {
                $settings[$mapped_key] = $value;
                continue;
            }
            $textdomain = $metadata['textdomain'];
            switch ($key) {
                case 'title':
                case 'description':
                    // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralDomain
                    $settings[$mapped_key] = translate_with_gettext_context($value, sprintf('block %s', $key), $textdomain);
                    break;
                case 'keywords':
                    $settings[$mapped_key] = array();
                    if (!is_array($value)) {
                        continue 2;
                    }
                    foreach ($value as $keyword) {
                        // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
                        $settings[$mapped_key][] = translate_with_gettext_context($keyword, 'block keyword', $textdomain);
                    }
                    break;
                case 'styles':
                    $settings[$mapped_key] = array();
                    if (!is_array($value)) {
                        continue 2;
                    }
                    foreach ($value as $style) {
                        if (!empty($style['label'])) {
                            // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
                            $style['label'] = translate_with_gettext_context($style['label'], 'block style label', $textdomain);
                        }
                        $settings[$mapped_key][] = $style;
                    }
                    break;
                default:
                    $settings[$mapped_key] = $value;
            }
        }
    }
    if (!empty($metadata['editorScript'])) {
        $settings['editor_script'] = register_block_script_handle($metadata, 'editorScript');
    }
    if (!empty($metadata['script'])) {
        $settings['script'] = register_block_script_handle($metadata, 'script');
    }
    if (!empty($metadata['editorStyle'])) {
        $settings['editor_style'] = register_block_style_handle($metadata, 'editorStyle');
    }
    if (!empty($metadata['style'])) {
        $settings['style'] = register_block_style_handle($metadata, 'style');
    }
    /**
     * Filters the settings determined from the block type metadata.
     *
     * @since 5.7.0
     *
     * @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', array_merge($settings, $args), $metadata);
    return register_block_type($metadata['name'], $settings);
}

WordPress Version: 5.6

/**
 * Registers a block type from metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 * @param array  $args {
 *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
 *     Any arguments may be defined, however the ones described below are supported by default.
 *     Default empty array.
 *
 *     @type callable $render_callback Callback used to render blocks of this block type.
 * }
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
 */
function register_block_type_from_metadata($file_or_folder, $args = array())
{
    $filename = 'block.json';
    $metadata_file = (substr($file_or_folder, -strlen($filename)) !== $filename) ? trailingslashit($file_or_folder) . $filename : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    $metadata = json_decode(file_get_contents($metadata_file), true);
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = $metadata_file;
    $settings = array();
    $property_mappings = array('title' => 'title', 'category' => 'category', 'parent' => 'parent', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'example' => 'example', 'apiVersion' => 'api_version');
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
        }
    }
    if (!empty($metadata['editorScript'])) {
        $settings['editor_script'] = register_block_script_handle($metadata, 'editorScript');
    }
    if (!empty($metadata['script'])) {
        $settings['script'] = register_block_script_handle($metadata, 'script');
    }
    if (!empty($metadata['editorStyle'])) {
        $settings['editor_style'] = register_block_style_handle($metadata, 'editorStyle');
    }
    if (!empty($metadata['style'])) {
        $settings['style'] = register_block_style_handle($metadata, 'style');
    }
    return register_block_type($metadata['name'], array_merge($settings, $args));
}

WordPress Version: 5.5

/**
 * Registers a block type from metadata stored in the `block.json` file.
 *
 * @since 5.5.0
 *
 * @param string $file_or_folder Path to the JSON file with metadata definition for
 *                               the block or path to the folder where the `block.json` file is located.
 * @param array  $args {
 *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
 *     Any arguments may be defined, however the ones described below are supported by default.
 *     Default empty array.
 *
 *     @type callable $render_callback Callback used to render blocks of this block type.
 * }
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
 */
function register_block_type_from_metadata($file_or_folder, $args = array())
{
    $filename = 'block.json';
    $metadata_file = (substr($file_or_folder, -strlen($filename)) !== $filename) ? trailingslashit($file_or_folder) . $filename : $file_or_folder;
    if (!file_exists($metadata_file)) {
        return false;
    }
    $metadata = json_decode(file_get_contents($metadata_file), true);
    if (!is_array($metadata) || empty($metadata['name'])) {
        return false;
    }
    $metadata['file'] = $metadata_file;
    $settings = array();
    $property_mappings = array('title' => 'title', 'category' => 'category', 'parent' => 'parent', 'icon' => 'icon', 'description' => 'description', 'keywords' => 'keywords', 'attributes' => 'attributes', 'providesContext' => 'provides_context', 'usesContext' => 'uses_context', 'supports' => 'supports', 'styles' => 'styles', 'example' => 'example');
    foreach ($property_mappings as $key => $mapped_key) {
        if (isset($metadata[$key])) {
            $settings[$mapped_key] = $metadata[$key];
        }
    }
    if (!empty($metadata['editorScript'])) {
        $settings['editor_script'] = register_block_script_handle($metadata, 'editorScript');
    }
    if (!empty($metadata['script'])) {
        $settings['script'] = register_block_script_handle($metadata, 'script');
    }
    if (!empty($metadata['editorStyle'])) {
        $settings['editor_style'] = register_block_style_handle($metadata, 'editorStyle');
    }
    if (!empty($metadata['style'])) {
        $settings['style'] = register_block_style_handle($metadata, 'style');
    }
    return register_block_type($metadata['name'], array_merge($settings, $args));
}