Make WordPress Core

Changeset 58234


Ignore:
Timestamp:
05/29/2024 11:55:27 AM (9 months ago)
Author:
gziolo
Message:

Interactivity API: Move directive processing to WP_Block class

Integrates the directives processing into the WP_Block class. It removes the overhead of running additional hooks when rendering blocks and simplifies the way we detect whether the directive processing should run on an interactive region of the produced final HTML for the blocks.

Introduces interactivity_process_directives filter to offer a way to opt out from directives processing. It's needed in Gutenberg: https://github.com/WordPress/gutenberg/pull/62095.

Props gziolo, cbravobernal.
Fixes #61185.

Location:
trunk
Files:
4 edited

Legend:

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

    r58084 r58234  
    409409    public function render( $options = array() ) {
    410410        global $post;
     411
     412        /*
     413         * There can be only one root interactive block at a time because the rendered HTML of that block contains
     414         * the rendered HTML of all its inner blocks, including any interactive block.
     415         */
     416        static $root_interactive_block  = null;
     417        /**
     418         * Filters whether Interactivity API should process directives.
     419         *
     420         * @since 6.6.0
     421         *
     422         * @param bool $enabled Whether the directives processing is enabled.
     423         */
     424        $interactivity_process_directives_enabled = apply_filters( 'interactivity_process_directives', true );
     425        if (
     426            $interactivity_process_directives_enabled && null === $root_interactive_block && (
     427                ( isset( $this->block_type->supports['interactivity'] ) && true === $this->block_type->supports['interactivity'] ) ||
     428                ! empty( $this->block_type->supports['interactivity']['interactive'] )
     429            )
     430        ) {
     431            $root_interactive_block = $this;
     432        }
     433
    411434        $options = wp_parse_args(
    412435            $options,
     
    534557        $block_content = apply_filters( "render_block_{$this->name}", $block_content, $this->parsed_block, $this );
    535558
     559        if ( $root_interactive_block === $this ) {
     560            // The root interactive block has finished rendering. Time to process directives.
     561            $block_content          = wp_interactivity_process_directives( $block_content );
     562            $root_interactive_block = null;
     563        }
     564
    536565        return $block_content;
    537566    }
  • trunk/src/wp-includes/deprecated.php

    r58090 r58234  
    63196319    return $block_content;
    63206320}
     6321
     6322/**
     6323 * Processes the directives on the rendered HTML of the interactive blocks.
     6324 *
     6325 * This processes only one root interactive block at a time because the
     6326 * rendered HTML of that block contains the rendered HTML of all its inner
     6327 * blocks, including any interactive block. It does so by ignoring all the
     6328 * interactive inner blocks until the root interactive block is processed.
     6329 *
     6330 * @since 6.5.0
     6331 * @deprecated 6.6.0
     6332 *
     6333 * @param array $parsed_block The parsed block.
     6334 * @return array The same parsed block.
     6335 */
     6336function wp_interactivity_process_directives_of_interactive_blocks( array $parsed_block ): array {
     6337    _deprecated_function( __FUNCTION__, '6.6.0' );
     6338    return $parsed_block;
     6339}
  • trunk/src/wp-includes/interactivity-api/interactivity-api.php

    r57826 r58234  
    77 * @since 6.5.0
    88 */
    9 
    10 /**
    11  * Processes the directives on the rendered HTML of the interactive blocks.
    12  *
    13  * This processes only one root interactive block at a time because the
    14  * rendered HTML of that block contains the rendered HTML of all its inner
    15  * blocks, including any interactive block. It does so by ignoring all the
    16  * interactive inner blocks until the root interactive block is processed.
    17  *
    18  * @since 6.5.0
    19  *
    20  * @param array $parsed_block The parsed block.
    21  * @return array The same parsed block.
    22  */
    23 function wp_interactivity_process_directives_of_interactive_blocks( array $parsed_block ): array {
    24     static $root_interactive_block = null;
    25 
    26     /*
    27      * Checks whether a root interactive block is already annotated for
    28      * processing, and if it is, it ignores the subsequent ones.
    29      */
    30     if ( null === $root_interactive_block ) {
    31         $block_name = $parsed_block['blockName'];
    32         $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
    33 
    34         if (
    35             isset( $block_name ) &&
    36             ( ( isset( $block_type->supports['interactivity'] ) && true === $block_type->supports['interactivity'] ) ||
    37             ( isset( $block_type->supports['interactivity']['interactive'] ) && true === $block_type->supports['interactivity']['interactive'] ) )
    38         ) {
    39             // Annotates the root interactive block for processing.
    40             $root_interactive_block = array( $block_name, $parsed_block );
    41 
    42             /*
    43              * Adds a filter to process the root interactive block once it has
    44              * finished rendering.
    45              */
    46             $process_interactive_blocks = static function ( string $content, array $parsed_block ) use ( &$root_interactive_block, &$process_interactive_blocks ): string {
    47                 // Checks whether the current block is the root interactive block.
    48                 list($root_block_name, $root_parsed_block) = $root_interactive_block;
    49                 if ( $root_block_name === $parsed_block['blockName'] && $parsed_block === $root_parsed_block ) {
    50                     // The root interactive blocks has finished rendering, process it.
    51                     $content = wp_interactivity_process_directives( $content );
    52                     // Removes the filter and reset the root interactive block.
    53                     remove_filter( 'render_block_' . $parsed_block['blockName'], $process_interactive_blocks );
    54                     $root_interactive_block = null;
    55                 }
    56                 return $content;
    57             };
    58 
    59             /*
    60              * Uses a priority of 100 to ensure that other filters can add additional
    61              * directives before the processing starts.
    62              */
    63             add_filter( 'render_block_' . $block_name, $process_interactive_blocks, 100, 2 );
    64         }
    65     }
    66 
    67     return $parsed_block;
    68 }
    69 /*
    70  * Uses a priority of 100 to ensure that other filters can add additional attributes to
    71  * $parsed_block before the processing starts.
    72  */
    73 add_filter( 'render_block_data', 'wp_interactivity_process_directives_of_interactive_blocks', 100, 1 );
    749
    7510/**
  • trunk/tests/phpunit/tests/interactivity-api/wpInteractivityAPIFunctions.php

    r57987 r58234  
    526526        $this->assertNull( $input_value );
    527527    }
     528
     529    /**
     530     * Tests interactivity_process_directives filter.
     531     *
     532     * @ticket 61185
     533     *
     534     * @covers wp_interactivity_process_directives
     535     */
     536    public function test_not_processing_directives_filter() {
     537        wp_interactivity_state(
     538            'dont-process',
     539            array(
     540                'text' => 'text',
     541            )
     542        );
     543        register_block_type(
     544            'test/custom-directive-block',
     545            array(
     546                'render_callback' => function () {
     547                    return '<div data-wp-interactive="dont-process"><input data-wp-bind--value="state.text" /></div>';
     548                },
     549                'supports'        => array(
     550                    'interactivity' => true,
     551                ),
     552            )
     553        );
     554        $post_content = '<!-- wp:test/custom-directive-block /-->';
     555        add_filter( 'interactivity_process_directives', '__return_false' );
     556        $processed_content = do_blocks( $post_content );
     557        $processor         = new WP_HTML_Tag_Processor( $processed_content );
     558        $processor->next_tag( array( 'tag_name' => 'input' ) );
     559        $input_value = $processor->get_attribute( 'value' );
     560        remove_filter( 'interactivity_process_directives', '__return_false' );
     561        unregister_block_type( 'test/custom-directive-block' );
     562        $this->assertNull( $input_value );
     563    }
    528564}
Note: See TracChangeset for help on using the changeset viewer.