//html.spec.whatwg.org/#has-an-element-in-select-scope * * @param string $tag_name Name of tag to check. * @return bool Whether the given element is in SELECT scope. */ public function has_element_in_select_scope( string $tag_name ): bool { foreach ( $this->walk_up() as $node ) { if ( $node->node_name === $tag_name ) { return true; } if ( 'OPTION' !== $node->node_name && 'OPTGROUP' !== $node->node_name ) { return false; } } return false; } /** * Returns whether a P is in BUTTON scope. * * @since 6.4.0 * * @see https://html.spec.whatwg.org/#has-an-element-in-button-scope * * @return bool Whether a P is in BUTTON scope. */ public function has_p_in_button_scope(): bool { return $this->has_p_in_button_scope; } /** * Pops a node off of the stack of open elements. * * @since 6.4.0 * * @see https://html.spec.whatwg.org/#stack-of-open-elements * * @return bool Whether a node was popped off of the stack. */ public function pop(): bool { $item = array_pop( $this->stack ); if ( null === $item ) { return false; } if ( 'context-node' === $item->bookmark_name ) { $this->stack[] = $item; return false; } $this->after_element_pop( $item ); return true; } /** * Pops nodes off of the stack of open elements until an HTML tag with the given name has been popped. * * @since 6.4.0 * * @see WP_HTML_Open_Elements::pop * * @param string $html_tag_name Name of tag that needs to be popped off of the stack of open elements. * @return bool Whether a tag of the given name was found and popped off of the stack of open elements. */ public function pop_until( string $html_tag_name ): bool { foreach ( $this->walk_up() as $item ) { $this->pop(); if ( 'html' !== $item->namespace ) { continue; } if ( '(internal: H1 through H6 - do not use)' === $html_tag_name && in_array( $item->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true ) ) { return true; } if ( $html_tag_name === $item->node_name ) { return true; } } return false; } /** * Pushes a node onto the stack of open elements. * * @since 6.4.0 * * @see https://html.spec.whatwg.org/#stack-of-open-elements * * @param WP_HTML_Token $stack_item Item to add onto stack. */ public function push( WP_HTML_Token $stack_item ): void { $this->stack[] = $stack_item; $this->after_element_push( $stack_item ); } /** * Removes a specific node from the stack of open elements. * * @since 6.4.0 * * @param WP_HTML_Token $token The node to remove from the stack of open elements. * @return bool Whether the node was found and removed from the stack of open elements. */ public function remove_node( WP_HTML_Token $token ): bool { if ( 'context-node' === $token->bookmark_name ) { return false; } foreach ( $this->walk_up() as $position_from_end => $item ) { if ( $token->bookmark_name !== $item->bookmark_name ) { continue; } $position_from_start = $this->count() - $position_from_end - 1; array_splice( $this->stack, $position_from_start, 1 ); $this->after_element_pop( $item ); return true; } return false; } /** * Steps through the stack of open elements, starting with the top element * (added first) and walking downwards to the one added last. * * This generator function is designed to be used inside a "foreach" loop. * * Example: * * $html = 'We are here'; * foreach ( $stack->walk_down() as $node ) { * echo "{$node->node_name} -> "; * } * > EM -> STRONG -> A -> * * To start with the most-recently added element and walk towards the top, * see WP_HTML_Open_Elements::walk_up(). * * @since 6.4.0 */ public function walk_down() { $count = count( $this->stack ); for ( $i = 0; $i < $count; $i++ ) { yield $this->stack[ $i ]; } } /** * Steps through the stack of open elements, starting with the bottom element * (added last) and walking upwards to the one added first. * * This generator function is designed to be used inside a "foreach" loop. * * Example: * * $html = 'We are here'; * foreach ( $stack->walk_up() as $node ) { * echo "{$node->node_name} -> "; * } * > A -> STRONG -> EM -> * * To start with the first added element and walk towards the bottom, * see WP_HTML_Open_Elements::walk_down(). * * @since 6.4.0 * @since 6.5.0 Accepts $above_this_node to start traversal above a given node, if it exists. * * @param WP_HTML_Token|null $above_this_node Optional. Start traversing above this node, * if provided and if the node exists. */ public function walk_up( ?WP_HTML_Token $above_this_node = null ) { $has_found_node = null === $above_this_node; for ( $i = count( $this->stack ) - 1; $i >= 0; $i-- ) { $node = $this->stack[ $i ]; if ( ! $has_found_node ) { $has_found_node = $node === $above_this_node; continue; } yield $node; } } /* * Internal helpers. */ /** * Updates internal flags after adding an element. * * Certain conditions (such as "has_p_in_button_scope") are maintained here as * flags that are only modified when adding and removing elements. This allows * the HTML Processor to quickly check for these conditions instead of iterating * over the open stack elements upon each new tag it encounters. These flags, * however, need to be maintained as items are added and removed from the stack. * * @since 6.4.0 * * @param WP_HTML_Token $item Element that was added to the stack of open elements. */ public function after_element_push( WP_HTML_Token $item ): void { $namespaced_name = 'html' === $item->namespace ? $item->node_name : "{$item->namespace} {$item->node_name}"; /* * When adding support for new elements, expand this switch to trap * cases where the precalculated value needs to change. */ switch ( $namespaced_name ) { case 'APPLET': case 'BUTTON': case 'CAPTION': case 'HTML': case 'TABLE': case 'TD': case 'TH': case 'MARQUEE': case 'OBJECT': case 'TEMPLATE': case 'math MI': case 'math MO': case 'math MN': case 'math MS': case 'math MTEXT': case 'math ANNOTATION-XML': case 'svg FOREIGNOBJECT': case 'svg DESC': case 'svg TITLE': $this->has_p_in_button_scope = false; break; case 'P': $this->has_p_in_button_scope = true; break; } if ( null !== $this->push_handler ) { ( $this->push_handler )( $item ); } } /** * Updates internal flags after removing an element. * * Certain conditions (such as "has_p_in_button_scope") are maintained here as * flags that are only modified when adding and removing elements. This allows * the HTML Processor to quickly check for these conditions instead of iterating * over the open stack elements upon each new tag it encounters. These flags, * however, need to be maintained as items are added and removed from the stack. * * @since 6.4.0 * * @param WP_HTML_Token $item Element that was removed from the stack of open elements. */ public function after_element_pop( WP_HTML_Token $item ): void { /* * When adding support for new elements, expand this switch to trap * cases where the precalculated value needs to change. */ switch ( $item->node_name ) { case 'APPLET': case 'BUTTON': case 'CAPTION': case 'HTML': case 'P': case 'TABLE': case 'TD': case 'TH': case 'MARQUEE': case 'OBJECT': case 'TEMPLATE': case 'math MI': case 'math MO': case 'math MN': case 'math MS': case 'math MTEXT': case 'math ANNOTATION-XML': case 'svg FOREIGNOBJECT': case 'svg DESC': case 'svg TITLE': $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' ); break; } if ( null !== $this->pop_handler ) { ( $this->pop_handler )( $item ); } } /** * Clear the stack back to a table context. * * > When the steps above require the UA to clear the stack back to a table context, it means * > that the UA must, while the current node is not a table, template, or html element, pop * > elements from the stack of open elements. * * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-stack-back-to-a-table-context * * @since 6.7.0 */ public function clear_to_table_context(): void { foreach ( $this->walk_up() as $item ) { if ( 'TABLE' === $item->node_name || 'TEMPLATE' === $item->node_name || 'HTML' === $item->node_name ) { break; } $this->pop(); } } /** * Clear the stack back to a table body context. * * > When the steps above require the UA to clear the stack back to a table body context, it * > means that the UA must, while the current node is not a tbody, tfoot, thead, template, or * > html element, pop elements from the stack of open elements. * * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-stack-back-to-a-table-body-context * * @since 6.7.0 */ public function clear_to_table_body_context(): void { foreach ( $this->walk_up() as $item ) { if ( 'TBODY' === $item->node_name || 'TFOOT' === $item->node_name || 'THEAD' === $item->node_name || 'TEMPLATE' === $item->node_name || 'HTML' === $item->node_name ) { break; } $this->pop(); } } /** * Clear the stack back to a table row context. * * > When the steps above require the UA to clear the stack back to a table row context, it * > means that the UA must, while the current node is not a tr, template, or html element, pop * > elements from the stack of open elements. * * @see https://html.spec.whatwg.org/multipage/parsing.html#clear-the-stack-back-to-a-table-row-context * * @since 6.7.0 */ public function clear_to_table_row_context(): void { foreach ( $this->walk_up() as $item ) { if ( 'TR' === $item->node_name || 'TEMPLATE' === $item->node_name || 'HTML' === $item->node_name ) { break; } $this->pop(); } } /** * Wakeup magic method. * * @since 6.6.0 */ public function __wakeup() { throw new \LogicException( __CLASS__ . ' should never be unserialized' ); } } er])) { /* * Integrators who cannot yet upgrade to the PSR-4 class names can silence deprecations * by defining a `REQUESTS_SILENCE_PSR0_DEPRECATIONS` constant and setting it to `true`. * The constant needs to be defined before the first deprecated class is requested * via this autoloader. */ if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS') || REQUESTS_SILENCE_PSR0_DEPRECATIONS !== true) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error trigger_error( 'The PSR-0 `Requests_...` class names in the Requests library are deprecated.' . ' Switch to the PSR-4 `WpOrg\Requests\...` class names at your earliest convenience.', E_USER_DEPRECATED ); // Prevent the deprecation notice from being thrown twice. if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) { define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true); } } // Create an alias and let the autoloader recursively kick in to load the PSR-4 class. return class_alias(self::$deprecated_classes[$class_lower], $class_name, true); } return false; } } }