Changeset 63913
- Timestamp:
- 09/24/2026 05:53:51 AM (4 days ago)
- Location:
- trunk
- Files:
-
- 2 edited
-
src/wp-includes/class-wp-block-processor.php (modified) (7 diffs)
-
tests/phpunit/tests/block-processor/wpBlockProcessor.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-wp-block-processor.php
r63911 r63913 980 980 * even though it allows for matching invalid JSON content. 981 981 * 982 * The delimiter must also be a single complete HTML comment. 983 * 982 984 * <!-- /wp:core/paragraph {"dropCap":true} /-⃨-⃨>⃨ 983 985 */ 984 $comment_closing_at = strpos( $text, '-->', $json_at ); 985 if ( false === $comment_closing_at ) { 986 $after_comment_end = $this->find_html_comment_end( $comment_opening_at, $end ); 987 988 /* 989 * The reported end of the comment could be after the end of the document if 990 * no actual end was found, so differentiate a comment ending at the end of 991 * the document from documents with missing comment ends. 992 */ 993 if ( $after_comment_end >= $end && ! str_ends_with( $text, '-->' ) && ! str_ends_with( $text, '--!>' ) ) { 986 994 goto incomplete; 987 995 } 996 997 /* 998 * Only normative comment closers are recognized block delimiters, 999 * so skip past any HTML comments ending in `--!>`. 1000 */ 1001 if ( '!' === $text[ $after_comment_end - 2 ] ) { 1002 $at = $after_comment_end; 1003 continue; 1004 } 1005 1006 $comment_closing_at = $after_comment_end - 3; 988 1007 989 1008 // <!-- /wp:core/paragraph {"dropCap":true} /⃨--> … … 1008 1027 } 1009 1028 1010 $at = $ this->find_html_comment_end( $comment_opening_at, $end );1029 $at = $after_comment_end; 1011 1030 continue; 1012 1031 } … … 1016 1035 * 1017 1036 * @todo It’s likely faster to scan forward instead of in reverse. 1037 * @todo Skip ahead with `strcspn()` and decide only on syntax characters. 1018 1038 * 1019 1039 * <!-- /wp:core/paragraph {"dropCap":true}⃨ ⃨/--> … … 1037 1057 1038 1058 default: 1039 ++$at;1059 $at = $after_comment_end; 1040 1060 continue 3; 1041 1061 } … … 1047 1067 */ 1048 1068 if ( 0 === $json_length || 0 === $after_json_whitespace_length ) { 1049 $at = $ this->find_html_comment_end( $comment_opening_at, $end );1069 $at = $after_comment_end; 1050 1070 continue; 1051 1071 } … … 1058 1078 // The end of the document was reached without a match. 1059 1079 if ( self::MATCHED !== $this->state ) { 1080 // Stop at top-level free-form HTML at the end of the document. 1081 if ( $after_prev_delimiter < $end ) { 1082 $this->state = self::HTML_SPAN; 1083 $this->after_previous_delimiter = $after_prev_delimiter; 1084 $this->matched_delimiter_at = $end; 1085 $this->matched_delimiter_length = 0; 1086 $this->open_blocks_at[] = $after_prev_delimiter; 1087 $this->open_blocks_length[] = 0; 1088 $this->was_void = true; 1089 1090 return true; 1091 } 1092 1060 1093 $this->state = self::COMPLETE; 1061 1094 return false; … … 1337 1370 '>' === $text[ $comment_starting_at + 2 + $span_of_dashes ] 1338 1371 ) { 1339 return $comment_starting_at + $span_of_dashes + 1;1372 return $comment_starting_at + 2 + $span_of_dashes + 1; 1340 1373 } 1341 1374 -
trunk/tests/phpunit/tests/block-processor/wpBlockProcessor.php
r63911 r63913 311 311 312 312 /** 313 * Verifies that HTML comments which end a document but which are not block 314 * delimiters are incorporated into the final HTML span. 315 * 316 * @ticket 66138 317 * 318 * @dataProvider data_terminal_non_delimiter_comments 319 * 320 * @param string $html Input ending in a non-delimiter HTML comment. 321 */ 322 public function test_preserves_terminal_non_delimiter_comments_as_html( string $html ): void { 323 $processor = new WP_Block_Processor( $html ); 324 325 $this->assertTrue( 326 $processor->next_token(), 327 'Should have found a single HTML span but found nothing: check test setup.' 328 ); 329 330 $this->assertTrue( 331 $processor->is_html(), 332 'Should have matched an HTML span.' 333 ); 334 335 $this->assertSame( 336 $html, 337 $processor->get_html_content(), 338 'Should have preserved the complete terminal HTML span.' 339 ); 340 341 $this->assertFalse( 342 $processor->next_token(), 343 'Should have only found one token, an HTML comment, but found more: check test setup.' 344 ); 345 346 $this->assertNull( 347 $processor->get_last_error(), 348 'Should have finished without error.' 349 ); 350 } 351 352 /** 353 * Data provider. 354 * 355 * @return array<string, array{0: string}> 356 */ 357 public static function data_terminal_non_delimiter_comments(): array { 358 return array( 359 'Ordinary comment' => array( 'content<!-- x -->' ), 360 'Abrupt empty comment' => array( 'content<!-->' ), 361 'Rejected JSON attributes' => array( 'content<!-- wp:a {x -->' ), 362 'Exclamation comment ending' => array( 'content<!-- wp:a --!>' ), 363 ); 364 } 365 366 /** 313 367 * Verifies that incomplete HTML comments which could not produce delimiters 314 368 * are not considered incomplete input by the processor. … … 536 590 $processor->next_delimiter(), 537 591 "Should have failed to find block delimiter but found '{$processor->get_block_type()}' instead." 592 ); 593 } 594 595 /** 596 * Verifies that a delimiter does not span the end of the comment it started in. 597 * 598 * An HTML comment ends at its first `-->` or `--!>`. The scan looked only for 599 * `-->`, so a comment ended by `--!>` was stepped over and a later `-->` was 600 * taken as the end of the delimiter, producing a delimiter which spanned two 601 * comments. 602 * 603 * @ticket 66138 604 * 605 * @dataProvider data_delimiters_and_comment_endings 606 * 607 * @param string $html Document to scan. 608 * @param string[] $block_types Printable block type of every delimiter in the document, in order. 609 */ 610 public function test_delimiter_does_not_span_a_comment_ending( string $html, array $block_types ): void { 611 $processor = new WP_Block_Processor( $html ); 612 613 $found = array(); 614 while ( $processor->next_delimiter() ) { 615 $found[] = $processor->get_printable_block_type(); 616 } 617 618 $this->assertSame( 619 $block_types, 620 $found, 621 'Should have found only the delimiters which are in the document.' 622 ); 623 } 624 625 /** 626 * Verifies that a comment ended by `--!>` at the end of the document is not 627 * reported as incomplete input: the comment is closed, so no delimiter could 628 * be completed by further input. 629 * 630 * @ticket 66138 631 */ 632 public function test_exclamation_ending_at_end_of_document_is_not_incomplete_input(): void { 633 $processor = new WP_Block_Processor( '<!-- wp:a {"k":"x --!>' ); 634 635 $this->assertFalse( 636 $processor->next_delimiter(), 637 "Should have found no delimiter but found a '{$processor->get_block_type()}' instead." 638 ); 639 640 $this->assertNull( 641 $processor->get_last_error(), 642 'Should have completed without reporting an error.' 643 ); 644 } 645 646 /** 647 * Data provider. 648 * 649 * @return array<string, array{0: string, 1: string[]}> 650 */ 651 public static function data_delimiters_and_comment_endings(): array { 652 return array( 653 // The comment ended at `--!>`, so the later `-->` is not the delimiter's end. 654 'Spans an exclamation ending' => array( '<!-- wp:a {"k":"x --!><!-- y"} -->', array() ), 655 'Spans two exclamation endings' => array( '<!-- wp:a {"k":"x --!><!-- y --!><!-- z"} -->', array() ), 656 'Exclamation ending at the end' => array( '<!-- wp:a {"k":"x --!>', array() ), 657 'Repeated exclamation endings' => array( str_repeat( '<!-- wp:a {"k":" --!>', 100 ) . '"} -->', array() ), 658 659 // Rejected for other reasons, asserted so the comment ending is not what rescues them. 660 'Exclamation ending, no JSON' => array( '<!-- wp:a --!><!-- "} -->', array() ), 661 'Opening inside a dashed comment' => array( '<!---<!-- wp:a {"k":1} -->', array() ), 662 663 // Delimiters which stay inside one comment are unaffected. 664 'Plain delimiter' => array( '<!-- wp:a -->', array( 'core/a' ) ), 665 'Delimiter with attributes' => array( '<!-- wp:a {"k":1} -->', array( 'core/a' ) ), 666 'Void delimiter' => array( '<!-- wp:a {"k":1} /-->', array( 'core/a' ) ), 667 'Hyphens inside the attributes' => array( '<!-- wp:a {"k":"x--y"} -->', array( 'core/a' ) ), 668 'Dash run inside the attributes' => array( '<!-- wp:a {"k":"x-----y"} -->', array( 'core/a' ) ), 669 'Dash run then bang' => array( '<!-- wp:a {"k":"x---!y"} -->', array( 'core/a' ) ), 670 'Bang then dashes' => array( '<!-- wp:a {"k":"x--!-y"} -->', array( 'core/a' ) ), 671 'Dashes before the closer' => array( '<!-- wp:a {"k":"x--"} -->', array( 'core/a' ) ), 672 'Dash run before a space closer' => array( '<!-- wp:a {"k":"x"} --- -->', array() ), 673 'Delimiter after a closed comment' => array( '<!-- x --!><!-- wp:a {"k":1} -->', array( 'core/a' ) ), 674 ); 675 } 676 677 /** 678 * Verifies that a comment whose JSON attributes are rejected is skipped whole. 679 * 680 * A block delimiter is a single HTML comment. When the content after the JSON 681 * attributes rules out a delimiter, the scan resumes after that comment, not one 682 * byte later, so a `<!--` inside the comment's text is not treated as the start 683 * of another delimiter. 684 * 685 * @ticket 66138 686 * 687 * @dataProvider data_comments_with_rejected_json_attributes 688 * 689 * @param string $html Document to scan. 690 * @param string[] $block_types Printable block type of every delimiter in the document, in order. 691 */ 692 public function test_skips_the_whole_comment_when_the_json_attributes_are_rejected( string $html, array $block_types ): void { 693 $processor = new WP_Block_Processor( $html ); 694 695 $found = array(); 696 while ( $processor->next_delimiter() ) { 697 $found[] = $processor->get_printable_block_type(); 698 } 699 700 $this->assertSame( 701 $block_types, 702 $found, 703 'Should have found only the delimiters which are in the document.' 704 ); 705 } 706 707 /** 708 * Data provider. 709 * 710 * @return array<string, array{0: string, 1: string[]}> 711 */ 712 public static function data_comments_with_rejected_json_attributes(): array { 713 return array( 714 // A `<!--` inside the comment's text does not start a delimiter. 715 'Opening inside the comment' => array( '<!-- wp:a {x <!-- wp:b -->', array() ), 716 'Void opening inside the comment' => array( '<!-- wp:a {x <!-- wp:b /-->', array() ), 717 'Closer inside the comment' => array( '<!-- wp:a {x <!-- /wp:b -->', array() ), 718 719 // A delimiter in a later comment is still found. 720 'Delimiter in the next comment' => array( '<!-- wp:a {x --><!-- wp:b -->', array( 'core/b' ) ), 721 'Content after the JSON' => array( '<!-- wp:a {"k":1} x --><!-- wp:b -->', array( 'core/b' ) ), 722 'Valid delimiter first' => array( '<!-- wp:a {"k":1} --><!-- wp:b {y -->', array( 'core/a' ) ), 538 723 ); 539 724 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)