diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php
index 4f771535edaa7..dd4469d5277d9 100644
--- a/src/wp-includes/html-api/class-wp-html-processor.php
+++ b/src/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|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
*/
private $breadcrumbs = array();
@@ -687,6 +696,8 @@ public function get_unsupported_exception() {
* 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 @@ private function is_virtual(): bool {
* @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 $breadcrumbs
*/
public function matches_breadcrumbs( $breadcrumbs ): bool {
// Everything matches when there are zero constraints.
@@ -1201,6 +1214,8 @@ public function step( $node_to_process = self::PROCESS_NEXT_NODE ): bool {
* @since 6.4.0
*
* @return string[] Array of tag names representing path to matched node.
+ *
+ * @phpstan-return list
*/
public function get_breadcrumbs(): array {
return $this->breadcrumbs;
@@ -5154,6 +5169,8 @@ public function get_namespace(): string {
* @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 @@ public function has_self_closing_flag(): bool {
* @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 @@ private function insert_foreign_element( WP_HTML_Token $token, bool $only_add_to
* @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/src/wp-includes/html-api/class-wp-html-tag-processor.php b/src/wp-includes/html-api/class-wp-html-tag-processor.php
index c39368e9a40c1..cb92daa3d5a8c 100644
--- a/src/wp-includes/html-api/class-wp-html-tag-processor.php
+++ b/src/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 @@ public function change_parsing_namespace( string $new_namespace ): bool {
* }
* @return bool Whether a tag was matched.
*
+ * @phpstan-param Query $query
* @phpstan-impure
*/
public function next_tag( $query = null ): bool {
@@ -3034,12 +3046,22 @@ public function get_namespace(): string {
* @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 @@ public function get_tag(): ?string {
* @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 @@ public function get_token_type(): ?string {
* @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 @@ public function get_updated_html(): string {
* @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. .
* }
+ * @phpstan-param Query $query
*/
private function parse_query( $query ) {
if ( null !== $query && $query === $this->last_query ) {
diff --git a/src/wp-includes/html-api/class-wp-html-token.php b/src/wp-includes/html-api/class-wp-html-token.php
index 01701d02ec9d1..926a12cf8200f 100644
--- a/src/wp-includes/html-api/class-wp-html-token.php
+++ b/src/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;