Skip to content

Commit

Permalink
WP_HTML_Tag_Processor: Add add_lexical_update() method
Browse files Browse the repository at this point in the history
  • Loading branch information
ockham committed Jan 12, 2023
1 parent 4ea8fa6 commit 8ec0306
Showing 1 changed file with 62 additions and 12 deletions.
74 changes: 62 additions & 12 deletions lib/experimental/html/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,18 @@ class WP_HTML_Tag_Processor {
*/
private $lexical_updates = array();

/**
* Attribute replacements to apply to input HTML document.
*
* Unlike more generic lexical updates, attribute updates are stored
* in an associative array, where the keys are (lowercase-normalized)
* attribute names, in order to avoid duplication.
*
* @since 6.2.0
* @var WP_HTML_Text_Replacement[]
*/
private $attribute_updates = array();

/**
* Tracks how many times we've performed a `seek()`
* so that we can prevent accidental infinite loops.
Expand Down Expand Up @@ -1103,7 +1115,8 @@ private function skip_whitespace() {
* @return void
*/
private function after_tag() {
$this->class_name_updates_to_lexical_updates();
$this->class_name_updates_to_attribute_updates();
$this->attribute_updates_to_lexical_updates();
$this->apply_lexical_updates();
$this->tag_name_starts_at = null;
$this->tag_name_length = null;
Expand All @@ -1113,20 +1126,20 @@ private function after_tag() {
}

/**
* Converts class name updates into tag lexical updates
* Converts class name updates into tag attribute updates
* (they are accumulated in different data formats for performance).
*
* This method is only meant to run right before the lexical updates are applied.
* This method is only meant to run right before the attribute updates are applied.
* The behavior in all other cases is undefined.
*
* @return void
* @since 6.2.0
*
* @see $classname_updates
* @see $lexical_updates
* @see $attribute_updates
*/
private function class_name_updates_to_lexical_updates() {
if ( count( $this->classname_updates ) === 0 || isset( $this->lexical_updates['class'] ) ) {
private function class_name_updates_to_attribute_updates() {
if ( count( $this->classname_updates ) === 0 || isset( $this->attribute_updates['class'] ) ) {
$this->classname_updates = array();
return;
}
Expand Down Expand Up @@ -1241,6 +1254,26 @@ private function class_name_updates_to_lexical_updates() {
}
}

/**
* Converts attribute updates into lexical updates.
*
* This method is only meant to run right before the attribute updates are applied.
* The behavior in all other cases is undefined.
*
* @return void
* @since 6.2.0
*
* @see $attribute_updates
* @see $lexical_updates
*/
private function attribute_updates_to_lexical_updates() {
$this->lexical_updates = array_merge(
$this->lexical_updates,
array_values( $this->attribute_updates )
);
$this->attribute_updates = array();
}

/**
* Applies updates to attributes.
*
Expand Down Expand Up @@ -1501,6 +1534,18 @@ public function is_tag_closer() {
return $this->is_closing_tag;
}

/**
* Add a lexical update, i.e. a replacement of HTML at a given position.
*
* @param int $start The start offset of the replacement.
* @param int $end The end offset of the replacement.
* @param string $text The replacement.
* @return void
*/
protected function add_lexical_update( $start, $end, $text ) {
$this->lexical_updates[] = new WP_HTML_Text_Replacement( $start, $end, $text );
}

/**
* Updates or creates a new attribute on the currently matched tag with the value passed.
*
Expand Down Expand Up @@ -1604,8 +1649,8 @@ public function set_attribute( $name, $value ) {
*
* Result: <div id="new"/>
*/
$existing_attribute = $this->attributes[ $comparable_name ];
$this->lexical_updates[ $name ] = new WP_HTML_Text_Replacement(
$existing_attribute = $this->attributes[ $comparable_name ];
$this->attribute_updates[ $name ] = new WP_HTML_Text_Replacement(
$existing_attribute->start,
$existing_attribute->end,
$updated_attribute
Expand All @@ -1622,7 +1667,7 @@ public function set_attribute( $name, $value ) {
*
* Result: <div id="new"/>
*/
$this->lexical_updates[ $comparable_name ] = new WP_HTML_Text_Replacement(
$this->attribute_updates[ $comparable_name ] = new WP_HTML_Text_Replacement(
$this->tag_name_starts_at + $this->tag_name_length,
$this->tag_name_starts_at + $this->tag_name_length,
' ' . $updated_attribute
Expand Down Expand Up @@ -1662,7 +1707,7 @@ public function remove_attribute( $name ) {
*
* Result: <div />
*/
$this->lexical_updates[ $name ] = new WP_HTML_Text_Replacement(
$this->attribute_updates[ $name ] = new WP_HTML_Text_Replacement(
$this->attributes[ $name ]->start,
$this->attributes[ $name ]->end,
''
Expand Down Expand Up @@ -1724,7 +1769,11 @@ public function __toString() {
*/
public function get_updated_html() {
// Short-circuit if there are no new updates to apply.
if ( ! count( $this->classname_updates ) && ! count( $this->lexical_updates ) ) {
if (
! count( $this->classname_updates ) &&
! count( $this->attribute_updates ) &&
! count( $this->lexical_updates )
) {
return $this->updated_html . substr( $this->html, $this->updated_bytes );
}

Expand All @@ -1737,7 +1786,8 @@ public function get_updated_html() {
$updated_html_up_to_current_tag_name_end = $this->updated_html . $delta_between_updated_html_end_and_current_tag_end;

// 1. Apply the attributes updates to the original HTML
$this->class_name_updates_to_lexical_updates();
$this->class_name_updates_to_attribute_updates();
$this->attribute_updates_to_lexical_updates();
$this->apply_lexical_updates();

// 2. Replace the original HTML with the updated HTML
Expand Down

0 comments on commit 8ec0306

Please sign in to comment.