Skip to content
Closed
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7f1f6a0
Introduce data provider to allow extending test coverage
ockham Aug 13, 2025
054756d
Add test coverage for Button block's text attribute
ockham Aug 13, 2025
b328dee
Block Bindings: Simplify replace_html() method
ockham Aug 13, 2025
21e80e2
Add WP_Block_Bindings_Processor class
ockham Aug 18, 2025
1c70bc3
Use WP_Block_Bindings_Processor for block bindings
ockham Aug 18, 2025
8f76759
Add kses back :/
ockham Aug 18, 2025
b7b7ca5
WPCS
ockham Aug 18, 2025
bb7c906
Remove obsolete var
ockham Aug 18, 2025
103a5c4
Indentation
ockham Aug 18, 2025
3f2e32b
Return true upon success
ockham Aug 18, 2025
85b6354
Add basic PHPDoc
ockham Aug 18, 2025
b67890e
Add more PHPDoc
ockham Aug 19, 2025
6bc4fd5
Add basic test coverage
ockham Aug 19, 2025
d943cd8
Allow setting attributes
ockham Aug 19, 2025
527c5d8
Increase test coverage
ockham Aug 19, 2025
86e836d
Increase test coverage
ockham Aug 20, 2025
dad7380
Add more commentary and warnings
ockham Aug 20, 2025
88af5ff
Do not explose block bindings processor class
sirreal Aug 21, 2025
a56f978
Remove block bindings processor class
sirreal Aug 21, 2025
7c3fc45
wpcs
sirreal Aug 21, 2025
66fef38
Make the hidden class instance static
sirreal Aug 21, 2025
24a0b0d
Use Reflection to make tests work again
ockham Aug 25, 2025
2e7df73
Block Bindings Processor: Tweak test to break with current implementa…
ockham Aug 25, 2025
e2f0a38
Block Bindings Processor: Base implementation on WP_HTML_Text_Replace…
ockham Aug 25, 2025
b045050
Remove now-obsolete build() alias method
ockham Aug 25, 2025
e739c6c
Rename test file
ockham Aug 25, 2025
6a4a3a3
Correct @since PHPDoc
ockham Aug 25, 2025
4dbb0e5
More descriptive variable names
ockham Aug 26, 2025
a18e435
Remove static var
ockham Aug 26, 2025
3410f40
Make sure we're not stopped on atomic/void/self-closing element
ockham Aug 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove static var
  • Loading branch information
ockham committed Aug 26, 2025
commit a18e435b2af10a73aa5c7e86eb11dfbc2ea9396c
73 changes: 35 additions & 38 deletions src/wp-includes/class-wp-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -462,50 +462,47 @@ private function replace_html( string $block_content, string $attribute_name, $s
}

private static function get_block_bindings_processor( string $block_content ) {
static $internal_processor_class = null;
if ( null === $internal_processor_class ) {
$internal_processor_class = new class('', WP_HTML_Processor::CONSTRUCTOR_UNLOCK_CODE) extends WP_HTML_Processor {
/**
* Replace the rich text content between a tag opener and matching closer.
*
* When stopped on a tag opener, replace the content enclosed by it and its
* matching closer with the provided rich text.
*
* @param string $rich_text The rich text to replace the original content with.
* @return bool True on success.
*/
public function replace_rich_text( $rich_text ) {
if ( $this->is_tag_closer() ) {
return false;
}
$internal_processor_class = new class('', WP_HTML_Processor::CONSTRUCTOR_UNLOCK_CODE) extends WP_HTML_Processor {
/**
* Replace the rich text content between a tag opener and matching closer.
*
* When stopped on a tag opener, replace the content enclosed by it and its
* matching closer with the provided rich text.
*
* @param string $rich_text The rich text to replace the original content with.
* @return bool True on success.
*/
public function replace_rich_text( $rich_text ) {
if ( $this->is_tag_closer() ) {
return false;
}

$depth = $this->get_current_depth();
$depth = $this->get_current_depth();

$this->set_bookmark( '_wp_block_bindings_tag_opener' );
// The bookmark names are prefixed with `_` so the key below has an extra `_`.
$tag_opener = $this->bookmarks['__wp_block_bindings_tag_opener'];
$start = $tag_opener->start + $tag_opener->length;
$this->release_bookmark( '_wp_block_bindings_tag_opener' );
$this->set_bookmark( '_wp_block_bindings_tag_opener' );
// The bookmark names are prefixed with `_` so the key below has an extra `_`.
$tag_opener = $this->bookmarks['__wp_block_bindings_tag_opener'];
$start = $tag_opener->start + $tag_opener->length;
$this->release_bookmark( '_wp_block_bindings_tag_opener' );
Copy link
Member

@dmsnell dmsnell Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is certainly welcome to do, to release the bookmarks, but performance-wise it’s not setting a better example than leaving them dangling.

given that this function contains all of the scanning, it’s even fine to trap the bookmark bounds and re-use one, such as set_bookmark( 'here' ). I’ve considered exposing something like ->current_token() but haven’t yet.

part of this is the fact that we expect isolation of bookmarks here and we will dismiss the processor when we’re done.


// Find matching tag closer.
while ( $this->next_token() && $this->get_current_depth() >= $depth ) {
}
// Find matching tag closer.
while ( $this->next_token() && $this->get_current_depth() >= $depth ) {
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reasonable follow-up idea: verify that the processor completed and didn’t pause at an incomplete token or reach unsupported markup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally addressing these notes over at #10029.

This look about good? 8d7590e
(I was at first thinking to check if state was STATE_MATCHED_TAG, but then I thought, the higher-level check should give me that anyway.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good; I left a comment on the new PR.


$this->set_bookmark( '_wp_block_bindings_tag_closer' );
$tag_closer = $this->bookmarks['__wp_block_bindings_tag_closer'];
$end = $tag_closer->start;
$this->release_bookmark( '_wp_block_bindings_tag_closer' );
$this->set_bookmark( '_wp_block_bindings_tag_closer' );
$tag_closer = $this->bookmarks['__wp_block_bindings_tag_closer'];
$end = $tag_closer->start;
$this->release_bookmark( '_wp_block_bindings_tag_closer' );

$this->lexical_updates[] = new WP_HTML_Text_Replacement(
$start,
$end - $start,
$rich_text
);
$this->lexical_updates[] = new WP_HTML_Text_Replacement(
$start,
$end - $start,
$rich_text
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applying lexical updates that span token boundaries is not something we have particularly tested well. it’s probably working well enough and hopefully the tests here will catch them, but this is sort of uncharted territory within the HTML Processor. we should make sure that we don’t seek anywhere here to verify that we don’t cross indices.

this single issue is one of the main reasons I proposed the single linear pass of the serialization builder, because it structurally prevents the ability of jumping around or referring to indices after changing positions.

I’m not particularly confident I would know how this interacts with virtual tokens, for example, whose boundaries are coincident with bookmarks (vs. realized tokens which all have non-overlapping spans)


return true;
}
};
}
return true;
}
};

return $internal_processor_class::create_fragment( $block_content );
}
Expand Down
Loading