[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/html-api/ -> class-wp-html-open-elements.php (source)

   1  <?php
   2  /**
   3   * HTML API: WP_HTML_Open_Elements class
   4   *
   5   * @package WordPress
   6   * @subpackage HTML-API
   7   * @since 6.4.0
   8   */
   9  
  10  /**
  11   * Core class used by the HTML processor during HTML parsing
  12   * for managing the stack of open elements.
  13   *
  14   * This class is designed for internal use by the HTML processor.
  15   *
  16   * > Initially, the stack of open elements is empty. The stack grows
  17   * > downwards; the topmost node on the stack is the first one added
  18   * > to the stack, and the bottommost node of the stack is the most
  19   * > recently added node in the stack (notwithstanding when the stack
  20   * > is manipulated in a random access fashion as part of the handling
  21   * > for misnested tags).
  22   *
  23   * @since 6.4.0
  24   *
  25   * @access private
  26   * @ignore
  27   *
  28   * @see https://html.spec.whatwg.org/#stack-of-open-elements
  29   * @see WP_HTML_Processor
  30   */
  31  class WP_HTML_Open_Elements {
  32      /**
  33       * Holds the stack of open element references.
  34       *
  35       * @since 6.4.0
  36       *
  37       * @var WP_HTML_Token[]
  38       */
  39      public $stack = array();
  40  
  41      /**
  42       * Whether a P element is in button scope currently.
  43       *
  44       * This class optimizes scope lookup by pre-calculating
  45       * this value when elements are added and removed to the
  46       * stack of open elements which might change its value.
  47       * This avoids frequent iteration over the stack.
  48       *
  49       * @since 6.4.0
  50       *
  51       * @var bool
  52       */
  53      private $has_p_in_button_scope = false;
  54  
  55      /**
  56       * A function that will be called when an item is popped off the stack of open elements.
  57       *
  58       * The function will be called with the popped item as its argument.
  59       *
  60       * @since 6.6.0
  61       *
  62       * @var Closure|null
  63       */
  64      private $pop_handler = null;
  65  
  66      /**
  67       * A function that will be called when an item is pushed onto the stack of open elements.
  68       *
  69       * The function will be called with the pushed item as its argument.
  70       *
  71       * @since 6.6.0
  72       *
  73       * @var Closure|null
  74       */
  75      private $push_handler = null;
  76  
  77      /**
  78       * Sets a pop handler that will be called when an item is popped off the stack of
  79       * open elements.
  80       *
  81       * The function will be called with the popped item as its argument.
  82       *
  83       * @since 6.6.0
  84       *
  85       * @param Closure $handler The handler function.
  86       */
  87  	public function set_pop_handler( Closure $handler ): void {
  88          $this->pop_handler = $handler;
  89      }
  90  
  91      /**
  92       * Sets a push handler that will be called when an item is pushed onto the stack of
  93       * open elements.
  94       *
  95       * The function will be called with the pushed item as its argument.
  96       *
  97       * @since 6.6.0
  98       *
  99       * @param Closure $handler The handler function.
 100       */
 101  	public function set_push_handler( Closure $handler ): void {
 102          $this->push_handler = $handler;
 103      }
 104  
 105      /**
 106       * Returns the node at the nth position on the stack
 107       * of open elements, or `null` if no such position exists.
 108       *
 109       * Note that this uses a 1-based index, which represents the
 110       * "nth item" on the stack, counting from the top, where the
 111       * top-most element is the 1st, the second is the 2nd, etc...
 112       *
 113       * @since 6.7.0
 114       *
 115       * @param int $nth Retrieve the nth item on the stack, with 1 being
 116       *                 the top element, 2 being the second, etc...
 117       * @return WP_HTML_Token|null The node on the stack at the given location,
 118       *                            or `null` if the location isn't on the stack.
 119       */
 120      public function at( int $nth ): ?WP_HTML_Token {
 121          foreach ( $this->walk_down() as $item ) {
 122              if ( 0 === --$nth ) {
 123                  return $item;
 124              }
 125          }
 126  
 127          return null;
 128      }
 129  
 130      /**
 131       * Reports if a node of a given name is in the stack of open elements.
 132       *
 133       * @since 6.7.0
 134       *
 135       * @param string $node_name Name of node for which to check.
 136       * @return bool Whether a node of the given name is in the stack of open elements.
 137       */
 138  	public function contains( string $node_name ): bool {
 139          foreach ( $this->walk_up() as $item ) {
 140              if ( $node_name === $item->node_name ) {
 141                  return true;
 142              }
 143          }
 144  
 145          return false;
 146      }
 147  
 148      /**
 149       * Reports if a specific node is in the stack of open elements.
 150       *
 151       * @since 6.4.0
 152       *
 153       * @param WP_HTML_Token $token Look for this node in the stack.
 154       * @return bool Whether the referenced node is in the stack of open elements.
 155       */
 156  	public function contains_node( WP_HTML_Token $token ): bool {
 157          foreach ( $this->walk_up() as $item ) {
 158              if ( $token === $item ) {
 159                  return true;
 160              }
 161          }
 162  
 163          return false;
 164      }
 165  
 166      /**
 167       * Returns how many nodes are currently in the stack of open elements.
 168       *
 169       * @since 6.4.0
 170       *
 171       * @return int How many nodes are in the stack of open elements.
 172       */
 173  	public function count(): int {
 174          return count( $this->stack );
 175      }
 176  
 177      /**
 178       * Returns the node at the end of the stack of open elements,
 179       * if one exists. If the stack is empty, returns null.
 180       *
 181       * @since 6.4.0
 182       *
 183       * @return WP_HTML_Token|null Last node in the stack of open elements, if one exists, otherwise null.
 184       */
 185  	public function current_node(): ?WP_HTML_Token {
 186          $current_node = end( $this->stack );
 187  
 188          return $current_node ? $current_node : null;
 189      }
 190  
 191      /**
 192       * Indicates if the current node is of a given type or name.
 193       *
 194       * It's possible to pass either a node type or a node name to this function.
 195       * In the case there is no current element it will always return `false`.
 196       *
 197       * Example:
 198       *
 199       *     // Is the current node a text node?
 200       *     $stack->current_node_is( '#text' );
 201       *
 202       *     // Is the current node a DIV element?
 203       *     $stack->current_node_is( 'DIV' );
 204       *
 205       *     // Is the current node any element/tag?
 206       *     $stack->current_node_is( '#tag' );
 207       *
 208       * @see WP_HTML_Tag_Processor::get_token_type
 209       * @see WP_HTML_Tag_Processor::get_token_name
 210       *
 211       * @since 6.7.0
 212       *
 213       * @access private
 214       *
 215       * @param string $identity Check if the current node has this name or type (depending on what is provided).
 216       * @return bool Whether there is a current element that matches the given identity, whether a token name or type.
 217       */
 218  	public function current_node_is( string $identity ): bool {
 219          $current_node = end( $this->stack );
 220          if ( false === $current_node ) {
 221              return false;
 222          }
 223  
 224          $current_node_name = $current_node->node_name;
 225  
 226          return (
 227              $current_node_name === $identity ||
 228              ( '#doctype' === $identity && 'html' === $current_node_name ) ||
 229              ( '#tag' === $identity && ctype_upper( $current_node_name ) )
 230          );
 231      }
 232  
 233      /**
 234       * Returns whether an element is in a specific scope.
 235       *
 236       * @since 6.4.0
 237       *
 238       * @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope
 239       *
 240       * @param string   $tag_name         Name of tag check.
 241       * @param string[] $termination_list List of elements that terminate the search.
 242       * @return bool Whether the element was found in a specific scope.
 243       */
 244  	public function has_element_in_specific_scope( string $tag_name, $termination_list ): bool {
 245          foreach ( $this->walk_up() as $node ) {
 246              $namespaced_name = 'html' === $node->namespace
 247                  ? $node->node_name
 248                  : "{$node->namespace} {$node->node_name}";
 249  
 250              if ( $namespaced_name === $tag_name ) {
 251                  return true;
 252              }
 253  
 254              if (
 255                  '(internal: H1 through H6 - do not use)' === $tag_name &&
 256                  in_array( $namespaced_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
 257              ) {
 258                  return true;
 259              }
 260  
 261              if ( in_array( $namespaced_name, $termination_list, true ) ) {
 262                  return false;
 263              }
 264          }
 265  
 266          return false;
 267      }
 268  
 269      /**
 270       * Returns whether a particular element is in scope.
 271       *
 272       * > The stack of open elements is said to have a particular element in
 273       * > scope when it has that element in the specific scope consisting of
 274       * > the following element types:
 275       * >
 276       * >   - applet
 277       * >   - caption
 278       * >   - html
 279       * >   - table
 280       * >   - td
 281       * >   - th
 282       * >   - marquee
 283       * >   - object
 284       * >   - template
 285       * >   - MathML mi
 286       * >   - MathML mo
 287       * >   - MathML mn
 288       * >   - MathML ms
 289       * >   - MathML mtext
 290       * >   - MathML annotation-xml
 291       * >   - SVG foreignObject
 292       * >   - SVG desc
 293       * >   - SVG title
 294       *
 295       * @since 6.4.0
 296       * @since 6.7.0 Full support.
 297       *
 298       * @see https://html.spec.whatwg.org/#has-an-element-in-scope
 299       *
 300       * @param string $tag_name Name of tag to check.
 301       * @return bool Whether given element is in scope.
 302       */
 303  	public function has_element_in_scope( string $tag_name ): bool {
 304          return $this->has_element_in_specific_scope(
 305              $tag_name,
 306              array(
 307                  'APPLET',
 308                  'CAPTION',
 309                  'HTML',
 310                  'TABLE',
 311                  'TD',
 312                  'TH',
 313                  'MARQUEE',
 314                  'OBJECT',
 315                  'TEMPLATE',
 316  
 317                  'math MI',
 318                  'math MO',
 319                  'math MN',
 320                  'math MS',
 321                  'math MTEXT',
 322                  'math ANNOTATION-XML',
 323  
 324                  'svg FOREIGNOBJECT',
 325                  'svg DESC',
 326                  'svg TITLE',
 327              )
 328          );
 329      }
 330  
 331      /**
 332       * Returns whether a particular element is in list item scope.
 333       *
 334       * > The stack of open elements is said to have a particular element
 335       * > in list item scope when it has that element in the specific scope
 336       * > consisting of the following element types:
 337       * >
 338       * >   - All the element types listed above for the has an element in scope algorithm.
 339       * >   - ol in the HTML namespace
 340       * >   - ul in the HTML namespace
 341       *
 342       * @since 6.4.0
 343       * @since 6.5.0 Implemented: no longer throws on every invocation.
 344       * @since 6.7.0 Supports all required HTML elements.
 345       *
 346       * @see https://html.spec.whatwg.org/#has-an-element-in-list-item-scope
 347       *
 348       * @param string $tag_name Name of tag to check.
 349       * @return bool Whether given element is in scope.
 350       */
 351  	public function has_element_in_list_item_scope( string $tag_name ): bool {
 352          return $this->has_element_in_specific_scope(
 353              $tag_name,
 354              array(
 355                  'APPLET',
 356                  'BUTTON',
 357                  'CAPTION',
 358                  'HTML',
 359                  'TABLE',
 360                  'TD',
 361                  'TH',
 362                  'MARQUEE',
 363                  'OBJECT',
 364                  'OL',
 365                  'TEMPLATE',
 366                  'UL',
 367  
 368                  'math MI',
 369                  'math MO',
 370                  'math MN',
 371                  'math MS',
 372                  'math MTEXT',
 373                  'math ANNOTATION-XML',
 374  
 375                  'svg FOREIGNOBJECT',
 376                  'svg DESC',
 377                  'svg TITLE',
 378              )
 379          );
 380      }
 381  
 382      /**
 383       * Returns whether a particular element is in button scope.
 384       *
 385       * > The stack of open elements is said to have a particular element
 386       * > in button scope when it has that element in the specific scope
 387       * > consisting of the following element types:
 388       * >
 389       * >   - All the element types listed above for the has an element in scope algorithm.
 390       * >   - button in the HTML namespace
 391       *
 392       * @since 6.4.0
 393       * @since 6.7.0 Supports all required HTML elements.
 394       *
 395       * @see https://html.spec.whatwg.org/#has-an-element-in-button-scope
 396       *
 397       * @param string $tag_name Name of tag to check.
 398       * @return bool Whether given element is in scope.
 399       */
 400  	public function has_element_in_button_scope( string $tag_name ): bool {
 401          return $this->has_element_in_specific_scope(
 402              $tag_name,
 403              array(
 404                  'APPLET',
 405                  'BUTTON',
 406                  'CAPTION',
 407                  'HTML',
 408                  'TABLE',
 409                  'TD',
 410                  'TH',
 411                  'MARQUEE',
 412                  'OBJECT',
 413                  'TEMPLATE',
 414  
 415                  'math MI',
 416                  'math MO',
 417                  'math MN',
 418                  'math MS',
 419                  'math MTEXT',
 420                  'math ANNOTATION-XML',
 421  
 422                  'svg FOREIGNOBJECT',
 423                  'svg DESC',
 424                  'svg TITLE',
 425              )
 426          );
 427      }
 428  
 429      /**
 430       * Returns whether a particular element is in table scope.
 431       *
 432       * > The stack of open elements is said to have a particular element
 433       * > in table scope when it has that element in the specific scope
 434       * > consisting of the following element types:
 435       * >
 436       * >   - html in the HTML namespace
 437       * >   - table in the HTML namespace
 438       * >   - template in the HTML namespace
 439       *
 440       * @since 6.4.0
 441       * @since 6.7.0 Full implementation.
 442       *
 443       * @see https://html.spec.whatwg.org/#has-an-element-in-table-scope
 444       *
 445       * @param string $tag_name Name of tag to check.
 446       * @return bool Whether given element is in scope.
 447       */
 448  	public function has_element_in_table_scope( string $tag_name ): bool {
 449          return $this->has_element_in_specific_scope(
 450              $tag_name,
 451              array(
 452                  'HTML',
 453                  'TABLE',
 454                  'TEMPLATE',
 455              )
 456          );
 457      }
 458  
 459      /**
 460       * Returns whether a particular element is in select scope.
 461       *
 462       * This test differs from the others like it, in that its rules are inverted.
 463       * Instead of arriving at a match when one of any tag in a termination group
 464       * is reached, this one terminates if any other tag is reached.
 465       *
 466       * > The stack of open elements is said to have a particular element in select scope when it has
 467       * > that element in the specific scope consisting of all element types except the following:
 468       * >   - optgroup in the HTML namespace
 469       * >   - option in the HTML namespace
 470       *
 471       * @since 6.4.0 Stub implementation (throws).
 472       * @since 6.7.0 Full implementation.
 473       *
 474       * @see https://html.spec.whatwg.org/#has-an-element-in-select-scope
 475       *
 476       * @param string $tag_name Name of tag to check.
 477       * @return bool Whether the given element is in SELECT scope.
 478       */
 479  	public function has_element_in_select_scope( string $tag_name ): bool {
 480          foreach ( $this->walk_up() as $node ) {
 481              if ( $node->node_name === $tag_name ) {
 482                  return true;
 483              }
 484  
 485              if (
 486                  'OPTION' !== $node->node_name &&
 487                  'OPTGROUP' !== $node->node_name
 488              ) {
 489                  return false;
 490              }
 491          }
 492  
 493          return false;
 494      }
 495  
 496      /**
 497       * Returns whether a P is in BUTTON scope.
 498       *
 499       * @since 6.4.0
 500       *
 501       * @see https://html.spec.whatwg.org/#has-an-element-in-button-scope
 502       *
 503       * @return bool Whether a P is in BUTTON scope.
 504       */
 505  	public function has_p_in_button_scope(): bool {
 506          return $this->has_p_in_button_scope;
 507      }
 508  
 509      /**
 510       * Pops a node off of the stack of open elements.
 511       *
 512       * @since 6.4.0
 513       *
 514       * @see https://html.spec.whatwg.org/#stack-of-open-elements
 515       *
 516       * @return bool Whether a node was popped off of the stack.
 517       */
 518  	public function pop(): bool {
 519          $item = array_pop( $this->stack );
 520          if ( null === $item ) {
 521              return false;
 522          }
 523  
 524          $this->after_element_pop( $item );
 525          return true;
 526      }
 527  
 528      /**
 529       * Pops nodes off of the stack of open elements until an HTML tag with the given name has been popped.
 530       *
 531       * @since 6.4.0
 532       *
 533       * @see WP_HTML_Open_Elements::pop
 534       *
 535       * @param string $html_tag_name Name of tag that needs to be popped off of the stack of open elements.
 536       * @return bool Whether a tag of the given name was found and popped off of the stack of open elements.
 537       */
 538  	public function pop_until( string $html_tag_name ): bool {
 539          foreach ( $this->walk_up() as $item ) {
 540              $this->pop();
 541  
 542              if ( 'html' !== $item->namespace ) {
 543                  continue;
 544              }
 545  
 546              if (
 547                  '(internal: H1 through H6 - do not use)' === $html_tag_name &&
 548                  in_array( $item->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
 549              ) {
 550                  return true;
 551              }
 552  
 553              if ( $html_tag_name === $item->node_name ) {
 554                  return true;
 555              }
 556          }
 557  
 558          return false;
 559      }
 560  
 561      /**
 562       * Pushes a node onto the stack of open elements.
 563       *
 564       * @since 6.4.0
 565       *
 566       * @see https://html.spec.whatwg.org/#stack-of-open-elements
 567       *
 568       * @param WP_HTML_Token $stack_item Item to add onto stack.
 569       */
 570  	public function push( WP_HTML_Token $stack_item ): void {
 571          $this->stack[] = $stack_item;
 572          $this->after_element_push( $stack_item );
 573      }
 574  
 575      /**
 576       * Removes a specific node from the stack of open elements.
 577       *
 578       * @since 6.4.0
 579       *
 580       * @param WP_HTML_Token $token The node to remove from the stack of open elements.
 581       * @return bool Whether the node was found and removed from the stack of open elements.
 582       */
 583  	public function remove_node( WP_HTML_Token $token ): bool {
 584          foreach ( $this->walk_up() as $position_from_end => $item ) {
 585              if ( $token->bookmark_name !== $item->bookmark_name ) {
 586                  continue;
 587              }
 588  
 589              $position_from_start = $this->count() - $position_from_end - 1;
 590              array_splice( $this->stack, $position_from_start, 1 );
 591              $this->after_element_pop( $item );
 592              return true;
 593          }
 594  
 595          return false;
 596      }
 597  
 598  
 599      /**
 600       * Steps through the stack of open elements, starting with the top element
 601       * (added first) and walking downwards to the one added last.
 602       *
 603       * This generator function is designed to be used inside a "foreach" loop.
 604       *
 605       * Example:
 606       *
 607       *     $html = '<em><strong><a>We are here';
 608       *     foreach ( $stack->walk_down() as $node ) {
 609       *         echo "{$node->node_name} -> ";
 610       *     }
 611       *     > EM -> STRONG -> A ->
 612       *
 613       * To start with the most-recently added element and walk towards the top,
 614       * see WP_HTML_Open_Elements::walk_up().
 615       *
 616       * @since 6.4.0
 617       */
 618  	public function walk_down() {
 619          $count = count( $this->stack );
 620  
 621          for ( $i = 0; $i < $count; $i++ ) {
 622              yield $this->stack[ $i ];
 623          }
 624      }
 625  
 626      /**
 627       * Steps through the stack of open elements, starting with the bottom element
 628       * (added last) and walking upwards to the one added first.
 629       *
 630       * This generator function is designed to be used inside a "foreach" loop.
 631       *
 632       * Example:
 633       *
 634       *     $html = '<em><strong><a>We are here';
 635       *     foreach ( $stack->walk_up() as $node ) {
 636       *         echo "{$node->node_name} -> ";
 637       *     }
 638       *     > A -> STRONG -> EM ->
 639       *
 640       * To start with the first added element and walk towards the bottom,
 641       * see WP_HTML_Open_Elements::walk_down().
 642       *
 643       * @since 6.4.0
 644       * @since 6.5.0 Accepts $above_this_node to start traversal above a given node, if it exists.
 645       *
 646       * @param WP_HTML_Token|null $above_this_node Optional. Start traversing above this node,
 647       *                                            if provided and if the node exists.
 648       */
 649  	public function walk_up( ?WP_HTML_Token $above_this_node = null ) {
 650          $has_found_node = null === $above_this_node;
 651  
 652          for ( $i = count( $this->stack ) - 1; $i >= 0; $i-- ) {
 653              $node = $this->stack[ $i ];
 654  
 655              if ( ! $has_found_node ) {
 656                  $has_found_node = $node === $above_this_node;
 657                  continue;
 658              }
 659  
 660              yield $node;
 661          }
 662      }
 663  
 664      /*
 665       * Internal helpers.
 666       */
 667  
 668      /**
 669       * Updates internal flags after adding an element.
 670       *
 671       * Certain conditions (such as "has_p_in_button_scope") are maintained here as
 672       * flags that are only modified when adding and removing elements. This allows
 673       * the HTML Processor to quickly check for these conditions instead of iterating
 674       * over the open stack elements upon each new tag it encounters. These flags,
 675       * however, need to be maintained as items are added and removed from the stack.
 676       *
 677       * @since 6.4.0
 678       *
 679       * @param WP_HTML_Token $item Element that was added to the stack of open elements.
 680       */
 681  	public function after_element_push( WP_HTML_Token $item ): void {
 682          $namespaced_name = 'html' === $item->namespace
 683              ? $item->node_name
 684              : "{$item->namespace} {$item->node_name}";
 685  
 686          /*
 687           * When adding support for new elements, expand this switch to trap
 688           * cases where the precalculated value needs to change.
 689           */
 690          switch ( $namespaced_name ) {
 691              case 'APPLET':
 692              case 'BUTTON':
 693              case 'CAPTION':
 694              case 'HTML':
 695              case 'TABLE':
 696              case 'TD':
 697              case 'TH':
 698              case 'MARQUEE':
 699              case 'OBJECT':
 700              case 'TEMPLATE':
 701              case 'math MI':
 702              case 'math MO':
 703              case 'math MN':
 704              case 'math MS':
 705              case 'math MTEXT':
 706              case 'math ANNOTATION-XML':
 707              case 'svg FOREIGNOBJECT':
 708              case 'svg DESC':
 709              case 'svg TITLE':
 710                  $this->has_p_in_button_scope = false;
 711                  break;
 712  
 713              case 'P':
 714                  $this->has_p_in_button_scope = true;
 715                  break;
 716          }
 717  
 718          if ( null !== $this->push_handler ) {
 719              call_user_func( $this->push_handler, $item );
 720          }
 721      }
 722  
 723      /**
 724       * Updates internal flags after removing an element.
 725       *
 726       * Certain conditions (such as "has_p_in_button_scope") are maintained here as
 727       * flags that are only modified when adding and removing elements. This allows
 728       * the HTML Processor to quickly check for these conditions instead of iterating
 729       * over the open stack elements upon each new tag it encounters. These flags,
 730       * however, need to be maintained as items are added and removed from the stack.
 731       *
 732       * @since 6.4.0
 733       *
 734       * @param WP_HTML_Token $item Element that was removed from the stack of open elements.
 735       */
 736  	public function after_element_pop( WP_HTML_Token $item ): void {
 737          /*
 738           * When adding support for new elements, expand this switch to trap
 739           * cases where the precalculated value needs to change.
 740           */
 741          $namespaced_name = 'html' === $item->namespace
 742              ? $item->node_name
 743              : "{$item->namespace} {$item->node_name}";
 744  
 745          switch ( $namespaced_name ) {
 746              case 'APPLET':
 747              case 'BUTTON':
 748              case 'CAPTION':
 749              case 'HTML':
 750              case 'P':
 751              case 'TABLE':
 752              case 'TD':
 753              case 'TH':
 754              case 'MARQUEE':
 755              case 'OBJECT':
 756              case 'TEMPLATE':
 757              case 'math MI':
 758              case 'math MO':
 759              case 'math MN':
 760              case 'math MS':
 761              case 'math MTEXT':
 762              case 'math ANNOTATION-XML':
 763              case 'svg FOREIGNOBJECT':
 764              case 'svg DESC':
 765              case 'svg TITLE':
 766                  $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' );
 767                  break;
 768          }
 769  
 770          if ( null !== $this->pop_handler ) {
 771              call_user_func( $this->pop_handler, $item );
 772          }
 773      }
 774  
 775      /**
 776       * Clear the stack back to a table context.
 777       *
 778       * > When the steps above require the UA to clear the stack back to a table context, it means
 779       * > that the UA must, while the current node is not a table, template, or html element, pop
 780       * > elements from the stack of open elements.
 781       *
 782       * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-stack-back-to-a-table-context
 783       *
 784       * @since 6.7.0
 785       */
 786  	public function clear_to_table_context(): void {
 787          foreach ( $this->walk_up() as $item ) {
 788              if (
 789                  'TABLE' === $item->node_name ||
 790                  'TEMPLATE' === $item->node_name ||
 791                  'HTML' === $item->node_name
 792              ) {
 793                  break;
 794              }
 795              $this->pop();
 796          }
 797      }
 798  
 799      /**
 800       * Clear the stack back to a table body context.
 801       *
 802       * > When the steps above require the UA to clear the stack back to a table body context, it
 803       * > means that the UA must, while the current node is not a tbody, tfoot, thead, template, or
 804       * > html element, pop elements from the stack of open elements.
 805       *
 806       * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-stack-back-to-a-table-body-context
 807       *
 808       * @since 6.7.0
 809       */
 810  	public function clear_to_table_body_context(): void {
 811          foreach ( $this->walk_up() as $item ) {
 812              if (
 813                  'TBODY' === $item->node_name ||
 814                  'TFOOT' === $item->node_name ||
 815                  'THEAD' === $item->node_name ||
 816                  'TEMPLATE' === $item->node_name ||
 817                  'HTML' === $item->node_name
 818              ) {
 819                  break;
 820              }
 821              $this->pop();
 822          }
 823      }
 824  
 825      /**
 826       * Clear the stack back to a table row context.
 827       *
 828       * > When the steps above require the UA to clear the stack back to a table row context, it
 829       * > means that the UA must, while the current node is not a tr, template, or html element, pop
 830       * > elements from the stack of open elements.
 831       *
 832       * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-stack-back-to-a-table-row-context
 833       *
 834       * @since 6.7.0
 835       */
 836  	public function clear_to_table_row_context(): void {
 837          foreach ( $this->walk_up() as $item ) {
 838              if (
 839                  'TR' === $item->node_name ||
 840                  'TEMPLATE' === $item->node_name ||
 841                  'HTML' === $item->node_name
 842              ) {
 843                  break;
 844              }
 845              $this->pop();
 846          }
 847      }
 848  
 849      /**
 850       * Wakeup magic method.
 851       *
 852       * @since 6.6.0
 853       */
 854  	public function __wakeup() {
 855          throw new \LogicException( __CLASS__ . ' should never be unserialized' );
 856      }
 857  }


Generated : Sat Jun 27 08:20:12 2026 Cross-referenced by PHPXref