WordPress.org

Make WordPress Core

Ticket #47375: 47375-4.diff

File 47375-4.diff, 11.0 KB (added by jorgefilipecosta, 5 months ago)
  • src/wp-includes/blocks.php

     
    276276}
    277277
    278278/**
     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        $inner_block_index = 0;
     311        foreach ( $block['innerContent'] as $chunk ) {
     312                $output .= null === $chunk
     313                        ? serialize_block( $block['innerBlocks'][ $inner_block_index++ ] )
     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
     336/**
    279337 * Parses blocks out of a content string.
    280338 *
    281339 * @since 5.0.0
  • tests/phpunit/tests/blocks/serialize.php

     
     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
     19        /**
     20         * Test: test_serializes_freeform_block() should serialize a freeform block.
     21         *
     22         * @since 5.3.0
     23         */
     24        public function test_serializes_freeform_block() {
     25                $this->assertEquals(
     26                        '<strong>Success!</strong>',
     27                        serialize_block(
     28                                array(
     29                                        'blockName'    => null,
     30                                        'attrs'        => array(),
     31                                        'innerBlocks'  => array(),
     32                                        'innerHTML'    => '<strong>Success!</strong>',
     33                                        'innerContent' => array( '<strong>Success!</strong>' ),
     34                                )
     35                        )
     36                );
     37        }
     38
     39        /**
     40         * Test: test_serializes_void_block_without_attributes() should serialize a void block without attributes.
     41         *
     42         * @since 5.3.0
     43         */
     44        public function test_serializes_void_block_without_attributes() {
     45                $this->assertEquals(
     46                        '<!-- wp:test/void /-->',
     47                        serialize_block( self::make_block( array() ) )
     48                );
     49        }
     50
     51        /**
     52         * Test: test_serializes_void_block_with_attributes() should serialize a void block with attributes.
     53         *
     54         * @since 5.3.0
     55         */
     56        public function test_serializes_void_block_with_attributes() {
     57                $this->assertEquals(
     58                        '<!-- wp:test/void {"align":"left"} /-->',
     59                        serialize_block( self::make_block( array( 'attrs' => array( 'align' => 'left' ) ) ) )
     60                );
     61        }
     62
     63        /**
     64         * Test: test_serializes_block_without_attributes() should serialize a block without attributes.
     65         *
     66         * @since 5.3.0
     67         */
     68        public function test_serializes_block_without_attributes() {
     69                $this->assertEquals(
     70                        "<!-- wp:test/test -->\nOnce I ate a cheeseburger.\n<!-- /wp:test/test -->",
     71                        serialize_block(
     72                                self::make_block(
     73                                        array(
     74                                                'blockName'    => 'test/test',
     75                                                'innerHTML'    => 'Once I ate a cheeseburger.',
     76                                                'innerContent' => array( 'Once I ate a cheeseburger.' ),
     77                                        )
     78                                )
     79                        )
     80                );
     81        }
     82
     83        /**
     84         * Test: test_serializes_block_with_attributes() should serialize a block with attributes.
     85         *
     86         * @since 5.3.0
     87         */
     88        public function test_serializes_block_with_attributes() {
     89                $this->assertEquals(
     90                        "<!-- wp:test/test {\"count\":3} -->\nOnce I ate a cheeseburger.\n<!-- /wp:test/test -->",
     91                        serialize_block(
     92                                self::make_block(
     93                                        array(
     94                                                'blockName'    => 'test/test',
     95                                                'attrs'        => array( 'count' => 3 ),
     96                                                'innerHTML'    => 'Once I ate a cheeseburger.',
     97                                                'innerContent' => array( 'Once I ate a cheeseburger.' ),
     98                                        )
     99                                )
     100                        )
     101                );
     102        }
     103
     104        /**
     105         * Test: test_serializes_one_void_inner_block() should serialize a void inner block.
     106         *
     107         * @since 5.3.0
     108         */
     109        public function test_serializes_one_void_inner_block() {
     110                $inner_block = self::make_block( array( 'blockName' => 'test/inner' ) );
     111
     112                $this->assertEquals(
     113                        "<!-- wp:test/outer -->\n<!-- wp:test/inner /-->\n<!-- /wp:test/outer -->",
     114                        serialize_block(
     115                                self::make_block(
     116                                        array(
     117                                                'blockName'    => 'test/outer',
     118                                                'innerBlocks'  => array( $inner_block ),
     119                                                'innerContent' => array( null ),
     120                                        )
     121                                )
     122                        )
     123                );
     124        }
     125
     126        /**
     127         * Test: test_serializes_many_inner_blocks() should serialize multiple inner blocks.
     128         *
     129         * @since 5.3.0
     130         */
     131        public function test_serializes_many_inner_blocks() {
     132                $inner_block = self::make_block( array( 'blockName' => 'test/inner' ) );
     133
     134                $this->assertEquals(
     135                        "<!-- wp:test/outer -->\n<!-- wp:test/inner /-->\n<!-- wp:test/inner /-->\n<!-- /wp:test/outer -->",
     136                        serialize_block(
     137                                self::make_block(
     138                                        array(
     139                                                'blockName'    => 'test/outer',
     140                                                'innerBlocks'  => array( $inner_block, $inner_block ),
     141                                                'innerContent' => array( null, null ),
     142                                        )
     143                                )
     144                        )
     145                );
     146        }
     147
     148        /**
     149         * Test: test_serializes_mixed_content() should serialize mixed content.
     150         *
     151         * @since 5.3.0
     152         */
     153        public function test_serializes_mixed_content() {
     154                $inner_block = self::make_block(
     155                        array(
     156                                'blockName'    => 'test/inner',
     157                                'innerHTML'    => 'inside',
     158                                'innerContent' => array( 'inside' ),
     159                        )
     160                );
     161
     162                $this->assertEquals(
     163                        "<!-- wp:test/outer -->\nbefore\n<!-- wp:test/inner -->\ninside\n<!-- /wp:test/inner -->\nafter\n<!-- /wp:test/outer -->",
     164                        serialize_block(
     165                                self::make_block(
     166                                        array(
     167                                                'blockName'    => 'test/outer',
     168                                                'innerBlocks'  => array( $inner_block ),
     169                                                'innerContent' => array( 'before', null, 'after' ),
     170                                                'innerHTML'    => 'beforeafter',
     171                                        )
     172                                )
     173                        )
     174                );
     175        }
     176
     177        /**
     178         * Test: test_serializes_nested_inner_blocks() should serialize nested inner blocks.
     179         *
     180         * @since 5.3.0
     181         */
     182        public function test_serializes_nested_inner_blocks() {
     183                $inner_block = self::make_block(
     184                        array(
     185                                'blockName'    => 'test/inner',
     186                                'innerHTML'    => 'inside',
     187                                'innerContent' => array( 'inside' ),
     188                        )
     189                );
     190
     191                $middle_block = self::make_block(
     192                        array(
     193                                'blockName'    => 'test/middle',
     194                                'innerBlocks'  => array( $inner_block ),
     195                                'innerContent' => array( null ),
     196                        )
     197                );
     198
     199                $this->assertEquals(
     200                        "<!-- 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 -->",
     201                        serialize_block(
     202                                self::make_block(
     203                                        array(
     204                                                'blockName'    => 'test/outer',
     205                                                'innerBlocks'  => array( $middle_block ),
     206                                                'innerContent' => array( 'before', null, 'after' ),
     207                                                'innerHTML'    => 'beforeafter',
     208                                        )
     209                                )
     210                        )
     211                );
     212        }
     213
     214        /**
     215         * Test: test_serializes_dangerous_attrs() should serialize with escaping dangerous attributes.
     216         *
     217         * @since 5.3.0
     218         */
     219        public function test_serializes_dangerous_attrs() {
     220                $this->assertEquals(
     221                        '<!-- wp:test/void {"content":"\u003ctag\u003e"} /-->',
     222                        serialize_block( self::make_block( array( 'attrs' => array( 'content' => '<tag>' ) ) ) )
     223                );
     224
     225                $this->assertEquals(
     226                        '<!-- wp:test/void {"content":"\u003c!\u002d\u002d just a comment \u002d\u002d\u003e"} /-->',
     227                        serialize_block( self::make_block( array( 'attrs' => array( 'content' => '<!-- just a comment -->' ) ) ) )
     228                );
     229
     230                $this->assertEquals(
     231                        '<!-- wp:test/void {"content":"\u0022\u0026\u0022"} /-->',
     232                        serialize_block( self::make_block( array( 'attrs' => array( 'content' => '"&"' ) ) ) )
     233                );
     234
     235                $this->assertEquals(
     236                        "<!-- wp:test/test {\"content\":\"\u003ctag\u003e\"} -->\ntest\n<!-- /wp:test/test -->",
     237                        serialize_block(
     238                                self::make_block(
     239                                        array(
     240                                                'blockName'    => 'test/test',
     241                                                'attrs'        => array( 'content' => '<tag>' ),
     242                                                'innerHTML'    => 'test',
     243                                                'innerContent' => array( 'test' ),
     244                                        )
     245                                )
     246                        )
     247                );
     248
     249                $this->assertEquals(
     250                        "<!-- wp:test/test {\"content\":\"\u003c!\u002d\u002d just a comment \u002d\u002d\u003e\"} -->\ntest\n<!-- /wp:test/test -->",
     251                        serialize_block(
     252                                self::make_block(
     253                                        array(
     254                                                'blockName'    => 'test/test',
     255                                                'attrs'        => array( 'content' => '<!-- just a comment -->' ),
     256                                                'innerHTML'    => 'test',
     257                                                'innerContent' => array( 'test' ),
     258                                        )
     259                                )
     260                        )
     261                );
     262
     263                $this->assertEquals(
     264                        "<!-- wp:test/test {\"content\":\"\u0022\u0026\u0022\"} -->\ntest\n<!-- /wp:test/test -->",
     265                        serialize_block(
     266                                self::make_block(
     267                                        array(
     268                                                'blockName'    => 'test/test',
     269                                                'attrs'        => array( 'content' => '"&"' ),
     270                                                'innerHTML'    => 'test',
     271                                                'innerContent' => array( 'test' ),
     272                                        )
     273                                )
     274                        )
     275                );
     276        }
     277
     278        /**
     279         * Test: test_serializes_and_strips_away_core_namespace() should serialize and strip away core namespace.
     280         *
     281         * @since 5.3.0
     282         */
     283        public function test_serializes_and_strips_away_core_namespace() {
     284                $this->assertEquals(
     285                        '<!-- wp:test /-->',
     286                        serialize_block( self::make_block( array( 'blockName' => 'core/test' ) ) )
     287                );
     288        }
     289
     290        /**
     291         * Test: test_adds_pretty_whitespace_between_blocks() should add a whitespace between blocks.
     292         *
     293         * @since 5.3.0
     294         */
     295        public function test_adds_pretty_whitespace_between_blocks() {
     296                $this->assertEquals(
     297                        "<!-- wp:first /-->\n\n<!-- wp:second /-->",
     298                        serialize_blocks(
     299                                array(
     300                                        self::make_block( array( 'blockName' => 'first' ) ),
     301                                        self::make_block( array( 'blockName' => 'second' ) ),
     302                                )
     303                        )
     304                );
     305        }
     306
     307        /**
     308         * Util function that creates a test block.
     309         *
     310         * @since 5.3.0
     311         * @param array $props The properties of the test block.
     312         * @return array The test block.
     313         */
     314        private static function make_block( $props ) {
     315                return array_merge(
     316                        array(
     317                                'blockName'    => 'test/void',
     318                                'attrs'        => array(),
     319                                'innerBlocks'  => array(),
     320                                'innerHTML'    => '',
     321                                'innerContent' => array(),
     322                        ),
     323                        $props
     324                );
     325        }
     326}