Commit 296e5ad928 for wordpress.org

commit 296e5ad928ad28ece2ff119d5bdfbcb83a250040
Author: westonruter <westonruter@git.wordpress.org>
Date:   Tue Nov 3 05:59:54 2026 +0000

    HTML API: Type tag queries and tag names for PHPStan.

    The `$query` parameter of `next_tag()` is documented as `array|string|null`, but its hash notation only describes the array form, so stubs generated from it (as used by Gutenberg) turned it into an array shape and dropped the bare tag-name form, reporting calls like `next_tag( 'FIGURE' )` as errors. A `Query` type alias now covers the array, bare tag-name, and `null` forms, with each key allowed to be `null` as documented. The HTML Processor defines its own alias adding the `breadcrumbs` key, since type aliases are not inherited by subclasses.

    Tag names are returned as `non-falsy-string`, since a parsed name is never empty or `'0'`, but accepted as `non-empty-string`, since that is all a `'' !== $tag` check proves and a `'0'` query simply matches nothing.

    The changes are to PHPDoc only, so there is no runtime change.

    Developed in https://github.com/WordPress/wordpress-develop/pull/15305.
    Follow-up to r55203.

    Props westonruter, dmsnell.
    See #65817.

    Built from https://develop.svn.wordpress.org/trunk@65220


    git-svn-id: http://core.svn.wordpress.org/trunk@64369 1a063a9b-81f0-0310-95a4-ce76da25c4cd

diff --git a/wp-includes/html-api/class-wp-html-processor.php b/wp-includes/html-api/class-wp-html-processor.php
index 4f771535ed..dd4469d527 100644
--- a/wp-includes/html-api/class-wp-html-processor.php
+++ b/wp-includes/html-api/class-wp-html-processor.php
@@ -143,6 +143,14 @@
  * @see WP_HTML_Tag_Processor
  * @link https://html.spec.whatwg.org/
  * @phpstan-consistent-constructor
+ *
+ * @phpstan-type Query array{
+ *     tag_name?: non-empty-string|null,
+ *     match_offset?: int<1, max>|null,
+ *     class_name?: non-empty-string|null,
+ *     tag_closers?: 'visit'|'skip'|null,
+ *     breadcrumbs?: array<non-empty-string>,
+ * }|non-empty-string|null
  */
 class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	/**
@@ -237,6 +245,7 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 * @since 6.7.0
 	 *
 	 * @var string[]
+	 * @phpstan-var list<non-falsy-string>
 	 */
 	private $breadcrumbs = array();

@@ -687,6 +696,8 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 *                                     May also contain the wildcard `*` which matches a single element, e.g. `array( 'SECTION', '*' )`.
 	 * }
 	 * @return bool Whether a tag was matched.
