Make WordPress Core

Changeset 57354


Ignore:
Timestamp:
01/25/2024 01:46:49 PM (11 months 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.