Make WordPress Core

Changeset 57026


Ignore:
Timestamp:
10/28/2023 01:00:14 AM (3 years ago)
Author:
SergeyBiryukov
Message:

Blocks: Parse the arguments earlier in register_block_type_from_metadata().

This makes it possible to register a block by passing an array of arguments, without the presence of a block.json file.

Follow-up to [48141], [49948].

Props aristath, spacedmonkey, mukesh27, costdev, audrasjb, oglekler, felipeelia, hellofromTonya.
Fixes #56865.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/blocks.php

    r56970 r57026  
    353353
    354354        $is_core_block = str_starts_with( $file_or_folder, ABSPATH . WPINC );
    355 
    356         if ( ! $is_core_block && ! file_exists( $metadata_file ) ) {
     355        // If the block is not a core block, the metadata file must exist.
     356        $metadata_file_exists = $is_core_block || file_exists( $metadata_file );
     357        if ( ! $metadata_file_exists && empty( $args['name'] ) ) {
    357358                return false;
    358359        }
    359360
    360361        // Try to get metadata from the static cache for core blocks.
    361         $metadata = false;
     362        $metadata = array();
    362363        if ( $is_core_block ) {
    363364                $core_block_name = str_replace( ABSPATH . WPINC . '/blocks/', '', $file_or_folder );
     
    368369
    369370        // If metadata is not found in the static cache, read it from the file.
    370         if ( ! $metadata ) {
     371        if ( $metadata_file_exists && empty( $metadata ) ) {
    371372                $metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) );
    372373        }
    373374
    374         if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
     375        if ( ! is_array( $metadata ) || ( empty( $metadata['name'] ) && empty( $args['name'] ) ) ) {
    375376                return false;
    376377        }
    377         $metadata['file'] = wp_normalize_path( realpath( $metadata_file ) );
     378
     379        $metadata['file'] = $metadata_file_exists ? wp_normalize_path( realpath( $metadata_file ) ) : null;
    378380
    379381        /**
     
    405407        $property_mappings = array(
    406408                'apiVersion'      => 'api_version',
     409                'name'            => 'name',
    407410                'title'           => 'title',
    408411                'category'        => 'category',
     
    427430                if ( isset( $metadata[ $key ] ) ) {
    428431                        $settings[ $mapped_key ] = $metadata[ $key ];
    429                         if ( $textdomain && isset( $i18n_schema->$key ) ) {
     432                        if ( $metadata_file_exists && $textdomain && isset( $i18n_schema->$key ) ) {
    430433                                $settings[ $mapped_key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $settings[ $key ], $textdomain );
    431434                        }
    432                 }
    433         }
    434 
    435         $script_fields = array(
    436                 'editorScript' => 'editor_script_handles',
    437                 'script'       => 'script_handles',
    438                 'viewScript'   => 'view_script_handles',
    439         );
    440         foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
    441                 if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
    442                         $scripts           = $metadata[ $metadata_field_name ];
    443                         $processed_scripts = array();
    444                         if ( is_array( $scripts ) ) {
    445                                 for ( $index = 0; $index < count( $scripts ); $index++ ) {
    446                                         $result = register_block_script_handle(
    447                                                 $metadata,
    448                                                 $metadata_field_name,
    449                                                 $index
    450                                         );
    451                                         if ( $result ) {
    452                                                 $processed_scripts[] = $result;
    453                                         }
    454                                 }
    455                         } else {
    456                                 $result = register_block_script_handle(
    457                                         $metadata,
    458                                         $metadata_field_name
    459                                 );
    460                                 if ( $result ) {
    461                                         $processed_scripts[] = $result;
    462                                 }
    463                         }
    464                         $settings[ $settings_field_name ] = $processed_scripts;
    465                 }
    466         }
    467 
    468         $style_fields = array(
    469                 'editorStyle' => 'editor_style_handles',
    470                 'style'       => 'style_handles',
    471         );
    472         foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
    473                 if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
    474                         $styles           = $metadata[ $metadata_field_name ];
    475                         $processed_styles = array();
    476                         if ( is_array( $styles ) ) {
    477                                 for ( $index = 0; $index < count( $styles ); $index++ ) {
    478                                         $result = register_block_style_handle(
    479                                                 $metadata,
    480                                                 $metadata_field_name,
    481                                                 $index
    482                                         );
    483                                         if ( $result ) {
    484                                                 $processed_styles[] = $result;
    485                                         }
    486                                 }
    487                         } else {
    488                                 $result = register_block_style_handle(
    489                                         $metadata,
    490                                         $metadata_field_name
    491                                 );
    492                                 if ( $result ) {
    493                                         $processed_styles[] = $result;
    494                                 }
    495                         }
    496                         $settings[ $settings_field_name ] = $processed_styles;
    497                 }
    498         }
    499 
    500         if ( ! empty( $metadata['blockHooks'] ) ) {
    501                 /**
    502                  * Map camelCased position string (from block.json) to snake_cased block type position.
    503                  *
    504                  * @var array
    505                  */
    506                 $position_mappings = array(
    507                         'before'     => 'before',
    508                         'after'      => 'after',
    509                         'firstChild' => 'first_child',
    510                         'lastChild'  => 'last_child',
    511                 );
    512 
    513                 $settings['block_hooks'] = array();
    514                 foreach ( $metadata['blockHooks'] as $anchor_block_name => $position ) {
    515                         // Avoid infinite recursion (hooking to itself).
    516                         if ( $metadata['name'] === $anchor_block_name ) {
    517                                 _doing_it_wrong(
    518                                         __METHOD__,
    519                                         __( 'Cannot hook block to itself.' ),
    520                                         '6.4.0'
    521                                 );
    522                                 continue;
    523                         }
    524 
    525                         if ( ! isset( $position_mappings[ $position ] ) ) {
    526                                 continue;
    527                         }
    528 
    529                         $settings['block_hooks'][ $anchor_block_name ] = $position_mappings[ $position ];
    530435                }
    531436        }
     
    558463        }
    559464
     465        $settings = array_merge( $settings, $args );
     466
     467        $script_fields = array(
     468                'editorScript' => 'editor_script_handles',
     469                'script'       => 'script_handles',
     470                'viewScript'   => 'view_script_handles',
     471        );
     472        foreach ( $script_fields as $metadata_field_name => $settings_field_name ) {
     473                if ( ! empty( $settings[ $metadata_field_name ] ) ) {
     474                        $metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
     475                }
     476                if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
     477                        $scripts           = $metadata[ $metadata_field_name ];
     478                        $processed_scripts = array();
     479                        if ( is_array( $scripts ) ) {
     480                                for ( $index = 0; $index < count( $scripts ); $index++ ) {
     481                                        $result = register_block_script_handle(
     482                                                $metadata,
     483                                                $metadata_field_name,
     484                                                $index
     485                                        );
     486                                        if ( $result ) {
     487                                                $processed_scripts[] = $result;
     488                                        }
     489                                }
     490                        } else {
     491                                $result = register_block_script_handle(
     492                                        $metadata,
     493                                        $metadata_field_name
     494                                );
     495                                if ( $result ) {
     496                                        $processed_scripts[] = $result;
     497                                }
     498                        }
     499                        $settings[ $settings_field_name ] = $processed_scripts;
     500                }
     501        }
     502
     503        $style_fields = array(
     504                'editorStyle' => 'editor_style_handles',
     505                'style'       => 'style_handles',
     506        );
     507        foreach ( $style_fields as $metadata_field_name => $settings_field_name ) {
     508                if ( ! empty( $settings[ $metadata_field_name ] ) ) {
     509                        $metadata[ $metadata_field_name ] = $settings[ $metadata_field_name ];
     510                }
     511                if ( ! empty( $metadata[ $metadata_field_name ] ) ) {
     512                        $styles           = $metadata[ $metadata_field_name ];
     513                        $processed_styles = array();
     514                        if ( is_array( $styles ) ) {
     515                                for ( $index = 0; $index < count( $styles ); $index++ ) {
     516                                        $result = register_block_style_handle(
     517                                                $metadata,
     518                                                $metadata_field_name,
     519                                                $index
     520                                        );
     521                                        if ( $result ) {
     522                                                $processed_styles[] = $result;
     523                                        }
     524                                }
     525                        } else {
     526                                $result = register_block_style_handle(
     527                                        $metadata,
     528                                        $metadata_field_name
     529                                );
     530                                if ( $result ) {
     531                                        $processed_styles[] = $result;
     532                                }
     533                        }
     534                        $settings[ $settings_field_name ] = $processed_styles;
     535                }
     536        }
     537
     538        if ( ! empty( $metadata['blockHooks'] ) ) {
     539                /**
     540                 * Map camelCased position string (from block.json) to snake_cased block type position.
     541                 *
     542                 * @var array
     543                 */
     544                $position_mappings = array(
     545                        'before'     => 'before',
     546                        'after'      => 'after',
     547                        'firstChild' => 'first_child',
     548                        'lastChild'  => 'last_child',
     549                );
     550
     551                $settings['block_hooks'] = array();
     552                foreach ( $metadata['blockHooks'] as $anchor_block_name => $position ) {
     553                        // Avoid infinite recursion (hooking to itself).
     554                        if ( $metadata['name'] === $anchor_block_name ) {
     555                                _doing_it_wrong(
     556                                        __METHOD__,
     557                                        __( 'Cannot hook block to itself.' ),
     558                                        '6.4.0'
     559                                );
     560                                continue;
     561                        }
     562
     563                        if ( ! isset( $position_mappings[ $position ] ) ) {
     564                                continue;
     565                        }
     566
     567                        $settings['block_hooks'][ $anchor_block_name ] = $position_mappings[ $position ];
     568                }
     569        }
     570
    560571        /**
    561572         * Filters the settings determined from the block type metadata.
     
    566577         * @param array $metadata Metadata provided for registering a block type.
    567578         */
    568         $settings = apply_filters(
    569                 'block_type_metadata_settings',
    570                 array_merge(
    571                         $settings,
    572                         $args
    573                 ),
    574                 $metadata
    575         );
     579        $settings = apply_filters( 'block_type_metadata_settings', $settings, $metadata );
     580
     581        $metadata['name'] = ! empty( $settings['name'] ) ? $settings['name'] : $metadata['name'];
    576582
    577583        return WP_Block_Type_Registry::get_instance()->register(
  • trunk/tests/phpunit/tests/blocks/register.php

    r56607 r57026  
    601601
    602602        /**
     603         * Tests registering a block using arguments instead of a block.json file.
     604         *
     605         * @ticket 56865
     606         *
     607         * @covers ::register_block_type_from_metadata
     608         */
     609        public function test_register_block_type_from_metadata_with_arguments() {
     610                $result = register_block_type_from_metadata(
     611                        '',
     612                        array(
     613                                'api_version' => 2,
     614                                'name'        => 'tests/notice-from-array',
     615                                'title'       => 'Notice from array',
     616                                'category'    => 'common',
     617                                'icon'        => 'star',
     618                                'description' => 'Shows warning, error or success notices… (registered from an array)',
     619                                'keywords'    => array(
     620                                        'alert',
     621                                        'message',
     622                                ),
     623                                'textdomain'  => 'notice-from-array',
     624                        )
     625                );
     626
     627                $this->assertInstanceOf( 'WP_Block_Type', $result, 'The block was not registered' );
     628                $this->assertSame( 2, $result->api_version, 'The API version is incorrect' );
     629                $this->assertSame( 'tests/notice-from-array', $result->name, 'The block name is incorrect' );
     630                $this->assertSame( 'Notice from array', $result->title, 'The block title is incorrect' );
     631                $this->assertSame( 'common', $result->category, 'The block category is incorrect' );
     632                $this->assertSame( 'star', $result->icon, 'The block icon is incorrect' );
     633                $this->assertSame(
     634                        'Shows warning, error or success notices… (registered from an array)',
     635                        $result->description,
     636                        'The block description is incorrect'
     637                );
     638                $this->assertSameSets( array( 'alert', 'message' ), $result->keywords, 'The block keywords are incorrect' );
     639        }
     640
     641        /**
     642         * Tests that defined $args can properly override the block.json file.
     643         *
     644         * @ticket 56865
     645         *
     646         * @covers ::register_block_type_from_metadata
     647         */
     648        public function test_block_registers_with_args_override() {
     649                $result = register_block_type_from_metadata(
     650                        DIR_TESTDATA . '/blocks/notice',
     651                        array(
     652                                'name'  => 'tests/notice-with-overrides',
     653                                'title' => 'Overriden title',
     654                                'style' => array( 'tests-notice-style-overridden' ),
     655                        )
     656                );
     657
     658                $this->assertInstanceOf( 'WP_Block_Type', $result, 'The block was not registered' );
     659                $this->assertSame( 2, $result->api_version, 'The API version is incorrect' );
     660                $this->assertSame( 'tests/notice-with-overrides', $result->name, 'The block name was not overridden' );
     661                $this->assertSame( 'Overriden title', $result->title, 'The block title was not overridden' );
     662                $this->assertSameSets(
     663                        array( 'tests-notice-editor-script' ),
     664                        $result->editor_script_handles,
     665                        'The block editor script is incorrect'
     666                );
     667                $this->assertSameSets(
     668                        array( 'tests-notice-style-overridden' ),
     669                        $result->style_handles,
     670                        'The block style was not overridden'
     671                );
     672                $this->assertIsCallable( $result->render_callback );
     673        }
     674
     675        /**
     676         * Tests that when the `name` is missing, `register_block_type_from_metadata()`
     677         * will return `false`.
     678         *
     679         * @ticket 56865
     680         *
     681         * @covers ::register_block_type_from_metadata
     682         *
     683         * @dataProvider data_register_block_registers_with_args_override_returns_false_when_name_is_missing
     684         *
     685         * @param string $file The metadata file.
     686         * @param array  $args Array of block type arguments.
     687         */
     688        public function test_block_registers_with_args_override_returns_false_when_name_is_missing( $file, $args ) {
     689                $this->assertFalse( register_block_type_from_metadata( $file, $args ) );
     690        }
     691
     692        /**
     693         * Data provider.
     694         *
     695         * @return array[]
     696         */
     697        public function data_register_block_registers_with_args_override_returns_false_when_name_is_missing() {
     698                return array(
     699                        'no block.json file and no name argument' => array(
     700                                'file' => '', // No block.json file.
     701                                'args' => array(
     702                                        'title' => 'Overriden title',
     703                                        'style' => array( 'tests-notice-style-overridden' ),
     704                                ),
     705                        ),
     706                        'existing file and args not an array'     => array(
     707                                // A file that exists but is empty. This will bypass the file_exists() check.
     708                                'file' => DIR_TESTDATA . '/blocks/notice/block.js',
     709                                'args' => false,
     710                        ),
     711                        'existing file and args[name] missing'    => array(
     712                                // A file that exists but is empty. This will bypass the file_exists() check.
     713                                'file' => DIR_TESTDATA . '/blocks/notice/block.js',
     714                                'args' => array(
     715                                        'title' => 'Overriden title',
     716                                        'style' => array( 'tests-notice-style-overridden' ),
     717                                ),
     718                        ),
     719                );
     720        }
     721
     722        /**
    603723         * Tests that the function returns the registered block when the `block.json`
    604724         * is found in the fixtures directory.
Note: See TracChangeset for help on using the changeset viewer.