Make WordPress Core

Changeset 57526


Ignore:
Timestamp:
02/02/2024 08:22:11 PM (8 months 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.