Make WordPress Core

Changeset 57354


Ignore:
Timestamp:
01/25/2024 01:46:49 PM (3 years ago)
Author:
Bernhard Reiter
Message:

Block Hooks: Introduce a new hooked_block_{$block_type} filter.

Add a new hooked_block_{$block_type} filter that allows modifying a hooked block (in parsed block format) prior to insertion, while providing read access to its anchor block (in the same format).

This allows block authors to e.g. set a hooked block's attributes, or its inner blocks; the filter can peruse information about the anchor block when doing so. As such, this filter provides a solution to both #59572 and #60126.

The new filter is designed to strike a good balance and separation of concerns with regard to the existing `hooked_block_types` filter, which allows addition or removal of a block to the list of hooked blocks for a given anchor block -- all of which are identified only by their block types. This new filter, on the other hand, only applies to one hooked block at a time, and allows modifying the entire (parsed) hooked block; it also gives (read) access to the parsed anchor block.

Props gziolo, tomjcafferkey, andrewserong, isabel_brison, timbroddin, yansern.
Fixes #59572, #60126.

Location:
trunk
Files:
1 added
2 edited

Legend:

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

    r57157 r57354  
    759759
    760760/**
    761  * Conditionally returns the markup for a given hooked block type.
    762  *
    763  * Accepts two arguments: A reference to an anchor block, and the name of a hooked block type.
     761 * Conditionally returns the markup for a given hooked block.
     762 *
     763 * Accepts three arguments: A hooked block, its type, and a reference to an anchor block.
    764764 * If the anchor block has already been processed, and the given hooked block type is in the list
    765765 * of ignored hooked blocks, an empty string is returned.
    766766 *
     767 * The hooked block type is specified separately as it's possible that a filter might've modified
     768 * the hooked block such that `$hooked_block['blockName']` does no longer reflect the original type.
     769 *
    767770 * This function is meant for internal use only.
    768771 *
     
    770773 * @access private
    771774 *
    772  * @param array   $anchor_block      The anchor block. Passed by reference.
    773  * @param string  $hooked_block_type The name of the hooked block type.
    774  * @return string The markup for the given hooked block type, or an empty string if the block is ignored.
    775  */
    776 function get_hooked_block_markup( &$anchor_block, $hooked_block_type ) {
     775 * @param array  $hooked_block      The hooked block, represented as a parsed block array.
     776 * @param string $hooked_block_type The type of the hooked block. This could be different from
     777 *                                  $hooked_block['blockName'], as a filter might've modified the latter.
     778 * @param array  $anchor_block      The anchor block, represented as a parsed block array.
     779 *                                  Passed by reference.
     780 * @return string The markup for the given hooked block, or an empty string if the block is ignored.
     781 */
     782function get_hooked_block_markup( $hooked_block, $hooked_block_type, &$anchor_block ) {
    777783        if ( ! isset( $anchor_block['attrs']['metadata']['ignoredHookedBlocks'] ) ) {
    778784                $anchor_block['attrs']['metadata']['ignoredHookedBlocks'] = array();
     
    787793        $anchor_block['attrs']['metadata']['ignoredHookedBlocks'][] = $hooked_block_type;
    788794
    789         return get_comment_delimited_block_content( $hooked_block_type, array(), '' );
     795        return serialize_block( $hooked_block );
     796}
     797
     798/**
     799 * Returns the markup for blocks hooked to the given anchor block in a specific relative position.
     800 *
     801 * @since 6.5.0
     802 * @access private
     803 *
     804 * @param array                   $parsed_anchor_block The anchor block, in parsed block array format.
     805 * @param string                  $relative_position   The relative position of the hooked blocks.
     806 *                                                     Can be one of 'before', 'after', 'first_child', or 'last_child'.
     807 * @param array                   $hooked_blocks       An array of hooked block types, grouped by anchor block and relative position.
     808 * @param WP_Block_Template|array $context             The block template, template part, or pattern that the anchor block belongs to.
     809 * @return string
     810 */
     811function insert_hooked_blocks( &$parsed_anchor_block, $relative_position, $hooked_blocks, $context ) {
     812        $anchor_block_type  = $parsed_anchor_block['blockName'];
     813        $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
     814                ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
     815                : array();
     816
     817        /**
     818         * Filters the list of hooked block types for a given anchor block type and relative position.
     819         *
     820         * @since 6.4.0
     821         *
     822         * @param string[]                $hooked_block_types The list of hooked block types.
     823         * @param string                  $relative_position  The relative position of the hooked blocks.
     824         *                                                    Can be one of 'before', 'after', 'first_child', or 'last_child'.
     825         * @param string                  $anchor_block_type  The anchor block type.
     826         * @param WP_Block_Template|array $context            The block template, template part, or pattern that the anchor block belongs to.
     827         */
     828        $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
     829
     830        $markup = '';
     831        foreach ( $hooked_block_types as $hooked_block_type ) {
     832                $parsed_hooked_block = array(
     833                        'blockName'    => $hooked_block_type,
     834                        'attrs'        => array(),
     835                        'innerBlocks'  => array(),
     836                        'innerContent' => array(),
     837                );
     838
     839                /**
     840                 * Filters the parsed block array for a given hooked block.
     841                 *
     842                 * @since 6.5.0
     843                 *
     844                 * @param array                   $parsed_hooked_block The parsed block array for the given hooked block type.
     845                 * @param string                  $relative_position   The relative position of the hooked block.
     846                 * @param array                   $parsed_anchor_block The anchor block, in parsed block array format.
     847                 * @param WP_Block_Template|array $context             The block template, template part, or pattern that the anchor block belongs to.
     848                 */
     849                $parsed_hooked_block = apply_filters( "hooked_block_{$hooked_block_type}", $parsed_hooked_block, $relative_position, $parsed_anchor_block, $context );
     850
     851                // It's possible that the `hooked_block_{$hooked_block_type}` filter returned a block of a different type,
     852                // so we need to pass the original $hooked_block_type as well.
     853                $markup .= get_hooked_block_markup( $parsed_hooked_block, $hooked_block_type, $parsed_anchor_block );
     854        }
     855
     856        return $markup;
    790857}
    791858
     
    827894                if ( $parent_block && ! $prev ) {
    828895                        // Candidate for first-child insertion.
    829                         $relative_position  = 'first_child';
    830                         $anchor_block_type  = $parent_block['blockName'];
    831                         $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
    832                                 ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
    833                                 : array();
    834 
    835                         /**
    836                          * Filters the list of hooked block types for a given anchor block type and relative position.
    837                          *
    838                          * @since 6.4.0
    839                          *
    840                          * @param string[]                $hooked_block_types  The list of hooked block types.
    841                          * @param string                  $relative_position   The relative position of the hooked blocks.
    842                          *                                                     Can be one of 'before', 'after', 'first_child', or 'last_child'.
    843                          * @param string                  $anchor_block_type   The anchor block type.
    844                          * @param WP_Block_Template|array $context             The block template, template part, or pattern that the anchor block belongs to.
    845                          */
    846                         $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
    847                         foreach ( $hooked_block_types as $hooked_block_type ) {
    848                                 $markup .= get_hooked_block_markup( $parent_block, $hooked_block_type );
    849                         }
    850                 }
    851 
    852                 $relative_position  = 'before';
    853                 $anchor_block_type  = $block['blockName'];
    854                 $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
    855                         ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
    856                         : array();
    857 
    858                 /** This filter is documented in wp-includes/blocks.php */
    859                 $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
    860                 foreach ( $hooked_block_types as $hooked_block_type ) {
    861                         $markup .= get_hooked_block_markup( $block, $hooked_block_type );
    862                 }
     896                        $markup .= insert_hooked_blocks( $parent_block, 'first_child', $hooked_blocks, $context );
     897                }
     898
     899                $markup .= insert_hooked_blocks( $block, 'before', $hooked_blocks, $context );
    863900
    864901                return $markup;
     
    896933         */
    897934        return function ( &$block, &$parent_block = null, $next = null ) use ( $hooked_blocks, $context ) {
    898                 $markup = '';
    899 
    900                 $relative_position  = 'after';
    901                 $anchor_block_type  = $block['blockName'];
    902                 $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
    903                                 ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
    904                                 : array();
    905 
    906                 /** This filter is documented in wp-includes/blocks.php */
    907                 $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
    908                 foreach ( $hooked_block_types as $hooked_block_type ) {
    909                         $markup .= get_hooked_block_markup( $block, $hooked_block_type );
    910                 }
     935                $markup = insert_hooked_blocks( $block, 'after', $hooked_blocks, $context );
    911936
    912937                if ( $parent_block && ! $next ) {
    913938                        // Candidate for last-child insertion.
    914                         $relative_position  = 'last_child';
    915                         $anchor_block_type  = $parent_block['blockName'];
    916                         $hooked_block_types = isset( $hooked_blocks[ $anchor_block_type ][ $relative_position ] )
    917                                 ? $hooked_blocks[ $anchor_block_type ][ $relative_position ]
    918                                 : array();
    919 
    920                         /** This filter is documented in wp-includes/blocks.php */
    921                         $hooked_block_types = apply_filters( 'hooked_block_types', $hooked_block_types, $relative_position, $anchor_block_type, $context );
    922                         foreach ( $hooked_block_types as $hooked_block_type ) {
    923                                 $markup .= get_hooked_block_markup( $parent_block, $hooked_block_type );
    924                         }
     939                        $markup .= insert_hooked_blocks( $parent_block, 'last_child', $hooked_blocks, $context );
    925940                }
    926941
  • trunk/tests/phpunit/tests/blocks/getHookedBlockMarkup.php

    r57172 r57354  
    1212 */
    1313class Tests_Blocks_GetHookedBlockMarkup extends WP_UnitTestCase {
     14        const HOOKED_BLOCK_TYPE = 'tests/hooked-block';
     15        const HOOKED_BLOCK      = array(
     16                'blockName'    => 'tests/different-hooked-block',
     17                'attrs'        => array(),
     18                'innerContent' => array(),
     19        );
     20
    1421        /**
     22         * @ticket 59572
    1523         * @ticket 60008
     24         * @ticket 60126
    1625         *
    1726         * @covers ::get_hooked_block_markup
     
    2231                );
    2332
    24                 $actual = get_hooked_block_markup( $anchor_block, 'tests/hooked-block' );
    25                 $this->assertSame( array( 'tests/hooked-block' ), $anchor_block['attrs']['metadata']['ignoredHookedBlocks'] );
    26                 $this->assertSame( '<!-- wp:tests/hooked-block /-->', $actual );
     33                $actual = get_hooked_block_markup( self::HOOKED_BLOCK, self::HOOKED_BLOCK_TYPE, $anchor_block );
     34                $this->assertSame(
     35                        array( self::HOOKED_BLOCK_TYPE ),
     36                        $anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
     37                        "Hooked block type wasn't added to ignoredHookedBlocks metadata."
     38                );
     39                $this->assertSame(
     40                        '<!-- wp:' . self::HOOKED_BLOCK['blockName'] . ' /-->',
     41                        $actual,
     42                        "Markup for hooked block wasn't generated correctly."
     43                );
    2744        }
    2845
    2946        /**
     47         * @ticket 59572
    3048         * @ticket 60008
     49         * @ticket 60126
    3150         *
    3251         * @covers ::get_hooked_block_markup
     
    3756                        'attrs'     => array(
    3857                                'metadata' => array(
    39                                         'ignoredHookedBlocks' => array( 'tests/hooked-block' ),
     58                                        'ignoredHookedBlocks' => array( self::HOOKED_BLOCK_TYPE ),
    4059                                ),
    4160                        ),
    4261                );
    4362
    44                 $actual = get_hooked_block_markup( $anchor_block, 'tests/hooked-block' );
    45                 $this->assertSame( array( 'tests/hooked-block' ), $anchor_block['attrs']['metadata']['ignoredHookedBlocks'] );
    46                 $this->assertSame( '', $actual );
     63                $actual = get_hooked_block_markup( self::HOOKED_BLOCK, self::HOOKED_BLOCK_TYPE, $anchor_block );
     64                $this->assertSame(
     65                        array( self::HOOKED_BLOCK_TYPE ),
     66                        $anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
     67                        "ignoredHookedBlocks metadata shouldn't have been modified."
     68                );
     69                $this->assertSame(
     70                        '',
     71                        $actual,
     72                        "No markup should've been generated for ignored hooked block."
     73                );
    4774        }
    4875
    4976        /**
     77         * @ticket 59572
    5078         * @ticket 60008
     79         * @ticket 60126
    5180         *
    5281         * @covers ::get_hooked_block_markup
    5382         */
    5483        public function test_get_hooked_block_markup_adds_to_ignored_hooked_blocks() {
     84                $other_hooked_block_type = 'tests/other-hooked-block';
     85                $other_hooked_block      = array(
     86                        'blockName'    => $other_hooked_block_type,
     87                        'attrs'        => array(),
     88                        'innerContent' => array(),
     89                );
     90
    5591                $anchor_block = array(
    5692                        'blockName' => 'tests/anchor-block',
    5793                        'attrs'     => array(
    5894                                'metadata' => array(
    59                                         'ignoredHookedBlocks' => array( 'tests/hooked-block' ),
     95                                        'ignoredHookedBlocks' => array( self::HOOKED_BLOCK_TYPE ),
    6096                                ),
    6197                        ),
    6298                );
    6399
    64                 $actual = get_hooked_block_markup( $anchor_block, 'tests/other-hooked-block' );
    65                 $this->assertSame( array( 'tests/hooked-block', 'tests/other-hooked-block' ), $anchor_block['attrs']['metadata']['ignoredHookedBlocks'] );
    66                 $this->assertSame( '<!-- wp:tests/other-hooked-block /-->', $actual );
     100                $actual = get_hooked_block_markup( $other_hooked_block, $other_hooked_block_type, $anchor_block );
     101                $this->assertSame(
     102                        array( self::HOOKED_BLOCK_TYPE, $other_hooked_block_type ),
     103                        $anchor_block['attrs']['metadata']['ignoredHookedBlocks'],
     104                        "Newly hooked block should've been added to ignoredHookedBlocks metadata while retaining previously ignored one."
     105                );
     106                $this->assertSame(
     107                        '<!-- wp:' . $other_hooked_block_type . ' /-->',
     108                        $actual,
     109                        "Markup for newly hooked block should've been generated."
     110                );
    67111        }
    68112}
Note: See TracChangeset for help on using the changeset viewer.