+	 *
+	 * @phpstan-param Query $query
 	 */
 	public function next_tag( $query = null ): bool {
 		$visit_closers = isset( $query['tag_closers'] ) && 'visit' === $query['tag_closers'];
@@ -930,6 +941,8 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 * @param string[] $breadcrumbs DOM sub-path at which element is found, e.g. `array( 'FIGURE', 'IMG' )`.
 	 *                              May also contain the wildcard `*` which matches a single element, e.g. `array( 'SECTION', '*' )`.
 	 * @return bool Whether the currently-matched tag is found at the given nested structure.
+	 *
+	 * @phpstan-param array<non-empty-string> $breadcrumbs
 	 */
 	public function matches_breadcrumbs( $breadcrumbs ): bool {
 		// Everything matches when there are zero constraints.
@@ -1201,6 +1214,8 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 * @since 6.4.0
 	 *
 	 * @return string[] Array of tag names representing path to matched node.
+	 *
+	 * @phpstan-return list<non-falsy-string>
 	 */
 	public function get_breadcrumbs(): array {
 		return $this->breadcrumbs;
@@ -5154,6 +5169,8 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 * @since 6.4.0
 	 *
 	 * @return string|null Name of currently matched tag in input HTML, or `null` if none found.
+	 *
+	 * @phpstan-return non-falsy-string|null
 	 */
 	public function get_tag(): ?string {
 		if ( null !== $this->last_error ) {
@@ -5219,6 +5236,8 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 * @since 6.6.0 Subclassed for the HTML Processor.
 	 *
 	 * @return string|null Name of the matched token.
+	 *
+	 * @phpstan-return non-falsy-string|null
 	 */
 	public function get_token_name(): ?string {
 		return $this->is_virtual()
@@ -6276,6 +6295,8 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor {
 	 * @param string|null $bookmark_name Optional. Name to give bookmark for created virtual node.
 	 *                                   Defaults to auto-creating a bookmark name.
 	 * @return WP_HTML_Token Newly-created virtual token.
+	 *
+	 * @phpstan-param non-falsy-string $token_name
 	 */
 	private function insert_virtual_node( $token_name, $bookmark_name = null ): WP_HTML_Token {
 		$here = $this->bookmarks[ $this->state->current_token->bookmark_name ];
diff --git a/wp-includes/html-api/class-wp-html-tag-processor.php b/wp-includes/html-api/class-wp-html-tag-processor.php
index c39368e9a4..cb92daa3d5 100644
--- a/wp-includes/html-api/class-wp-html-tag-processor.php
+++ b/wp-includes/html-api/class-wp-html-tag-processor.php
@@ -407,6 +407,13 @@
  * @since 6.5.0 Pauses processor when input ends in an incomplete syntax token.
  *              Introduces "special" elements which act like void elements, e.g. TITLE, STYLE.
  *              Allows scanning through all tokens and processing modifiable text, where applicable.
+ *
+ * @phpstan-type Query array{
+ *     tag_name?: non-empty-string|null,
+ *     match_offset?: int<1, max>|null,
+ *     class_name?: non-empty-string|null,
+ *     tag_closers?: 'visit'|'skip'|null,
+ * }|non-empty-string|null
  */
 class WP_HTML_Tag_Processor {
 	/**
@@ -443,7 +450,8 @@ class WP_HTML_Tag_Processor {
 	 * The last query passed to next_tag().
 	 *
 	 * @since 6.2.0
-	 * @var array|null
+	 * @var array|string|null
+	 * @phpstan-var Query
 	 */
 	private $last_query;

@@ -452,6 +460,7 @@ class WP_HTML_Tag_Processor {
 	 *
 	 * @since 6.2.0
 	 * @var string|null
+	 * @phpstan-var non-empty-string|null
 	 */
 	private $sought_tag_name;

@@ -460,6 +469,7 @@ class WP_HTML_Tag_Processor {
 	 *
 	 * @since 6.2.0
 	 * @var string|null
+	 * @phpstan-var non-empty-string|null
 	 */
 	private $sought_class_name;

@@ -468,6 +478,7 @@ class WP_HTML_Tag_Processor {
 	 *
 	 * @since 6.2.0
 	 * @var int|null
+	 * @phpstan-var int<1, max>|null
 	 */
 	private $sought_match_offset;

@@ -895,6 +906,7 @@ class WP_HTML_Tag_Processor {
 	 * }
 	 * @return bool Whether a tag was matched.
 	 *
+	 * @phpstan-param Query $query
 	 * @phpstan-impure
 	 */
 	public function next_tag( $query = null ): bool {
@@ -3034,12 +3046,22 @@ class WP_HTML_Tag_Processor {
 	 * @since 6.2.0
 	 *
 	 * @return string|null Name of currently matched tag in input HTML, or `null` if none found.
+	 *
+	 * @phpstan-return non-falsy-string|null
 	 */
 	public function get_tag(): ?string {
 		if ( null === $this->tag_name_starts_at ) {
 			return null;
 		}

+		/**
+		 * The name is never empty and never "0": a tag name starts with an ASCII
+		 * letter, a processing instruction target with a letter or "_", and the
+		 * target of a comment which looks like a processing instruction with a
+		 * letter, ":", or "_".
+		 *
+		 * @var non-falsy-string $tag_name
+		 */
 		$tag_name = str_replace( "\x00", "\u{FFFD}", substr( $this->html, $this->tag_name_starts_at, $this->tag_name_length ) );

 		if ( self::STATE_MATCHED_TAG === $this->parser_state ) {
@@ -3071,6 +3093,8 @@ class WP_HTML_Tag_Processor {
 	 * @since 6.7.0
 	 *
 	 * @return string|null Name of current tag name.
+	 *
+	 * @phpstan-return non-falsy-string|null
 	 */
 	public function get_qualified_tag_name(): ?string {
 		$tag_name = $this->get_tag();
@@ -3569,6 +3593,8 @@ class WP_HTML_Tag_Processor {
 	 * @since 6.5.0
 	 *
 	 * @return string|null Name of the matched token.
+	 *
+	 * @phpstan-return non-falsy-string|null
 	 */
 	public function get_token_name(): ?string {
 		switch ( $this->parser_state ) {
@@ -4988,6 +5014,7 @@ class WP_HTML_Tag_Processor {
 	 *     @type string|null $class_name   Tag must contain this class name to match.
 	 *     @type string      $tag_closers  "visit" or "skip": whether to stop on tag closers, e.g. </div>.
 	 * }
+	 * @phpstan-param Query $query
 	 */
 	private function parse_query( $query ) {
 		if ( null !== $query && $query === $this->last_query ) {
diff --git a/wp-includes/html-api/class-wp-html-token.php b/wp-includes/html-api/class-wp-html-token.php
index 01701d02ec..926a12cf82 100644
--- a/wp-includes/html-api/class-wp-html-token.php
+++ b/wp-includes/html-api/class-wp-html-token.php
@@ -43,6 +43,7 @@ class WP_HTML_Token {
 	 * @see WP_HTML_Processor::get_tag()
 	 *
 	 * @var string
+	 * @phpstan-var non-falsy-string
 	 */
 	public $node_name = null;

@@ -96,6 +97,8 @@ class WP_HTML_Token {
 	 * @param string        $node_name             Name of node token represents; if uppercase, an HTML element; if lowercase, a special value like "marker".
 	 * @param bool          $has_self_closing_flag Whether the source token contains the self-closing flag, regardless of whether it's valid.
 	 * @param callable|null $on_destroy            Optional. Function to call when destroying token, useful for releasing the bookmark.
+	 *
+	 * @phpstan-param non-falsy-string $node_name
 	 */
 	public function __construct( ?string $bookmark_name, string $node_name, bool $has_self_closing_flag, ?callable $on_destroy = null ) {
 		$this->bookmark_name         = $bookmark_name;
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 323a23b894..65921e9e51 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
  *
  * @global string $wp_version
  */
-$wp_version = '7.2-beta3-65219';
+$wp_version = '7.2-beta3-65220';

 /**
  * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.