Make WordPress Core

Ticket #62002: add-metadata-registry.diff

File add-metadata-registry.diff, 11.4 KB (added by mreishus, 5 months ago)
  • src/wp-includes/blocks.php

    diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php
    index 9b24b989d4..b97a1e18ab 100644
    a b function get_block_metadata_i18n_schema() { 
    375375        return $i18n_block_schema;
    376376}
    377377
     378/**
     379 * Registers block metadata from a given source.
     380 *
     381 * This function allows core and third-party plugins to register their block metadata
     382 * in a centralized location. Registering metadata can improve performance by avoiding
     383 * multiple reads from the filesystem.
     384 *
     385 * @since 6.X.0
     386 *
     387 * @param string $namespace The namespace for the metadata (e.g., 'core', 'mythirdpartyplugin').
     388 * @param string $source    The source identifier for the metadata within the namespace.
     389 *                          This can be a unique identifier for your plugin's blocks.
     390 * @param array  $metadata  The block metadata to be registered.
     391 */
     392function wp_register_block_metadata( $namespace, $source, $metadata ) {
     393        WP_Block_Metadata_Registry::get_instance()->register( $namespace, $source, $metadata );
     394}
     395
    378396/**
    379397 * Registers a block type from the metadata stored in the `block.json` file.
    380398 *
    function get_block_metadata_i18n_schema() { 
    393411 * @param array  $args           Optional. Array of block type arguments. Accepts any public property
    394412 *                               of `WP_Block_Type`. See WP_Block_Type::__construct() for information
    395413 *                               on accepted arguments. Default empty array.
     414 * @param string $metadata_source Optional. The source identifier for the metadata in the format `namespace/source`.
     415 *                                The namespace is a unique identifier for your plugin or theme, and the source
     416 *                                is a unique identifier for your block's metadata within that namespace.
     417 *                                If provided, the function will attempt to retrieve the block's metadata
     418 *                                from the `WP_Block_Metadata_Registry` before falling back to reading
     419 *                                from the JSON file. Default empty string.
    396420 * @return WP_Block_Type|false The registered block type on success, or false on failure.
    397421 */
    398 function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
    399         /*
    400          * Get an array of metadata from a PHP file.
    401          * This improves performance for core blocks as it's only necessary to read a single PHP file
    402          * instead of reading a JSON file per-block, and then decoding from JSON to PHP.
    403          * Using a static variable ensures that the metadata is only read once per request.
    404          */
    405         static $core_blocks_meta;
    406         if ( ! $core_blocks_meta ) {
    407                 $core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
    408         }
    409 
    410         $metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ?
    411                 trailingslashit( $file_or_folder ) . 'block.json' :
    412                 $file_or_folder;
     422function register_block_type_from_metadata( $file_or_folder, $args = array(), $metadata_source = '' ) {
     423        $metadata = array();
     424        $registry = WP_Block_Metadata_Registry::get_instance();
    413425
    414426        $is_core_block = str_starts_with( $file_or_folder, ABSPATH . WPINC );
    415         // If the block is not a core block, the metadata file must exist.
    416         $metadata_file_exists = $is_core_block || file_exists( $metadata_file );
    417         if ( ! $metadata_file_exists && empty( $args['name'] ) ) {
    418                 return false;
    419         }
    420427
    421         // Try to get metadata from the static cache for core blocks.
    422         $metadata = array();
    423428        if ( $is_core_block ) {
    424429                $core_block_name = str_replace( ABSPATH . WPINC . '/blocks/', '', $file_or_folder );
    425                 if ( ! empty( $core_blocks_meta[ $core_block_name ] ) ) {
    426                         $metadata = $core_blocks_meta[ $core_block_name ];
     430                $metadata = $registry->get_metadata( 'core', $core_block_name );
     431
     432                if ( null === $metadata ) {
     433                        // Load core metadata if not already registered.
     434                        $core_blocks_meta = require ABSPATH . WPINC . '/blocks/blocks-json.php';
     435                        foreach ( $core_blocks_meta as $block_name => $block_meta ) {
     436                                wp_register_block_metadata( 'core', $block_name, $block_meta );
     437                        }
     438                        $metadata = $registry->get_metadata( 'core', $core_block_name );
     439                }
     440        } elseif ( $metadata_source ) {
     441                // Parse the metadata_source to get namespace and source
     442                $parts = explode( '/', $metadata_source, 2 );
     443                if ( count( $parts ) === 2 ) {
     444                        $namespace = $parts[0];
     445                        $source = $parts[1];
     446                        $metadata = $registry->get_metadata( $namespace, $source );
    427447                }
    428448        }
    429449
    430         // If metadata is not found in the static cache, read it from the file.
    431         if ( $metadata_file_exists && empty( $metadata ) ) {
    432                 $metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
     450        // If metadata is not found in the registry, read from JSON file.
     451        $metadata_file_exists = false;
     452        if ( empty( $metadata ) ) {
     453                $metadata_file = ( ! str_ends_with( $file_or_folder, 'block.json' ) ) ?
     454                        trailingslashit( $file_or_folder ) . 'block.json' :
     455                        $file_or_folder;
     456
     457                $metadata_file_exists = file_exists( $metadata_file );
     458                if ( $metadata_file_exists ) {
     459                        $metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
     460                } else if ( ! $metadata_file_exists && empty( $args['name'] ) ) {
     461                        return false;
     462                }
    433463        }
    434464
    435465        if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) {
  • new file src/wp-includes/class-wp-block-metadata-registry.php

    diff --git a/src/wp-includes/class-wp-block-metadata-registry.php b/src/wp-includes/class-wp-block-metadata-registry.php
    new file mode 100644
    index 0000000000..a11ca34764
    - +  
     1<?php
     2/**
     3 * Block Metadata Registry
     4 *
     5 * @package WordPress
     6 * @subpackage Blocks
     7 * @since 6.X.0
     8 */
     9
     10/**
     11 * Class used for managing block metadata from various sources.
     12 *
     13 * @since 6.X.0
     14 */
     15class WP_Block_Metadata_Registry {
     16
     17        /**
     18         * Holds the instance of this class.
     19         *
     20         * @since 6.X.0
     21         * @var WP_Block_Metadata_Registry
     22         */
     23        private static $instance;
     24
     25        /**
     26         * Container for storing block metadata.
     27         *
     28         * @since 6.X.0
     29         * @var array
     30         */
     31        private $metadata = array();
     32
     33        /**
     34         * Registers block metadata for a given source and namespace.
     35         *
     36         * @since 6.X.0
     37         *
     38         * @param string $namespace The namespace for the metadata (e.g., 'core', 'mythirdpartyplugin').
     39         * @param string $source        The source identifier for the metadata within the namespace.
     40         * @param array  $metadata      The block metadata.
     41         */
     42        public function register( $namespace, $source, $metadata ) {
     43                if ( ! isset( $this->metadata[ $namespace ] ) ) {
     44                        $this->metadata[ $namespace ] = array();
     45                }
     46                $this->metadata[ $namespace ][ $source ] = $metadata;
     47        }
     48
     49        /**
     50         * Retrieves block metadata for a given namespace and source.
     51         *
     52         * @since 6.X.0
     53         *
     54         * @param string $namespace The namespace for the metadata.
     55         * @param string $source        The source identifier for the metadata within the namespace.
     56         * @return array|null The block metadata for the source, or null if not found.
     57         */
     58        public function get_metadata( $namespace, $source ) {
     59                return isset( $this->metadata[ $namespace ][ $source ] ) ? $this->metadata[ $namespace ][ $source ] : null;
     60        }
     61
     62        /**
     63         * Retrieves the instance of this class.
     64         *
     65         * @since 6.X.0
     66         *
     67         * @return WP_Block_Metadata_Registry The instance.
     68         */
     69        public static function get_instance() {
     70                if ( null === self::$instance ) {
     71                        self::$instance = new self();
     72                }
     73                return self::$instance;
     74        }
     75}
  • src/wp-settings.php

    diff --git a/src/wp-settings.php b/src/wp-settings.php
    index d3dfe5776e..a152dd3f4f 100644
    a b require ABSPATH . WPINC . '/class-wp-block-styles-registry.php'; 
    353353require ABSPATH . WPINC . '/class-wp-block-type-registry.php';
    354354require ABSPATH . WPINC . '/class-wp-block.php';
    355355require ABSPATH . WPINC . '/class-wp-block-list.php';
     356require ABSPATH . WPINC . '/class-wp-block-metadata-registry.php';
    356357require ABSPATH . WPINC . '/class-wp-block-parser-block.php';
    357358require ABSPATH . WPINC . '/class-wp-block-parser-frame.php';
    358359require ABSPATH . WPINC . '/class-wp-block-parser.php';
  • tests/phpunit/tests/blocks/register.php

    diff --git a/tests/phpunit/tests/blocks/register.php b/tests/phpunit/tests/blocks/register.php
    index 7e0c391e1f..770470ea0c 100644
    a b class Tests_Blocks_Register extends WP_UnitTestCase { 
    15011501                        $block_type->block_hooks
    15021502                );
    15031503        }
     1504
     1505        /**
     1506         * Tests registering a third-party block type using metadata from the registry.
     1507         *
     1508         * This test ensures that a block type can be registered using metadata stored
     1509         * in the `WP_Block_Metadata_Registry` by providing a `$metadata_source` argument.
     1510         *
     1511         * @covers ::register_block_type_from_metadata
     1512         */
     1513        public function test_register_block_type_from_registry_metadata() {
     1514                $metadata = array(
     1515                        'name' => 'test/block-from-registry',
     1516                        'title' => 'Test Block From Registry',
     1517                        'category' => 'widgets',
     1518                        'icon' => 'smiley',
     1519                        'description' => 'This is a test block.',
     1520                );
     1521
     1522                $registry = WP_Block_Metadata_Registry::get_instance();
     1523                $registry->register( 'test', 'block-from-registry', $metadata );
     1524
     1525                $result = register_block_type_from_metadata( 'nonexistent/path', array(), 'test/block-from-registry' );
     1526
     1527                $this->assertInstanceOf( 'WP_Block_Type', $result );
     1528                $this->assertEquals( 'test/block-from-registry', $result->name );
     1529        }
     1530
     1531        /**
     1532         * Tests registering a block type from a `block.json` file.
     1533         *
     1534         * This test ensures that `register_block_type_from_metadata()` can still register
     1535         * a block type using metadata from a `block.json` file when no `$metadata_source`
     1536         * argument is provided.
     1537         *
     1538         * @covers ::register_block_type_from_metadata
     1539         */
     1540        public function test_register_block_type_from_json_file() {
     1541                $temp_dir = get_temp_dir();
     1542                $block_json = $temp_dir . 'block.json';
     1543                file_put_contents( $block_json, json_encode( array(
     1544                        'name' => 'test/json-block',
     1545                        'title' => 'Test JSON Block',
     1546                ) ) );
     1547
     1548                $result = register_block_type_from_metadata( $temp_dir );
     1549
     1550                $this->assertInstanceOf( 'WP_Block_Type', $result );
     1551                $this->assertEquals( 'test/json-block', $result->name );
     1552
     1553                unlink( $block_json );
     1554        }
    15041555}
  • new file tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php

    diff --git a/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php b/tests/phpunit/tests/blocks/wpBlockMetadataRegistry.php
    new file mode 100644
    index 0000000000..7cac9c6803
    - +  
     1<?php
     2
     3/**
     4 * Tests for WP_Block_Metadata_Registry class.
     5 *
     6 * @group blocks
     7 */
     8class Tests_Blocks_WpBlockMetadataRegistry extends WP_UnitTestCase {
     9        /**
     10         * @var WP_Block_Metadata_Registry
     11         */
     12        private $registry;
     13
     14        public function set_up() {
     15                parent::set_up();
     16                $this->registry = WP_Block_Metadata_Registry::get_instance();
     17        }
     18
     19        public function test_register_and_get_metadata() {
     20                $namespace = 'test-namespace';
     21                $source = 'test-source';
     22                $metadata = array( 'name' => 'test-block', 'title' => 'Test Block' );
     23
     24                $this->registry->register( $namespace, $source, $metadata );
     25
     26                $retrieved_metadata = $this->registry->get_metadata( $namespace, $source );
     27                $this->assertEquals( $metadata, $retrieved_metadata );
     28        }
     29
     30        public function test_get_nonexistent_metadata() {
     31                $retrieved_metadata = $this->registry->get_metadata( 'nonexistent', 'nonexistent' );
     32                $this->assertNull( $retrieved_metadata );
     33        }
     34
     35        public function test_singleton_instance() {
     36                $instance1 = WP_Block_Metadata_Registry::get_instance();
     37                $instance2 = WP_Block_Metadata_Registry::get_instance();
     38
     39                $this->assertSame( $instance1, $instance2 );
     40        }
     41}