wppaste
WordPress

WP_HTML_Processor::seek( string $bookmark_name ): bool

Since
6.4.0
Source
wp-includes/html-api/class-wp-html-processor.php:5534
Moves the internal cursor in the HTML Processor to a given bookmark's location.

Description

Be careful! Seeking backwards to a previous location resets the parser to the start of the document and reparses the entire contents up until it finds the sought-after bookmarked location.

In order to prevent accidental infinite loops, there's a maximum limit on the number of times seek() can be called.

Compatibility

WordPress
since 6.4.0
PHP
7.4–8.6-dev
  • 6.7.7
  • 6.8.8
  • 6.9.7
  • 7.0.4
  • 7.1.0

Present in every tracked release (6.7.7 to 7.1.0), and compiles on PHP 7.4 through 8.6-dev.

Parameters

$bookmark_namestring
Jump to the place in the document identified by this bookmark name.

Return value

bool
Whether the internal cursor was successfully moved to the bookmark's location.

Performance profile

How much work a call to WP_HTML_Processor::seek() does, and what it touches: the algorithmic scaling, the Zend instruction count per call across PHP versions, the hooks it hands control to, and the core code that calls it. Measured from the compiled opcodes, not a stopwatch, so every number is identical on any machine running the same PHP version, and every function in core is ranked by cost.

Cost class
Light

Touches nothing outside its own arguments.

Scaling
Scales with input

The body loops, so the work grows with what you pass in.

Instructions
25–119

Executed per call on PHP 8.5, depending on the branch taken. The body compiles to 158.

Plugin surface
None

Nothing here hands control to plugin code.

Called by
0

Nothing in core calls this; the cost is only what you spend yourself.

What one call costs · 6 distinct outcomes

One number would be a lie: the work depends on which branch runs. These are every distinct cost WP_HTML_Processor::seek() can have, taken from its control-flow graph on PHP 8.5.

WhenInstructionsCalls it makes
$direction !== "backward" && !->next_token()25–40->get_updated_html(), ->is_virtual(), ->next_token()
$direction !== "backward" && !->is_virtual() && $bookmark_starts_at === false30–37->get_updated_html(), ->is_virtual()
$direction === "backward" && !->next_token()81–98->get_updated_html(), ->walk_up(), ->walk_up(), ->change_parsing_namespace(), ::seek(), ->is_virtual(), ->next_token()
$direction === "backward" && !->is_virtual() && $bookmark_starts_at === false86–95->get_updated_html(), ->walk_up(), ->walk_up(), ->change_parsing_namespace(), ::seek(), ->is_virtual()
$direction === "backward" && !->next_token()97–119->get_updated_html(), ->walk_up(), ->walk_up(), ->push(), ->change_parsing_namespace(), ->reset_insertion_mode_appropriately(), array_slice(), ::seek(), ->is_virtual(), ->next_token()
$direction === "backward" && !->is_virtual() && $bookmark_starts_at === false102–116->get_updated_html(), ->walk_up(), ->walk_up(), ->push(), ->change_parsing_namespace(), ->reset_insertion_mode_appropriately(), array_slice(), ::seek(), ->is_virtual()

Across PHP versions

PHPCompiledExecutedBranchesNotes
8.6-dev15825–11913
8.515825–11913
8.415825–11913
8.315825–11913
8.215825–11913
8.115825–119132 fewer instructions than PHP 7.4
7.416025–12113

An instruction is not a fixed amount of time, so a matching count is not necessarily the same speed; what it rules out is a difference in the work itself.

Uses · 8

Source code

	public function seek( $bookmark_name ): bool {		// Flush any pending updates to the document before beginning.		$this->get_updated_html(); 		$actual_bookmark_name = "_{$bookmark_name}";		$processor_started_at = $this->state->current_token			? $this->bookmarks[ $this->state->current_token->bookmark_name ]->start			: 0;		$bookmark_starts_at   = $this->bookmarks[ $actual_bookmark_name ]->start;		$direction            = $bookmark_starts_at > $processor_started_at ? 'forward' : 'backward'; 		/*		 * If seeking backwards, it's possible that the sought-after bookmark exists within an element		 * which has been closed before the current cursor; in other words, it has already been removed		 * from the stack of open elements. This means that it's insufficient to simply pop off elements		 * from the stack of open elements which appear after the bookmarked location and then jump to		 * that location, as the elements which were open before won't be re-opened.		 *		 * In order to maintain consistency, the HTML Processor rewinds to the start of the document		 * and reparses everything until it finds the sought-after bookmark.		 *		 * There are potentially better ways to do this: cache the parser state for each bookmark and		 * restore it when seeking; store an immutable and idempotent register of where elements open		 * and close.		 *		 * If caching the parser state it will be essential to properly maintain the cached stack of		 * open elements and active formatting elements when modifying the document. This could be a		 * tedious and time-consuming process as well, and so for now will not be performed.		 *		 * It may be possible to track bookmarks for where elements open and close, and in doing so		 * be able to quickly recalculate breadcrumbs for any element in the document. It may even		 * be possible to remove the stack of open elements and compute it on the fly this way.		 * If doing this, the parser would need to track the opening and closing locations for all		 * tokens in the breadcrumb path for any and all bookmarks. By utilizing bookmarks themselves		 * this list could be automatically maintained while modifying the document. Finding the		 * breadcrumbs would then amount to traversing that list from the start until the token		 * being inspected. Once an element closes, if there are no bookmarks pointing to locations		 * within that element, then all of these locations may be forgotten to save on memory use		 * and computation time.		 */		if ( 'backward' === $direction ) { 			/*			 * When moving backward, stateful stacks should be cleared.			 */			foreach ( $this->state->stack_of_open_elements->walk_up() as $item ) {				$this->state->stack_of_open_elements->remove_node( $item );			} 			foreach ( $this->state->active_formatting_elements->walk_up() as $item ) {				$this->state->active_formatting_elements->remove_node( $item );			} 			/*			 * **After** clearing stacks, more processor state can be reset.			 * This must be done after clearing the stack because those stacks generate events that			 * would appear on a subsequent call to `next_token()`.			 */			$this->state->frameset_ok                       = true;			$this->state->stack_of_template_insertion_modes = array();			$this->state->head_element                      = null;			$this->state->form_element                      = null;			$this->state->current_token                     = null;			$this->current_element                          = null;			$this->element_queue                            = array(); 			/*			 * The absence of a context node indicates a full parse.			 * The presence of a context node indicates a fragment parser.			 */			if ( null === $this->context_node ) {				$this->change_parsing_namespace( 'html' );				$this->state->insertion_mode = WP_HTML_Processor_State::INSERTION_MODE_INITIAL;				$this->breadcrumbs           = array(); 				$this->bookmarks['initial'] = new WP_HTML_Span( 0, 0 );				parent::seek( 'initial' );				unset( $this->bookmarks['initial'] );			} else {

Changelog

Introduced in 6.4.0. Unchanged from 6.7.7 through 7.1.0.

  1. 6.7.7
  2. 6.8.8
  3. 6.9.7
  4. 7.0.4
  5. 7.1.0

Signature, return type and hooks compared across 5 parsed releases.

About this page

Parsed data
Generated from the wordpress-develop 7.1.0 tag, from src/wp-includes/html-api/class-wp-html-processor.php, and regenerated for each WordPress release so it tracks the code rather than a snapshot of it.
Corrections
Something wrong on this page? Report it and it gets fixed in the next regeneration.