Make WordPress Core

Changeset 59285


Ignore:
Timestamp:
10/23/2024 10:03:23 PM (6 weeks ago)
Author:
westonruter
Message:

HTML API: Fix extensibility of WP_HTML_Processor::next_token().

Break out logic from the next_token() method into a private method which may call itself recursively. This allows for subclasses to override the next_token() method and be assured that each call to next_token() corresponds with the consumption of one single token. This also parallels how WP_HTML_Tag_Processor::next_token() wraps a private base_class_next_token() method.

Props westonruter, jonsurrell.
Fixes #62269.

Location:
trunk
Files:
2 added
2 edited

Legend:

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

    r59248 r59285  
    605605
    606606    /**
    607      * Ensures internal accounting is maintained for HTML semantic rules while
    608      * the underlying Tag Processor class is seeking to a bookmark.
     607     * Finds the next token in the HTML document.
    609608     *
    610609     * This doesn't currently have a way to represent non-tags and doesn't process
     
    613612     *
    614613     * @since 6.5.0 Added for internal support; do not use.
     614     * @since 6.7.1 Refactored so subclasses may extend.
     615     *
     616     * @return bool Whether a token was parsed.
     617     */
     618    public function next_token(): bool {
     619        return $this->_next_token();
     620    }
     621
     622    /**
     623     * Ensures internal accounting is maintained for HTML semantic rules while
     624     * the underlying Tag Processor class is seeking to a bookmark.
     625     *
     626     * This doesn't currently have a way to represent non-tags and doesn't process
     627     * semantic rules for text nodes. For access to the raw tokens consider using
     628     * WP_HTML_Tag_Processor instead.
     629     *
     630     * @since 6.7.1 Added for internal support; do not use.
    615631     *
    616632     * @access private
     
    618634     * @return bool
    619635     */
    620     public function next_token(): bool {
     636    private function _next_token(): bool {
    621637        $this->current_element = null;
    622638
     
    636652         */
    637653        if ( empty( $this->element_queue ) && $this->step() ) {
    638             return $this->next_token();
     654            return $this->_next_token();
    639655        }
    640656
     
    647663            }
    648664
    649             return empty( $this->element_queue ) ? false : $this->next_token();
     665            return empty( $this->element_queue ) ? false : $this->_next_token();
    650666        }
    651667
     
    658674         */
    659675        if ( 'root-node' === $this->current_element->token->bookmark_name ) {
    660             return $this->next_token();
     676            return $this->_next_token();
    661677        }
    662678
     
    670686        // Avoid sending close events for elements which don't expect a closing.
    671687        if ( $is_pop && ! $this->expects_closer( $this->current_element->token ) ) {
    672             return $this->next_token();
     688            return $this->_next_token();
    673689        }
    674690
  • trunk/tests/phpunit/tests/html-api/wpHtmlProcessor.php

    r59248 r59285  
    883883        $this->assertTrue( $processor->is_tag_closer() );
    884884    }
     885
     886    /**
     887     * Data provider.
     888     *
     889     * @return array
     890     */
     891    public function data_html_processor_with_extended_next_token() {
     892        return array(
     893            'single_instance_per_tag'   => array(
     894                'html'                  => '
     895                    <html>
     896                        <head>
     897                            <meta charset="utf-8">
     898                            <title>Hello World</title>
     899                        </head>
     900                        <body>
     901                            <h1>Hello World!</h1>
     902                            <img src="example.png">
     903                            <p>Each tag should occur only once in this document.<!--Closing P tag omitted intentionally.-->
     904                            <footer>The end.</footer>
     905                        </body>
     906                    </html>
     907                ',
     908                'expected_token_counts' => array(
     909                    '+HTML'    => 1,
     910                    '+HEAD'    => 1,
     911                    '#text'    => 14,
     912                    '+META'    => 1,
     913                    '+TITLE'   => 1,
     914                    '-HEAD'    => 1,
     915                    '+BODY'    => 1,
     916                    '+H1'      => 1,
     917                    '-H1'      => 1,
     918                    '+IMG'     => 1,
     919                    '+P'       => 1,
     920                    '#comment' => 1,
     921                    '-P'       => 1,
     922                    '+FOOTER'  => 1,
     923                    '-FOOTER'  => 1,
     924                    '-BODY'    => 1,
     925                    '-HTML'    => 1,
     926                    ''         => 1,
     927                ),
     928                'expected_xpaths'       => array(
     929                    0 => '/*[1][self::HTML]',
     930                    1 => '/*[1][self::HTML]/*[1][self::HEAD]',
     931                    2 => '/*[1][self::HTML]/*[1][self::HEAD]/*[1][self::META]',
     932                    3 => '/*[1][self::HTML]/*[1][self::HEAD]/*[2][self::TITLE]',
     933                    4 => '/*[1][self::HTML]/*[2][self::BODY]',
     934                    5 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::H1]',
     935                    6 => '/*[1][self::HTML]/*[2][self::BODY]/*[2][self::IMG]',
     936                    7 => '/*[1][self::HTML]/*[2][self::BODY]/*[3][self::P]',
     937                    8 => '/*[1][self::HTML]/*[2][self::BODY]/*[4][self::FOOTER]',
     938                ),
     939            ),
     940
     941            'multiple_tag_instances'    => array(
     942                'html'                  => '
     943                    <html>
     944                        <body>
     945                            <h1>Hello World!</h1>
     946                            <p>First
     947                            <p>Second
     948                            <p>Third
     949                            <ul>
     950                                <li>1
     951                                <li>2
     952                                <li>3
     953                            </ul>
     954                        </body>
     955                    </html>
     956                ',
     957                'expected_token_counts' => array(
     958                    '+HTML' => 1,
     959                    '+HEAD' => 1,
     960                    '-HEAD' => 1,
     961                    '+BODY' => 1,
     962                    '#text' => 13,
     963                    '+H1'   => 1,
     964                    '-H1'   => 1,
     965                    '+P'    => 3,
     966                    '-P'    => 3,
     967                    '+UL'   => 1,
     968                    '+LI'   => 3,
     969                    '-LI'   => 3,
     970                    '-UL'   => 1,
     971                    '-BODY' => 1,
     972                    '-HTML' => 1,
     973                    ''      => 1,
     974                ),
     975                'expected_xpaths'       => array(
     976                    0  => '/*[1][self::HTML]',
     977                    1  => '/*[1][self::HTML]/*[1][self::HEAD]',
     978                    2  => '/*[1][self::HTML]/*[2][self::BODY]',
     979                    3  => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::H1]',
     980                    4  => '/*[1][self::HTML]/*[2][self::BODY]/*[2][self::P]',
     981                    5  => '/*[1][self::HTML]/*[2][self::BODY]/*[3][self::P]',
     982                    6  => '/*[1][self::HTML]/*[2][self::BODY]/*[4][self::P]',
     983                    7  => '/*[1][self::HTML]/*[2][self::BODY]/*[5][self::UL]',
     984                    8  => '/*[1][self::HTML]/*[2][self::BODY]/*[5][self::UL]/*[1][self::LI]',
     985                    9  => '/*[1][self::HTML]/*[2][self::BODY]/*[5][self::UL]/*[2][self::LI]',
     986                    10 => '/*[1][self::HTML]/*[2][self::BODY]/*[5][self::UL]/*[3][self::LI]',
     987                ),
     988            ),
     989
     990            'extreme_nested_formatting' => array(
     991                'html'                  => '
     992                    <html>
     993                        <body>
     994                            <p>
     995                                <strong><em><strike><i><b><u>FORMAT</u></b></i></strike></em></strong>
     996                            </p>
     997                        </body>
     998                    </html>
     999                ',
     1000                'expected_token_counts' => array(
     1001                    '+HTML'   => 1,
     1002                    '+HEAD'   => 1,
     1003                    '-HEAD'   => 1,
     1004                    '+BODY'   => 1,
     1005                    '#text'   => 7,
     1006                    '+P'      => 1,
     1007                    '+STRONG' => 1,
     1008                    '+EM'     => 1,
     1009                    '+STRIKE' => 1,
     1010                    '+I'      => 1,
     1011                    '+B'      => 1,
     1012                    '+U'      => 1,
     1013                    '-U'      => 1,
     1014                    '-B'      => 1,
     1015                    '-I'      => 1,
     1016                    '-STRIKE' => 1,
     1017                    '-EM'     => 1,
     1018                    '-STRONG' => 1,
     1019                    '-P'      => 1,
     1020                    '-BODY'   => 1,
     1021                    '-HTML'   => 1,
     1022                    ''        => 1,
     1023                ),
     1024                'expected_xpaths'       => array(
     1025                    0 => '/*[1][self::HTML]',
     1026                    1 => '/*[1][self::HTML]/*[1][self::HEAD]',
     1027                    2 => '/*[1][self::HTML]/*[2][self::BODY]',
     1028                    3 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]',
     1029                    4 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]/*[1][self::STRONG]',
     1030                    5 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]/*[1][self::STRONG]/*[1][self::EM]',
     1031                    6 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]/*[1][self::STRONG]/*[1][self::EM]/*[1][self::STRIKE]',
     1032                    7 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]/*[1][self::STRONG]/*[1][self::EM]/*[1][self::STRIKE]/*[1][self::I]',
     1033                    8 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]/*[1][self::STRONG]/*[1][self::EM]/*[1][self::STRIKE]/*[1][self::I]/*[1][self::B]',
     1034                    9 => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::P]/*[1][self::STRONG]/*[1][self::EM]/*[1][self::STRIKE]/*[1][self::I]/*[1][self::B]/*[1][self::U]',
     1035                ),
     1036            ),
     1037        );
     1038    }
     1039
     1040    /**
     1041     * Ensures that subclasses to WP_HTML_Processor can do bookkeeping by extending the next_token() method.
     1042     *
     1043     * @ticket ?
     1044     * @dataProvider data_html_processor_with_extended_next_token
     1045     */
     1046    public function test_ensure_next_token_method_extensibility( $html, $expected_token_counts, $expected_xpaths ) {
     1047        require_once DIR_TESTDATA . '/html-api/html-xpath-generating-processor.php';
     1048
     1049        $processor     = HTML_XPath_Generating_Processor::create_full_parser( $html );
     1050        $actual_xpaths = array();
     1051        while ( $processor->next_tag() ) {
     1052            if ( ! $processor->is_tag_closer() ) {
     1053                $processor->set_attribute( 'xpath', $processor->get_xpath() );
     1054                $actual_xpaths[] = $processor->get_xpath();
     1055            }
     1056        }
     1057
     1058        $this->assertEquals( $expected_token_counts, $processor->token_seen_count, 'Snapshot: ' . var_export( $processor->token_seen_count, true ) );
     1059        $this->assertEquals( $expected_xpaths, $actual_xpaths, 'Snapshot: ' . var_export( $actual_xpaths, true ) );
     1060    }
    8851061}
Note: See TracChangeset for help on using the changeset viewer.