Make WordPress Core

Changeset 56644


Ignore:
Timestamp:
09/21/2023 08:32:52 AM (12 months ago)
Author:
Bernhard Reiter
Message:

Blocks: Change traverse_and_serialize_block(s)'s callback signature.

During work on #59399, it was discovered that sibling block insertion wasn't likely going to work the way it was planned, which required devising an alternative solution. This new solution requires some changes to traverse_and_serialize_block(s):

  • Change the signature of the existing callback such that:
    • the return value is a string that will be prepended to the result of the inner block traversal and serialization;
    • the function arguments are: a reference to the current block (so it can be modified inline, which is important e.g. for theme attribute insertion), the parent block, and the previous block (instead of the block index and chunk index).
  • Add a second callback argument to traverse_and_serialize_block(s), which is called after the block is traversed and serialized.
    • Its function arguments are a reference to the current block, the parent block, and the next block.

Props gziolo.
Fixes #59412. See #59313.

Location:
trunk
Files:
4 edited

Legend:

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

    r56621 r56644  
    516516 *
    517517 * @param array $block a parsed block.
    518  * @return array Updated block.
    519  */
    520 function _inject_theme_attribute_in_template_part_block( $block ) {
     518 * @return void
     519 */
     520function _inject_theme_attribute_in_template_part_block( &$block ) {
    521521    if (
    522522        'core/template-part' === $block['blockName'] &&
     
    525525        $block['attrs']['theme'] = get_stylesheet();
    526526    }
    527     return $block;
    528527}
    529528
  • trunk/src/wp-includes/blocks.php

    r56634 r56644  
    887887
    888888/**
    889  * Traverses the block applying transformations using the callback provided and returns the content of a block,
    890  * including comment delimiters, serializing all attributes from the given parsed block.
    891  *
    892  * This should be used when there is a need to modify the saved block.
    893  * Prefer `serialize_block` when preparing a block to be saved to post content.
     889 * Traverses a parsed block tree and applies callbacks before and after serializing it.
     890 *
     891 * Recursively traverses the block and its inner blocks and applies the two callbacks provided as
     892 * arguments, the first one before serializing the block, and the second one after serializing it.
     893 * If either callback returns a string value, it will be prepended and appended to the serialized
     894 * block markup, respectively.
     895 *
     896 * The callbacks will receive a reference to the current block as their first argument, so that they
     897 * can also modify it, and the current block's parent block as second argument. Finally, the
     898 * `$pre_callback` receives the previous block, whereas the `$post_callback` receives
     899 * the next block as third argument.
     900 *
     901 * Serialized blocks are returned including comment delimiters, and with all attributes serialized.
     902 *
     903 * This function should be used when there is a need to modify the saved block, or to inject markup
     904 * into the return value. Prefer `serialize_block` when preparing a block to be saved to post content.
    894905 *
    895906 * @since 6.4.0
     
    897908 * @see serialize_block()
    898909 *
    899  * @param array    $block    A representative array of a single parsed block object. See WP_Block_Parser_Block.
    900  * @param callable $callback Callback to run on each block in the tree before serialization.
    901  *                           It is called with the following arguments: $block, $parent_block, $block_index, $chunk_index.
    902  * @return string String of rendered HTML.
    903  */
    904 function traverse_and_serialize_block( $block, $callback ) {
     910 * @param array    $block         A representative array of a single parsed block object. See WP_Block_Parser_Block.
     911 * @param callable $pre_callback  Callback to run on each block in the tree before it is traversed and serialized.
     912 *                                It is called with the following arguments: &$block, $parent_block, $previous_block.
     913 *                                Its string return value will be prepended to the serialized block markup.
     914 * @param callable $post_callback Callback to run on each block in the tree after it is traversed and serialized.
     915 *                                It is called with the following arguments: &$block, $parent_block, $next_block.
     916 *                                Its string return value will be appended to the serialized block markup.
     917 * @return string Serialized block markup.
     918 */
     919function traverse_and_serialize_block( $block, $pre_callback = null, $post_callback = null ) {
    905920    $block_content = '';
    906921    $block_index   = 0;
    907922
    908     foreach ( $block['innerContent'] as $chunk_index => $chunk ) {
     923    foreach ( $block['innerContent'] as $chunk ) {
    909924        if ( is_string( $chunk ) ) {
    910925            $block_content .= $chunk;
    911926        } else {
    912             $inner_block = call_user_func(
    913                 $callback,
    914                 $block['innerBlocks'][ $block_index ],
    915                 $block,
    916                 $block_index,
    917                 $chunk_index
    918             );
     927            $inner_block = $block['innerBlocks'][ $block_index ];
     928
     929            if ( is_callable( $pre_callback ) ) {
     930                $prev = 0 === $block_index
     931                    ? null
     932                    : $block['innerBlocks'][ $block_index - 1 ];
     933                $block_content .= call_user_func_array(
     934                    $pre_callback,
     935                    array( &$inner_block, $block, $prev )
     936                );
     937            }
     938
     939            $block_content .= traverse_and_serialize_block( $inner_block, $pre_callback, $post_callback );
     940
     941            if ( is_callable( $post_callback ) ) {
     942                $next = count( $block['innerBlocks'] ) - 1 === $block_index
     943                    ? null
     944                    : $block['innerBlocks'][ $block_index + 1 ];
     945                $block_content .= call_user_func_array(
     946                    $post_callback,
     947                    array( &$inner_block, $block, $next )
     948                );
     949            }
    919950            $block_index++;
    920             $block_content .= traverse_and_serialize_block( $inner_block, $callback );
    921951        }
    922952    }
     
    934964
    935965/**
    936  * Traverses the blocks applying transformations using the callback provided,
    937  * and returns a joined string of the aggregate serialization of the given parsed blocks.
    938  *
    939  * This should be used when there is a need to modify the saved blocks.
    940  * Prefer `serialize_blocks` when preparing blocks to be saved to post content.
     966 * Given an array of parsed block trees, applies callbacks before and after serializing them and
     967 * returns their concatenated output.
     968 *
     969 * Recursively traverses the blocks and their inner blocks and applies the two callbacks provided as
     970 * arguments, the first one before serializing a block, and the second one after serializing.
     971 * If either callback returns a string value, it will be prepended and appended to the serialized
     972 * block markup, respectively.
     973 *
     974 * The callbacks will receive a reference to the current block as their first argument, so that they
     975 * can also modify it, and the current block's parent block as second argument. Finally, the
     976 * `$pre_callback` receives the previous block, whereas the `$post_callback` receives
     977 * the next block as third argument.
     978 *
     979 * Serialized blocks are returned including comment delimiters, and with all attributes serialized.
     980 *
     981 * This function should be used when there is a need to modify the saved blocks, or to inject markup
     982 * into the return value. Prefer `serialize_blocks` when preparing blocks to be saved to post content.
    941983 *
    942984 * @since 6.4.0
     
    944986 * @see serialize_blocks()
    945987 *
    946  * @param array[]  $blocks   An array of representative arrays of parsed block objects. See serialize_block().
    947  * @param callable $callback Callback to run on each block in the tree before serialization.
    948  *                           It is called with the following arguments: $block, $parent_block, $block_index, $chunk_index.
    949  * @return string String of rendered HTML.
    950  */
    951 function traverse_and_serialize_blocks( $blocks, $callback ) {
     988 * @param array[]  $blocks        An array of parsed blocks. See WP_Block_Parser_Block.
     989 * @param callable $pre_callback  Callback to run on each block in the tree before it is traversed and serialized.
     990 *                                It is called with the following arguments: &$block, $parent_block, $previous_block.
     991 *                                Its string return value will be prepended to the serialized block markup.
     992 * @param callable $post_callback Callback to run on each block in the tree after it is traversed and serialized.
     993 *                                It is called with the following arguments: &$block, $parent_block, $next_block.
     994 *                                Its string return value will be appended to the serialized block markup.
     995 * @return string Serialized block markup.
     996 */
     997function traverse_and_serialize_blocks( $blocks, $pre_callback = null, $post_callback = null ) {
    952998    $result = '';
    953     foreach ( $blocks as $block ) {
    954         // At the top level, there is no parent block, block index, or chunk index to pass to the callback.
    955         $block = call_user_func( $callback, $block );
    956         $result .= traverse_and_serialize_block( $block, $callback );
     999    foreach ( $blocks as $index => $block ) {
     1000        if ( is_callable( $pre_callback ) ) {
     1001            $prev = 0 === $index
     1002                ? null
     1003                : $blocks[ $index - 1 ];
     1004            $result .= call_user_func_array(
     1005                $pre_callback,
     1006                array( &$block, null, $prev ) // At the top level, there is no parent block to pass to the callback.
     1007            );
     1008        }
     1009        $result .= traverse_and_serialize_block( $block, $pre_callback, $post_callback );
     1010        if ( is_callable( $post_callback ) ) {
     1011            $next = count( $blocks ) - 1 === $index
     1012                ? null
     1013                : $blocks[ $index + 1 ];
     1014            $result .= call_user_func_array(
     1015                $post_callback,
     1016                array( &$block, null, $next ) // At the top level, there is no parent block to pass to the callback.
     1017            );
     1018        }
    9571019    }
    9581020    return $result;
  • trunk/tests/phpunit/tests/block-template-utils.php

    r56584 r56644  
    226226     */
    227227    public function test_inject_theme_attribute_in_template_part_block() {
    228         $template_part_block_without_theme_attribute = array(
     228        $template_part_block = array(
    229229            'blockName'    => 'core/template-part',
    230230            'attrs'        => array(
     
    239239        );
    240240
    241         $actual   = _inject_theme_attribute_in_template_part_block( $template_part_block_without_theme_attribute );
     241        _inject_theme_attribute_in_template_part_block( $template_part_block );
    242242        $expected = array(
    243243            'blockName'    => 'core/template-part',
     
    255255        $this->assertSame(
    256256            $expected,
    257             $actual,
     257            $template_part_block,
    258258            '`theme` attribute was not correctly injected in template part block.'
    259259        );
     
    266266     */
    267267    public function test_not_inject_theme_attribute_in_template_part_block_theme_attribute_exists() {
    268         $template_part_block_with_existing_theme_attribute = array(
     268        $template_part_block = array(
    269269            'blockName'    => 'core/template-part',
    270270            'attrs'        => array(
     
    280280        );
    281281
    282         $actual = _inject_theme_attribute_in_template_part_block( $template_part_block_with_existing_theme_attribute );
     282        $expected = $template_part_block;
     283        _inject_theme_attribute_in_template_part_block( $template_part_block );
    283284        $this->assertSame(
    284             $template_part_block_with_existing_theme_attribute,
    285             $actual,
     285            $expected,
     286            $template_part_block,
    286287            'Existing `theme` attribute in template part block was not respected by attribute injection.'
    287288        );
     
    302303        );
    303304
    304         $actual = _inject_theme_attribute_in_template_part_block( $non_template_part_block );
     305        $expected = $non_template_part_block;
     306        _inject_theme_attribute_in_template_part_block( $non_template_part_block );
    305307        $this->assertSame(
     308            $expected,
    306309            $non_template_part_block,
    307             $actual,
    308310            '`theme` attribute injection modified non-template-part block.'
    309311        );
  • trunk/tests/phpunit/tests/blocks/serialize.php

    r56620 r56644  
    5959    /**
    6060     * @ticket 59327
     61     * @ticket 59412
    6162     *
    6263     * @covers ::traverse_and_serialize_blocks
     
    7475    }
    7576
    76     public static function add_attribute_to_inner_block( $block ) {
     77    public static function add_attribute_to_inner_block( &$block ) {
    7778        if ( 'core/inner' === $block['blockName'] ) {
    7879            $block['attrs']['myattr'] = 'myvalue';
    7980        }
    80         return $block;
    8181    }
    8282
    8383    /**
    8484     * @ticket 59327
     85     * @ticket 59412
    8586     *
    8687     * @covers ::traverse_and_serialize_blocks
     
    9394        $blocks = parse_blocks( $original );
    9495
    95         $actual = traverse_and_serialize_blocks(
    96             $blocks,
    97             function ( $block ) {
    98                 return $block;
    99             }
    100         );
     96        $actual = traverse_and_serialize_blocks( $blocks );
    10197
    10298        $this->assertSame( $original, $actual );
Note: See TracChangeset for help on using the changeset viewer.