WordPress.org

Make WordPress Core

Ticket #47375: 47375-3.patch

File 47375-3.patch, 8.7 KB (added by dmsnell, 6 months ago)

Fix of 47375-2.patch - I messed up when creating and uploading that one

  • src/wp-includes/blocks.php

    diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php
    index 6032159fb9..e35cf20567 100644
    a b function render_block( $block ) { 
    275275        return apply_filters( 'render_block', $block_content, $block );
    276276}
    277277
     278/**
     279 * Renders an HTML-serialized form of a block object
     280 *
     281 * @since 5.3.0
     282 *
     283 * @param  array  $block The block being rendered
     284 * @return string        The HTML-serialized form of the block
     285 */
     286function serialize_block( $block ) {
     287        // non-block content has no block name
     288        if ( null === $block['blockName'] ) {
     289                return $block['innerHTML'];
     290        }
     291
     292        $unwanted  = array( '--', '<', '>', '&', '\"' );
     293        $wanted    = array( '\u002d\u002d', '\u003c', '\u003e', '\u0026', '\u0022' );
     294
     295        $name      = 0 === strpos( $block['blockName'], 'core/' ) ? substr( $block['blockName'], 5 ) : $block['blockName'];
     296        $has_attrs = ! empty( $block['attrs'] );
     297        $attrs     = $has_attrs ? str_replace( $unwanted, $wanted, wp_json_encode( $block['attrs'] ) ) : '';
     298
     299        // early abort for void blocks holding no content
     300        if ( empty( $block['innerContent'] ) ) {
     301                return $has_attrs
     302                        ? "<!-- wp:{$name} {$attrs} /-->"
     303                        : "<!-- wp:{$name} /-->";
     304        }
     305
     306        $output = $has_attrs
     307                ? "<!-- wp:{$name} {$attrs} -->\n"
     308                : "<!-- wp:{$name} -->\n";
     309
     310        $innerBlockIndex = 0;
     311        foreach ( $block['innerContent'] as $chunk ) {
     312                $output .= null === $chunk
     313                        ? serialize_block( $block['innerBlocks'][$innerBlockIndex++] )
     314                        : $chunk;
     315
     316                $output .= "\n";
     317        }
     318
     319        $output .= "<!-- /wp:{$name} -->";
     320
     321        return $output;
     322}
     323
     324/**
     325 * Renders an HTML-serialized form of a list of block objects
     326 *
     327 * @since 5.3.0
     328 *
     329 * @param  array  $blocks The list of parsed block objects
     330 * @return string         The HTML-serialized form of the list of blocks
     331 */
     332function serialize_blocks( $blocks ) {
     333        return implode( "\n\n", array_map( 'serialize_block', $blocks ) );
     334}
     335
    278336/**
    279337 * Parses blocks out of a content string.
    280338 *
  • new file tests/phpunit/tests/blocks/serialize.php

    diff --git a/tests/phpunit/tests/blocks/serialize.php b/tests/phpunit/tests/blocks/serialize.php
    new file mode 100644
    index 0000000000..e52310a4c9
    - +  
     1<?php
     2/**
     3 * Block Serialization Tests
     4 *
     5 * @package WordPress
     6 * @subpackage Blocks
     7 * @since 5.3.0
     8 */
     9
     10/**
     11 * Tests for block serialization
     12 *
     13 * @since 5.3.0
     14 *
     15 * @group blocks
     16 */
     17class WP_Test_Block_Serialize extends WP_UnitTestCase {
     18        function test_serializes_freeform_block() {
     19                $this->assertEquals(
     20                        '<strong>Success!</strong>',
     21                        serialize_block( array(
     22                                'blockName'    => null,
     23                                'attrs'        => array(),
     24                                'innerBlocks'  => array(),
     25                                'innerHTML'    => '<strong>Success!</strong>',
     26                                'innerContent' => array( '<strong>Success!</strong>' )
     27                        ) )
     28                );
     29        }
     30
     31        function test_seriliazes_void_block_without_attributes() {
     32                $this->assertEquals(
     33                        '<!-- wp:test/void /-->',
     34                        serialize_block( self::make_block( array() ) )
     35                );
     36        }
     37
     38        function test_serializes_void_block_with_attributes() {
     39                $this->assertEquals(
     40                        '<!-- wp:test/void {"align":"left"} /-->',
     41                        serialize_block( self::make_block( array( 'attrs' => array( 'align' => 'left' ) ) ) )
     42                );
     43        }
     44
     45        function test_serializes_block_without_attributes() {
     46                $this->assertEquals(
     47                        "<!-- wp:test/test -->\nOnce I ate a cheeseburger.\n<!-- /wp:test/test -->",
     48                        serialize_block( self::make_block( array(
     49                                'blockName'    => 'test/test',
     50                                'innerHTML'    => 'Once I ate a cheeseburger.',
     51                                'innerContent' => array( 'Once I ate a cheeseburger.' ),
     52                        ) ) )
     53                );
     54        }
     55
     56        function test_serializes_block_with_attributes() {
     57                $this->assertEquals(
     58                        "<!-- wp:test/test {\"count\":3} -->\nOnce I ate a cheeseburger.\n<!-- /wp:test/test -->",
     59                        serialize_block( self::make_block( array(
     60                                'blockName'    => 'test/test',
     61                                'attrs'        => array( 'count' => 3 ),
     62                                'innerHTML'    => 'Once I ate a cheeseburger.',
     63                                'innerContent' => array( 'Once I ate a cheeseburger.' ),
     64                        ) ) )
     65                );
     66        }
     67
     68        function test_serializes_one_void_inner_block() {
     69                $inner_block = self::make_block( array( 'blockName' => 'test/inner' ) );
     70
     71                $this->assertEquals(
     72                        "<!-- wp:test/outer -->\n<!-- wp:test/inner /-->\n<!-- /wp:test/outer -->",
     73                        serialize_block( self::make_block( array(
     74                                'blockName'    => 'test/outer',
     75                                'innerBlocks'  => array( $inner_block ),
     76                                'innerContent' => array( null )
     77                        ) ) )
     78                );
     79        }
     80
     81        function test_seriailizes_many_inner_blocks() {
     82                $inner_block = self::make_block( array( 'blockName' => 'test/inner' ) );
     83
     84                $this->assertEquals(
     85                        "<!-- wp:test/outer -->\n<!-- wp:test/inner /-->\n<!-- wp:test/inner /-->\n<!-- /wp:test/outer -->",
     86                        serialize_block( self::make_block( array(
     87                                'blockName'    => 'test/outer',
     88                                'innerBlocks'  => array( $inner_block, $inner_block ),
     89                                'innerContent' => array( null, null )
     90                        ) ) )
     91                );
     92        }
     93
     94        function test_serializes_mixed_content() {
     95                $inner_block = self::make_block( array(
     96                        'blockName'    => 'test/inner',
     97                        'innerHTML'    => 'inside',
     98                        'innerContent' => array( 'inside' ),
     99                ) );
     100
     101                $this->assertEquals(
     102                        "<!-- wp:test/outer -->\nbefore\n<!-- wp:test/inner -->\ninside\n<!-- /wp:test/inner -->\nafter\n<!-- /wp:test/outer -->",
     103                        serialize_block( self::make_block( array(
     104                                'blockName'    => 'test/outer',
     105                                'innerBlocks'  => array( $inner_block ),
     106                                'innerContent' => array( 'before', null, 'after' ),
     107                                'innerHTML'    => 'beforeafter',
     108                        ) ) )
     109                );
     110        }
     111
     112        function test_serializes_nested_inner_blocks() {
     113                $inner_block = self::make_block( array(
     114                        'blockName'    => 'test/inner',
     115                        'innerHTML'    => 'inside',
     116                        'innerContent' => array( 'inside' ),
     117                ) );
     118
     119                $middle_block = self::make_block( array(
     120                        'blockName'    => 'test/middle',
     121                        'innerBlocks'  => array( $inner_block ),
     122                        'innerContent' => array( null ),
     123                ) );
     124
     125                $this->assertEquals(
     126                        "<!-- wp:test/outer -->\nbefore\n<!-- wp:test/middle -->\n<!-- wp:test/inner -->\ninside\n<!-- /wp:test/inner -->\n<!-- /wp:test/middle -->\nafter\n<!-- /wp:test/outer -->",
     127                        serialize_block( self::make_block( array(
     128                                'blockName'    => 'test/outer',
     129                                'innerBlocks'  => array( $middle_block ),
     130                                'innerContent' => array( 'before', null, 'after' ),
     131                                'innerHTML'    => 'beforeafter',
     132                        ) ) )
     133                );
     134        }
     135
     136        function test_serializes_dangerous_attrs() {
     137                $this->assertEquals(
     138                        '<!-- wp:test/void {"content":"\u003ctag\u003e"} /-->',
     139                        serialize_block( self::make_block( array( 'attrs' => array( 'content' => '<tag>' ) ) ) )
     140                );
     141
     142                $this->assertEquals(
     143                        '<!-- wp:test/void {"content":"\u003c!\u002d\u002d just a comment \u002d\u002d\u003e"} /-->',
     144                        serialize_block( self::make_block( array( 'attrs' => array( 'content' => '<!-- just a comment -->' ) ) ) )
     145                );
     146
     147                $this->assertEquals(
     148                        '<!-- wp:test/void {"content":"\u0022\u0026\u0022"} /-->',
     149                        serialize_block( self::make_block( array( 'attrs' => array( 'content' => '"&"' ) ) ) )
     150                );
     151
     152                $this->assertEquals(
     153                        "<!-- wp:test/test {\"content\":\"\u003ctag\u003e\"} -->\ntest\n<!-- /wp:test/test -->",
     154                        serialize_block( self::make_block( array(
     155                                'blockName'    => 'test/test',
     156                                'attrs'        => array( 'content' => '<tag>' ),
     157                                'innerHTML'    => 'test',
     158                                'innerContent' => array( 'test' ),
     159                        ) ) )
     160                );
     161
     162                $this->assertEquals(
     163                        "<!-- wp:test/test {\"content\":\"\u003c!\u002d\u002d just a comment \u002d\u002d\u003e\"} -->\ntest\n<!-- /wp:test/test -->",
     164                        serialize_block( self::make_block( array(
     165                                'blockName'    => 'test/test',
     166                                'attrs'        => array( 'content' => '<!-- just a comment -->' ),
     167                                'innerHTML'    => 'test',
     168                                'innerContent' => array( 'test' ),
     169                        ) ) )
     170                );
     171
     172                $this->assertEquals(
     173                        "<!-- wp:test/test {\"content\":\"\u0022\u0026\u0022\"} -->\ntest\n<!-- /wp:test/test -->",
     174                        serialize_block( self::make_block( array(
     175                                'blockName'    => 'test/test',
     176                                'attrs'        => array( 'content' => '"&"' ),
     177                                'innerHTML'    => 'test',
     178                                'innerContent' => array( 'test' ),
     179                        ) ) )
     180                );
     181        }
     182
     183        function test_serializes_and_strips_away_core_namespace() {
     184                $this->assertEquals(
     185                        '<!-- wp:test /-->',
     186                        serialize_block( self::make_block( array( 'blockName' => 'core/test' ) ) )
     187                );
     188        }
     189
     190        function test_adds_pretty_whitespace_betwen_blocks() {
     191                $this->assertEquals(
     192                        "<!-- wp:first /-->\n\n<!-- wp:second /-->",
     193                        serialize_blocks( array(
     194                                self::make_block( array( 'blockName' => 'first' ) ),
     195                                self::make_block( array( 'blockName' => 'second' ) ),
     196                        ) )
     197                );
     198        }
     199
     200        static function make_block( $props ) {
     201                return array_merge( array(
     202                        'blockName'    => 'test/void',
     203                        'attrs'        => array(),
     204                        'innerBlocks'  => array(),
     205                        'innerHTML'    => '',
     206                        'innerContent' => array(),
     207                ), $props );
     208        }
     209}