Make WordPress Core

Opened 3 years ago

Last modified 3 weeks ago

#56519 new defect (bug)

Inner blocks serialization bug in serialize_block function

Reported by: saqibsarwar's profile saqibsarwar Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 5.3.1
Component: Editor Keywords: has-patch reporter-feedback
Focuses: Cc:

Description

Hi,

The serialize_block function https://developer.wordpress.org/reference/functions/serialize_block/ fails when the number of inner blocks increases.

The following custom function solved it for me.

<?php
function custom_serialize_block( $block ) {
        $block_content = '';

        foreach ( $block['innerContent'] as $chunk ) {
                if ( is_string( $chunk ) ) {
                        $block_content .= $chunk;
                } else {
                        foreach ( $block['innerBlocks'] as $inner_block ) {
                                $block_content .= custom_serialize_block( $inner_block );
                        }
                }
        }

        if ( ! is_array( $block['attrs'] ) ) {
                $block['attrs'] = [];
        }

        return get_comment_delimited_block_content(
                $block['blockName'],
                $block['attrs'],
                $block_content
        );
}

Attachments (1)

inner-block-serialization-bug.patch (751 bytes) - added by dontfeedthecode 8 months ago.
Patch created from saqibsarwar's resolution to ticket 56519

Download all attachments as: .zip

Change History (7)

#1 @dingo_d
3 years ago

  • Focuses performance coding-standards removed
  • Keywords needs-patch added; has-patch removed

#2 @markparnell
3 years ago

  • Component changed from General to Editor
  • Version changed from 6.0.1 to 5.3.1

@dontfeedthecode
8 months ago

Patch created from saqibsarwar's resolution to ticket 56519

#3 @dontfeedthecode
8 months ago

  • Keywords has-patch added; needs-patch removed

I've created a patch from the fix provider by @saqibsarwar and attached it here, happy to assist if any further testing is required.

#4 @dontfeedthecode
8 months ago

  • Keywords needs-testing added

This ticket was mentioned in PR #8973 on WordPress/wordpress-develop by @dontfeedthecode.


8 months ago
#5

This PR introduces a fix provided by saqibsarwar 3 years ago or so, ticket attached to the PR. I've created a patch for this and am providing this PR as the issue has become dormant with no further input from the reporter however the solution itself is sound.

### Problem
The original serialize_block() function relied on index-based matching between innerContent and innerBlocks. This approach breaks when there are nested blocks within non-string innerContent elements, leading to incorrect or incomplete serialization of deeply nested structures.

### Solution
The updated serialize_block() function iterates over innerContent and dynamically processes nested innerBlocks without relying on positional indexing. For each non-string chunk in innerContent, it recursively serializes all child innerBlocks, ensuring accurate rendering and output for arbitrarily nested blocks.

### Benefits
Fixes serialization issues for complex/nested block layouts
Removes dependency on matching innerContent and innerBlocks indexes
Ensures consistent and correct block output, especially for custom blocks with dynamic inner structures

Trac ticket: https://core.trac.wordpress.org/ticket/56519

#6 @ozgursar
3 weeks ago

  • Keywords reporter-feedback added; needs-testing removed

Reproduction Report

Environment

  • WordPress: 7.0-alpha-61215-src
  • PHP: 8.2.29
  • Server: nginx/1.29.4
  • Database: mysqli (Server: 8.4.7 / Client: mysqlnd 8.2.29)
  • Browser: Chrome 144.0.0.0
  • OS: macOS
  • Theme: Twenty Twenty-Five 1.4
  • MU Plugins: None activated
  • Plugins:
    • Code Snippets 3.9.4
    • Test Reports 1.2.1

Steps taken

  1. Add the following snippet via Code Snippets plugin or functions.php to test the serialize_block() function
add_action( 'init', function() {
    if ( isset( $_GET['reproduce_bug'] ) ) {
        
        echo "<h1>Reproduce Inner Blocks Serialization Bug</h1>";
        
        // Create a block with 3 inner blocks
        $test_block = array(
            'blockName' => 'core/group',
            'attrs' => array(),
            'innerBlocks' => array(
                array(
                    'blockName' => 'core/paragraph',
                    'attrs' => array(),
                    'innerBlocks' => array(),
                    'innerHTML' => '<p>First</p>',
                    'innerContent' => array( '<p>First</p>' ),
                ),
                array(
                    'blockName' => 'core/paragraph',
                    'attrs' => array(),
                    'innerBlocks' => array(),
                    'innerHTML' => '<p>Second</p>',
                    'innerContent' => array( '<p>Second</p>' ),
                ),
                array(
                    'blockName' => 'core/paragraph',
                    'attrs' => array(),
                    'innerBlocks' => array(),
                    'innerHTML' => '<p>Third</p>',
                    'innerContent' => array( '<p>Third</p>' ),
                ),
            ),
            'innerHTML' => '<div class="wp-block-group"></div>',
            'innerContent' => array(
                '<div class="wp-block-group">',
                null,
                null,
                null,
                '</div>',
            ),
        );
        
        echo "<h2>Test Case:</h2>";
        echo "<p>Group block with 3 inner paragraph blocks</p>";
        echo "<p>innerContent has 3 null placeholders</p>";
        
        echo "<h2>Calling Core serialize_block():</h2>";
        $result = serialize_block( $test_block );
        
        echo "<pre>" . htmlspecialchars( $result ) . "</pre>";
        
        echo "<h2>Bug Check:</h2>";
        
        $first_count = substr_count( $result, 'First' );
        $second_count = substr_count( $result, 'Second' );
        $third_count = substr_count( $result, 'Third' );
        $para_count = substr_count( $result, 'wp:paragraph' ) / 2;
        
        echo "<table border='1'>";
        echo "<tr><th>Expected</th><th>Actual</th><th>Status</th></tr>";
        echo "<tr><td>3 paragraphs total</td><td>{$para_count} paragraphs</td><td>" . ($para_count == 3 ? '✓' : '✗ BUG') . "</td></tr>";
        echo "<tr><td>'First' appears 1x</td><td>{$first_count} times</td><td>" . ($first_count == 1 ? '✓' : '✗ BUG') . "</td></tr>";
        echo "<tr><td>'Second' appears 1x</td><td>{$second_count} times</td><td>" . ($second_count == 1 ? '✓' : '✗ BUG') . "</td></tr>";
        echo "<tr><td>'Third' appears 1x</td><td>{$third_count} times</td><td>" . ($third_count == 1 ? '✓' : '✗ BUG') . "</td></tr>";
        echo "</table>";
        
        if ( $para_count > 3 || $first_count > 1 || $second_count > 1 || $third_count > 1 ) {
            echo "<h2 style='color:red;'>✗ BUG CONFIRMED: Content is duplicated!</h2>";
            echo "<p>The serialize_block() function is duplicating inner blocks.</p>";
        } else {
            echo "<h2 style='color:green;'>✓ Bug NOT present in this WordPress version</h2>";
            echo "<p>The issue appears to be already fixed.</p>";
        }
        
        exit;
    }
});
  1. Visit http://localhost:8889/?reproduce_bug=1
  2. ❌ Bug is not occurring

Expected behavior

  • Ticket reports serialize_block() fails when the number of inner blocks increases.

Screenshots/Screencast with results

https://i.imgur.com/U4jyvdJ.png

Note: See TracTickets for help on using tickets.