Make WordPress Core


Ignore:
Timestamp:
09/26/2023 08:18:25 AM (3 years ago)
Author:
Bernhard Reiter
Message:

HTML API: Add matches_breadcrumbs() method for better querying.

Inside a next_tag() loop it can be challenging to use breadcrumbs because they are only exposed inside the call to next_tag() via the $query arg.

In this patch a new method, matches_breadcrumbs(), is exposed which allows for querying within the next_tag() loop for more complicated queries.

This method exposes a wildcard * operator to allow matching any HTML tag that the currently-matched tag is a child or descendant of.

Props dmsnell, westonruter, mukesh27.
Fixes #59400.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/html-api/class-wp-html-processor.php

    r56565 r56702  
    358358         *     @type string|null $class_name   Tag must contain this whole class name to match.
    359359         *     @type string[]    $breadcrumbs  DOM sub-path at which element is found, e.g. `array( 'FIGURE', 'IMG' )`.
     360         *                                     May also contain the wildcard `*` which matches a single element, e.g. `array( 'SECTION', '*' )`.
    360361         * }
    361362         * @return bool Whether a tag was matched.
    … …  
    407408                $match_offset = isset( $query['match_offset'] ) ? (int) $query['match_offset'] : 1;
    408409
    409                 $crumb  = end( $breadcrumbs );
    410                 $target = strtoupper( $crumb );
    411410                while ( $match_offset > 0 && $this->step() ) {
    412                         if ( $target !== $this->get_tag() ) {
    413                                 continue;
     411                        if ( $this->matches_breadcrumbs( $breadcrumbs ) && 0 === --$match_offset ) {
     412                                return true;
    414413                        }
    415 
    416                         // Look up the stack to see if the breadcrumbs match.
    417                         foreach ( $this->state->stack_of_open_elements->walk_up() as $node ) {
    418                                 if ( strtoupper( $crumb ) !== $node->node_name ) {
    419                                         break;
    420                                 }
    421 
    422                                 $crumb = prev( $breadcrumbs );
    423                                 if ( false === $crumb && 0 === --$match_offset && ! $this->is_tag_closer() ) {
    424                                         return true;
    425                                 }
     414                }
     415
     416                return false;
     417        }
     418
     419        /**
     420         * Indicates if the currently-matched tag matches the given breadcrumbs.
     421         *
     422         * A "*" represents a single tag wildcard, where any tag matches, but not no tags.
     423         *
     424         * At some point this function _may_ support a `**` syntax for matching any number
     425         * of unspecified tags in the breadcrumb stack. This has been intentionally left
     426         * out, however, to keep this function simple and to avoid introducing backtracking,
     427         * which could open up surprising performance breakdowns.
     428         *
     429         * Example:
     430         *
     431         *     $processor = WP_HTML_Processor::createFragment( '<div><span><figure><img></figure></span></div>' );
     432         *     $processor->next_tag( 'img' );
     433         *     true  === $processor->matches_breadcrumbs( array( 'figure', 'img' ) );
     434         *     true  === $processor->matches_breadcrumbs( array( 'span', 'figure', 'img' ) );
     435         *     false === $processor->matches_breadcrumbs( array( 'span', 'img' ) );
     436         *     true  === $processor->matches_breadcrumbs( array( 'span', '*', 'img' ) );
     437         *
     438         * @since 6.4.0
     439         *
     440         * @param string[] $breadcrumbs DOM sub-path at which element is found, e.g. `array( 'FIGURE', 'IMG' )`.
     441         *                              May also contain the wildcard `*` which matches a single element, e.g. `array( 'SECTION', '*' )`.
     442         * @return bool Whether the currently-matched tag is found at the given nested structure.
     443         */
     444        public function matches_breadcrumbs( $breadcrumbs ) {
     445                if ( ! $this->get_tag() ) {
     446                        return false;
     447                }
     448
     449                // Everything matches when there are zero constraints.
     450                if ( 0 === count( $breadcrumbs ) ) {
     451                        return true;
     452                }
     453
     454                // Start at the last crumb.
     455                $crumb = end( $breadcrumbs );
     456
     457                if ( '*' !== $crumb && $this->get_tag() !== strtoupper( $crumb ) ) {
     458                        return false;
     459                }
     460
     461                foreach ( $this->state->stack_of_open_elements->walk_up() as $node ) {
     462                        $crumb = strtoupper( current( $breadcrumbs ) );
     463
     464                        if ( '*' !== $crumb && $node->node_name !== $crumb ) {
     465                                return false;
    426466                        }
    427467
    428                         $crumb = end( $breadcrumbs );
     468                        if ( false === prev( $breadcrumbs ) ) {
     469                                return true;
     470                        }
    429471                }
    430472
Note: See TracChangeset for help on using the changeset viewer.