Commit 00025498a9 for wordpress.org

commit 00025498a9a5bd4d5c24b53f60ae0c3f710172c4
Author: dmsnell <dmsnell@git.wordpress.org>
Date:   Fri Oct 30 20:38:55 2026 +0000

    Blocks: Fix duplicated content when implicitly closing a final open block

    When a fix was recently applied to the block parser for implicitly closing open blocks that remain open at the end of a document, a mistake was made when collecting HTML spans preceding the still-open blocks. This led to duplicated content in documents missing expected closing delimiters.

    This patch adjusts the implict closing algorithm to properly detect those HTML spans.

    Developed in: https://github.com/WordPress/wordpress-develop/pull/15197
    Discussed in: https://core.trac.wordpress.org/ticket/66138

    Follow-up to r64996.

    Props chihsuan, dmsnell.
    See #66138.

    Built from https://develop.svn.wordpress.org/trunk@65090


    git-svn-id: http://core.svn.wordpress.org/trunk@64240 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/class-wp-block-parser.php b/wp-includes/class-wp-block-parser.php
index 5adc9d95e6..06342fc5ac 100644
--- a/wp-includes/class-wp-block-parser.php
+++ b/wp-includes/class-wp-block-parser.php
@@ -395,40 +395,55 @@ class WP_Block_Parser {
 	 * @since 7.2.0
 	 */
 	public function implicitly_close_and_add_all_open_blocks(): void {
-		$implicitly_closed = null;
-		$last_freeform     = null;
-
+		$last_closed_block = null;
+		$last_opener_at    = strlen( $this->document );
+		$last_leading_html = null;
+
+		/**
+		 * There is a stack of open blocks, starting with a top-level block,
+		 * and where each successive item in the stack is a child of that block.
+		 * There are also spans of HTML content between the block delimiters.
+		 *
+		 * As the stack is collapsed, any HTML segments within that block must
+		 * be collected into it, before closing and appending to its parent’s
+		 * inner blocks array.
+		 */
 		while ( null !== ( $stack_top = array_pop( $this->stack ) ) ) {
-			if ( isset( $last_freeform ) ) {
-				$html = substr( $this->document, $stack_top->prev_offset, $last_freeform[1] - $stack_top->prev_offset );
-			} else {
-				$html = substr( $this->document, $stack_top->prev_offset );
-			}
+			/*
+			 * Since there is no open inner block on the top-most block on the stack,
+			 * the first iteration’s remaining HTML span will be from the last `prev_offset`
+			 * until the end of the document. For each containing parent, it will run from
+			 * the `prev_offset` until the opening of the block that was just closed.
+			 */
+			$html_length = $last_opener_at - $stack_top->prev_offset;
+			if ( 0 !== $html_length ) {
+				$html = substr( $this->document, $stack_top->prev_offset, $html_length );

-			if ( '' !== $html ) {
 				$stack_top->block->innerHTML     .= $html;
 				$stack_top->block->innerContent[] = $html;
 			}

-			// Trap potential leading freeform content for the final output.
-			$last_freeform = array( $stack_top->leading_html_start ?? 0, $stack_top->token_start );
-
-			if ( isset( $implicitly_closed ) ) {
+			if ( isset( $last_closed_block ) ) {
 				$stack_top->block->innerContent[] = null;
-				$stack_top->block->innerBlocks[]  = (array) $implicitly_closed;
+				$stack_top->block->innerBlocks[]  = (array) $last_closed_block;
 			}

-			$implicitly_closed = $stack_top->block;
+			$last_closed_block = $stack_top->block;
+			$last_opener_at    = $stack_top->token_start;
+			$last_leading_html = $stack_top->leading_html_start;
 		}

-		if ( isset( $last_freeform ) && $last_freeform[1] > $last_freeform[0] ) {
-			$html           = substr( $this->document, $last_freeform[0], $last_freeform[1] - $last_freeform[0] );
+		// Collect any HTML preceding the outermost open block.
+		if ( isset( $last_leading_html ) && $last_opener_at > $last_leading_html ) {
+			$html           = substr( $this->document, $last_leading_html, $last_opener_at - $last_leading_html );
 			$this->output[] = (array) $this->freeform( $html );
 		}

-		if ( isset( $implicitly_closed ) ) {
-			$this->output[] = (array) $implicitly_closed;
+		if ( isset( $last_closed_block ) ) {
+			$this->output[] = (array) $last_closed_block;
 		}
+
+		$this->offset = strlen( $this->document );
 	}
 }

diff --git a/wp-includes/version.php b/wp-includes/version.php
index 03219aaca9..47576d75a5 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta3-65089';
+$wp_version = '7.2-beta3-65090';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.