Make WordPress Core

Changeset 57526


Ignore:
Timestamp:
02/02/2024 08:22:11 PM (3 years ago)
Author:
gziolo
Message:

Editor: Refactor the way block bindings sources are handled

It fixes the coding style issues reported. It goes further and improves the code quality it other places where the logic for block bindings was added.

Follow-up for [57514].
Props: gziolo, mukesh27, youknowriad, santosguillamot.
See #60282.

Location:
trunk
Files:
2 added
1 deleted
5 edited
1 moved

Legend:

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

    r57514 r57526  
    2020 * @since 6.5.0
    2121 *
    22  * @param string   $source_name       The name of the source. It must be a string containing a namespace prefix, i.e.
    23  *                                    `my-plugin/my-custom-source`. It must only contain lowercase alphanumeric
    24  *                                    characters, the forward slash `/` and dashes.
    25  * @param array    $source_properties {
     22 * @param string $source_name       The name of the source. It must be a string containing a namespace prefix, i.e.
     23 *                                  `my-plugin/my-custom-source`. It must only contain lowercase alphanumeric
     24 *                                  characters, the forward slash `/` and dashes.
     25 * @param array  $source_properties {
    2626 *     The array of arguments that are used to register a source.
    2727 *
     
    4141 * @return array|false Source when the registration was successful, or `false` on failure.
    4242 */
    43 function register_block_bindings_source( $source_name, array $source_properties ) {
     43function register_block_bindings_source( string $source_name, array $source_properties ) {
    4444        return WP_Block_Bindings_Registry::get_instance()->register( $source_name, $source_properties );
    4545}
     
    5353 * @return array|false The unregistered block bindings source on success and `false` otherwise.
    5454 */
    55 function unregister_block_bindings_source( $source_name ) {
     55function unregister_block_bindings_source( string $source_name ) {
    5656        return WP_Block_Bindings_Registry::get_instance()->unregister( $source_name );
    5757}
     
    6767        return WP_Block_Bindings_Registry::get_instance()->get_all_registered();
    6868}
     69
     70/**
     71 * Retrieves a registered block bindings source.
     72 *
     73 * @since 6.5.0
     74 *
     75 * @param string $source_name The name of the source.
     76 * @return array|null The registered block bindings source, or `null` if it is not registered.
     77 */
     78function get_block_bindings_source( string $source_name ) {
     79        return WP_Block_Bindings_Registry::get_instance()->get_registered( $source_name );
     80}
  • trunk/src/wp-includes/class-wp-block-bindings-registry.php

    r57514 r57526  
    5858         *                                                                              i.e. {"key": "foo"}.
    5959         *                                            - @param WP_Block $block_instance The block instance.
    60          *                                            - @param string   $attribute_name The name of an attribute .
     60         *                                            - @param string   $attribute_name The name of the target attribute.
    6161         *                                        The callback has a mixed return type; it may return a string to override
    6262         *                                        the block's original value, null, false to remove an attribute, etc.
     
    6464         * @return array|false Source when the registration was successful, or `false` on failure.
    6565         */
    66         public function register( $source_name, array $source_properties ) {
     66        public function register( string $source_name, array $source_properties ) {
    6767                if ( ! is_string( $source_name ) ) {
    6868                        _doing_it_wrong(
     
    121121         * @return array|false The unregistered block bindings source on success and `false` otherwise.
    122122         */
    123         public function unregister( $source_name ) {
     123        public function unregister( string $source_name ) {
    124124                if ( ! $this->is_registered( $source_name ) ) {
    125125                        _doing_it_wrong(
     
    157157         * @return array|null The registered block bindings source, or `null` if it is not registered.
    158158         */
    159         public function get_registered( $source_name ) {
     159        public function get_registered( string $source_name ) {
    160160                if ( ! $this->is_registered( $source_name ) ) {
    161161                        return null;
  • trunk/src/wp-includes/class-wp-block.php

    r57514 r57526  
    212212         *     "bindings": {
    213213         *       "title": {
    214          *         "source": "post_meta",
     214         *         "source": "core/post-meta",
    215215         *         "args": { "key": "text_custom_field" }
    216216         *       },
    217217         *       "url": {
    218          *         "source": "post_meta",
     218         *         "source": "core/post-meta",
    219219         *         "args": { "key": "url_custom_field" }
    220220         *       }
     
    227227         * block with the values of the `text_custom_field` and `url_custom_field` post meta.
    228228         *
    229          * @access private
    230229         * @since 6.5.0
    231230         *
    232          * @param string   $block_content Block content.
    233          * @param array    $block The full block, including name and attributes.
     231         * @param string $block_content Block content.
     232         * @param array  $block         The full block, including name and attributes.
     233         * @return string The modified block content.
    234234         */
    235235        private function process_block_bindings( $block_content ) {
    236                 $block = $this->parsed_block;
     236                $parsed_block = $this->parsed_block;
    237237
    238238                // Allowed blocks that support block bindings.
     
    247247                // If the block doesn't have the bindings property, isn't one of the allowed
    248248                // block types, or the bindings property is not an array, return the block content.
    249                 if ( ! isset( $block['attrs']['metadata']['bindings'] ) ||
    250                                 ! is_array( $block['attrs']['metadata']['bindings'] ) ||
    251                                 ! isset( $allowed_blocks[ $this->name ] )
    252                                 ) {
     249                if (
     250                        ! isset( $allowed_blocks[ $this->name ] ) ||
     251                        empty( $parsed_block['attrs']['metadata']['bindings'] ) ||
     252                        ! is_array( $parsed_block['attrs']['metadata']['bindings'] )
     253                ) {
    253254                        return $block_content;
    254255                }
    255256
    256                 $block_bindings_sources = get_all_registered_block_bindings_sources();
    257257                $modified_block_content = $block_content;
    258                 foreach ( $block['attrs']['metadata']['bindings'] as $binding_attribute => $binding_source ) {
    259                         // If the attribute is not in the list, process next attribute.
    260                         if ( ! in_array( $binding_attribute, $allowed_blocks[ $this->name ], true ) ) {
     258                foreach ( $parsed_block['attrs']['metadata']['bindings'] as $attribute_name => $block_binding ) {
     259                        // If the attribute is not in the allowed list, process next attribute.
     260                        if ( ! in_array( $attribute_name, $allowed_blocks[ $this->name ], true ) ) {
    261261                                continue;
    262262                        }
    263263                        // If no source is provided, or that source is not registered, process next attribute.
    264                         if ( ! isset( $binding_source['source'] ) || ! is_string( $binding_source['source'] ) || ! isset( $block_bindings_sources[ $binding_source['source'] ] ) ) {
     264                        if ( ! isset( $block_binding['source'] ) || ! is_string( $block_binding['source'] ) ) {
    265265                                continue;
    266266                        }
    267267
    268                         $source_callback = $block_bindings_sources[ $binding_source['source'] ]['get_value_callback'];
    269                         // Get the value based on the source.
    270                         if ( ! isset( $binding_source['args'] ) ) {
    271                                 $source_args = array();
    272                         } else {
    273                                 $source_args = $binding_source['args'];
    274                         }
    275                         $source_value = call_user_func_array( $source_callback, array( $source_args, $this, $binding_attribute ) );
    276                         // If the value is null, process next attribute.
    277                         if ( is_null( $source_value ) ) {
     268                        $block_binding_source = get_block_bindings_source( $block_binding['source'] );
     269                        if ( null === $block_binding_source ) {
    278270                                continue;
    279271                        }
    280272
    281                         // Process the HTML based on the block and the attribute.
    282                         $modified_block_content = $this->replace_html( $modified_block_content, $this->name, $binding_attribute, $source_value );
    283                 }
     273                        $source_callback = $block_binding_source['get_value_callback'];
     274                        $source_args     = ! empty( $block_binding['args'] ) && is_array( $block_binding['args'] ) ? $block_binding['args'] : array();
     275                        $source_value    = call_user_func_array( $source_callback, array( $source_args, $this, $attribute_name ) );
     276
     277                        // If the value is not null, process the HTML based on the block and the attribute.
     278                        if ( ! is_null( $source_value ) ) {
     279                                $modified_block_content = $this->replace_html( $modified_block_content, $attribute_name, $source_value );
     280                        }
     281                }
     282
    284283                return $modified_block_content;
    285284        }
    286285
    287286        /**
    288          * Depending on the block attributes, replace the HTML based on the value returned by the source.
     287         * Depending on the block attribute name, replace its value in the HTML based on the value provided.
    289288         *
    290289         * @since 6.5.0
    291290         *
    292          * @param string $block_content Block content.
    293          * @param string $block_name The name of the block to process.
    294          * @param string $block_attr The attribute of the block we want to process.
    295          * @param string $source_value The value used to replace the HTML.
    296          */
    297         private function replace_html( string $block_content, string $block_name, string $block_attr, string $source_value ) {
     291         * @param string $block_content  Block content.
     292         * @param string $attribute_name The attribute name to replace.
     293         * @param mixed  $source_value   The value used to replace in the HTML.
     294         * @return string The modified block content.
     295         */
     296        private function replace_html( string $block_content, string $attribute_name, $source_value ) {
    298297                $block_type = $this->block_type;
    299                 if ( null === $block_type || ! isset( $block_type->attributes[ $block_attr ] ) ) {
     298                if ( ! isset( $block_type->attributes[ $attribute_name ] ) ) {
    300299                        return $block_content;
    301300                }
    302301
    303302                // Depending on the attribute source, the processing will be different.
    304                 switch ( $block_type->attributes[ $block_attr ]['source'] ) {
     303                switch ( $block_type->attributes[ $attribute_name ]['source'] ) {
    305304                        case 'html':
    306305                        case 'rich-text':
     
    309308                                // TODO: Support for CSS selectors whenever they are ready in the HTML API.
    310309                                // In the meantime, support comma-separated selectors by exploding them into an array.
    311                                 $selectors = explode( ',', $block_type->attributes[ $block_attr ]['selector'] );
     310                                $selectors = explode( ',', $block_type->attributes[ $attribute_name ]['selector'] );
    312311                                // Add a bookmark to the first tag to be able to iterate over the selectors.
    313312                                $block_reader->next_tag();
     
    317316                                // Store the parent tag and its attributes to be able to restore them later in the button.
    318317                                // The button block has a wrapper while the paragraph and heading blocks don't.
    319                                 if ( 'core/button' === $block_name ) {
     318                                if ( 'core/button' === $this->name ) {
    320319                                        $button_wrapper                 = $block_reader->get_tag();
    321320                                        $button_wrapper_attribute_names = $block_reader->get_attribute_names_with_prefix( '' );
     
    349348                                                        $amended_content->set_attribute( $attribute_key, $attribute_value );
    350349                                                }
    351                                                 if ( 'core/paragraph' === $block_name || 'core/heading' === $block_name ) {
     350                                                if ( 'core/paragraph' === $this->name || 'core/heading' === $this->name ) {
    352351                                                        return $amended_content->get_updated_html();
    353352                                                }
    354                                                 if ( 'core/button' === $block_name ) {
     353                                                if ( 'core/button' === $this->name ) {
    355354                                                        $button_markup  = "<$button_wrapper>{$amended_content->get_updated_html()}</$button_wrapper>";
    356355                                                        $amended_button = new WP_HTML_Tag_Processor( $button_markup );
     
    373372                                        array(
    374373                                                // TODO: build the query from CSS selector.
    375                                                 'tag_name' => $block_type->attributes[ $block_attr ]['selector'],
     374                                                'tag_name' => $block_type->attributes[ $attribute_name ]['selector'],
    376375                                        )
    377376                                ) ) {
    378377                                        return $block_content;
    379378                                }
    380                                 $amended_content->set_attribute( $block_type->attributes[ $block_attr ]['attribute'], esc_attr( $source_value ) );
     379                                $amended_content->set_attribute( $block_type->attributes[ $attribute_name ]['attribute'], esc_attr( $source_value ) );
    381380                                return $amended_content->get_updated_html();
    382381                                break;
  • trunk/src/wp-settings.php

    r57514 r57526  
    345345require ABSPATH . WPINC . '/class-wp-navigation-fallback.php';
    346346require ABSPATH . WPINC . '/block-bindings.php';
     347require ABSPATH . WPINC . '/block-bindings/pattern-overrides.php';
     348require ABSPATH . WPINC . '/block-bindings/post-meta.php';
    347349require ABSPATH . WPINC . '/blocks.php';
    348350require ABSPATH . WPINC . '/blocks/index.php';
     
    377379require ABSPATH . WPINC . '/class-wp-script-modules.php';
    378380require ABSPATH . WPINC . '/script-modules.php';
    379 require ABSPATH . WPINC . '/block-bindings/sources/post-meta.php';
    380 require ABSPATH . WPINC . '/block-bindings/sources/pattern.php';
    381381require ABSPATH . WPINC . '/interactivity-api.php';
    382382
  • trunk/tests/phpunit/tests/block-bindings/register.php

    r57514 r57526  
    1818
    1919        /**
    20          * Set up before each test.
    21          *
    22          * @since 6.5.0
    23          */
    24         public function set_up() {
    25                 foreach ( get_all_registered_block_bindings_sources() as $source_name => $source_properties ) {
    26                         unregister_block_bindings_source( $source_name );
    27                 }
    28 
    29                 parent::set_up();
    30         }
    31 
    32         /**
    3320         * Tear down after each test.
    3421         *
     
    3724        public function tear_down() {
    3825                foreach ( get_all_registered_block_bindings_sources() as $source_name => $source_properties ) {
    39                         unregister_block_bindings_source( $source_name );
     26                        if ( str_starts_with( $source_name, 'test/' ) ) {
     27                                unregister_block_bindings_source( $source_name );
     28                        }
    4029                }
    4130
     
    5039         * @covers ::register_block_bindings_source
    5140         * @covers ::get_all_registered_block_bindings_sources
     41         * @covers ::get_block_bindings_source
    5242         */
    5343        public function test_get_all_registered() {
     
    6555
    6656                $expected = array(
    67                         $source_one_name   => array_merge( array( 'name' => $source_one_name ), $source_one_properties ),
    68                         $source_two_name   => array_merge( array( 'name' => $source_two_name ), $source_two_properties ),
    69                         $source_three_name => array_merge( array( 'name' => $source_three_name ), $source_three_properties ),
     57                        $source_one_name         => array_merge( array( 'name' => $source_one_name ), $source_one_properties ),
     58                        $source_two_name         => array_merge( array( 'name' => $source_two_name ), $source_two_properties ),
     59                        $source_three_name       => array_merge( array( 'name' => $source_three_name ), $source_three_properties ),
     60                        'core/post-meta'         => get_block_bindings_source( 'core/post-meta' ),
     61                        'core/pattern-overrides' => get_block_bindings_source( 'core/pattern-overrides' ),
    7062                );
    7163
    7264                $registered = get_all_registered_block_bindings_sources();
    73                 $this->assertSame( $expected, $registered );
     65                $this->assertEquals( $expected, $registered );
    7466        }
    7567
  • trunk/tests/phpunit/tests/block-bindings/render.php

    r57525 r57526  
    11<?php
    22/**
    3  * Unit tests covering the Block Bindings public API.
     3 * Tests for Block Bindings integration with block rendering.
    44 *
    55 * @package WordPress
     
    99 * @group blocks
    1010 * @group block-bindings
    11  *
    12  * @covers register_block_bindings_source
    1311 */
    14 class WP_Block_Bindings_Test extends WP_UnitTestCase {
     12class WP_Block_Bindings_Render extends WP_UnitTestCase {
    1513
    1614        const SOURCE_NAME  = 'test/source';
     
    2018
    2119        /**
    22          * Set up before each test.
     20         * Tear down after each test.
    2321         *
    2422         * @since 6.5.0
    2523         */
    26         public function set_up() {
     24        public function tear_down() {
    2725                foreach ( get_all_registered_block_bindings_sources() as $source_name => $source_properties ) {
    2826                        if ( str_starts_with( $source_name, 'test/' ) ) {
     
    3129                }
    3230
    33                 parent::set_up();
     31                parent::tear_down();
    3432        }
    3533
     
    3937         * @ticket 60282
    4038         *
    41          * @covers register_block_bindings_source
     39         * @covers ::register_block_bindings_source
    4240         */
    4341        public function test_update_block_with_value_from_source() {
     
    6462                $result   = $block->render();
    6563
    66                 // Check if the block content was updated correctly.
    6764                $this->assertEquals( $expected, $result, 'The block content should be updated with the value returned by the source.' );
    6865        }
     
    7370         * @ticket 60282
    7471         *
    75          * @covers register_block_bindings_source
     72         * @covers ::register_block_bindings_source
    7673         */
    7774        public function test_passing_arguments_to_source() {
     
    8986                );
    9087
    91                 $key = 'test';
    92 
     88                $value         = 'test';
    9389                $block_content = <<<HTML
    94 <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source", "args": {"key": "$key"}}}}} --><p>This should not appear</p><!-- /wp:paragraph -->
     90<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"test/source", "args": {"key": "$value"}}}}} --><p>This should not appear</p><!-- /wp:paragraph -->
    9591HTML;
    9692
     
    9894                $block         = new WP_Block( $parsed_blocks[0] );
    9995
    100                 $expected = "<p>The attribute name is 'content' and its binding has argument 'key' with value 'test'.</p>";
     96                $expected = "<p>The attribute name is 'content' and its binding has argument 'key' with value '$value'.</p>";
    10197                $result   = $block->render();
    10298
    103                 // Check if the block content was updated correctly.
    10499                $this->assertEquals( $expected, $result, 'The block content should be updated with the value returned by the source.' );
    105100        }
Note: See TracChangeset for help on using the changeset viewer.