WP_HTML_Tag_Processor::parse_next_attribute(): bool

In this article

This function’s access is marked private. This means it is not intended for use by plugin or theme developers, only in other core functions. It is listed here for completeness.

Parses the next attribute.

Return

bool Whether an attribute was found before the end of the document.

Source

private function parse_next_attribute() {
	// Skip whitespace and slashes.
	$this->bytes_already_parsed += strspn( $this->html, " \t\f\r\n/", $this->bytes_already_parsed );
	if ( $this->bytes_already_parsed >= strlen( $this->html ) ) {
		$this->parser_state = self::STATE_INCOMPLETE_INPUT;

		return false;
	}

	/*
	 * Treat the equal sign as a part of the attribute
	 * name if it is the first encountered byte.
	 *
	 * @see https://html.spec.whatwg.org/multipage/parsing.html#before-attribute-name-state
	 */
	$name_length = '=' === $this->html[ $this->bytes_already_parsed ]
		? 1 + strcspn( $this->html, "=/> \t\f\r\n", $this->bytes_already_parsed + 1 )
		: strcspn( $this->html, "=/> \t\f\r\n", $this->bytes_already_parsed );

	// No attribute, just tag closer.
	if ( 0 === $name_length || $this->bytes_already_parsed + $name_length >= strlen( $this->html ) ) {
		return false;
	}

	$attribute_start             = $this->bytes_already_parsed;
	$attribute_name              = substr( $this->html, $attribute_start, $name_length );
	$this->bytes_already_parsed += $name_length;
	if ( $this->bytes_already_parsed >= strlen( $this->html ) ) {
		$this->parser_state = self::STATE_INCOMPLETE_INPUT;

		return false;
	}

	$this->skip_whitespace();
	if ( $this->bytes_already_parsed >= strlen( $this->html ) ) {
		$this->parser_state = self::STATE_INCOMPLETE_INPUT;

		return false;
	}

	$has_value = '=' === $this->html[ $this->bytes_already_parsed ];
	if ( $has_value ) {
		++$this->bytes_already_parsed;
		$this->skip_whitespace();
		if ( $this->bytes_already_parsed >= strlen( $this->html ) ) {
			$this->parser_state = self::STATE_INCOMPLETE_INPUT;

			return false;
		}

		switch ( $this->html[ $this->bytes_already_parsed ] ) {
			case "'":
			case '"':
				$quote                      = $this->html[ $this->bytes_already_parsed ];
				$value_start                = $this->bytes_already_parsed + 1;
				$value_length               = strcspn( $this->html, $quote, $value_start );
				$attribute_end              = $value_start + $value_length + 1;
				$this->bytes_already_parsed = $attribute_end;
				break;

			default:
				$value_start                = $this->bytes_already_parsed;
				$value_length               = strcspn( $this->html, "> \t\f\r\n", $value_start );
				$attribute_end              = $value_start + $value_length;
				$this->bytes_already_parsed = $attribute_end;
		}
	} else {
		$value_start   = $this->bytes_already_parsed;
		$value_length  = 0;
		$attribute_end = $attribute_start + $name_length;
	}

	if ( $attribute_end >= strlen( $this->html ) ) {
		$this->parser_state = self::STATE_INCOMPLETE_INPUT;

		return false;
	}

	if ( $this->is_closing_tag ) {
		return true;
	}

	/*
	 * > There must never be two or more attributes on
	 * > the same start tag whose names are an ASCII
	 * > case-insensitive match for each other.
	 *     - HTML 5 spec
	 *
	 * @see https://html.spec.whatwg.org/multipage/syntax.html#attributes-2:ascii-case-insensitive
	 */
	$comparable_name = strtolower( $attribute_name );

	// If an attribute is listed many times, only use the first declaration and ignore the rest.
	if ( ! array_key_exists( $comparable_name, $this->attributes ) ) {
		$this->attributes[ $comparable_name ] = new WP_HTML_Attribute_Token(
			$attribute_name,
			$value_start,
			$value_length,
			$attribute_start,
			$attribute_end - $attribute_start,
			! $has_value
		);

		return true;
	}

	/*
	 * Track the duplicate attributes so if we remove it, all disappear together.
	 *
	 * While `$this->duplicated_attributes` could always be stored as an `array()`,
	 * which would simplify the logic here, storing a `null` and only allocating
	 * an array when encountering duplicates avoids needless allocations in the
	 * normative case of parsing tags with no duplicate attributes.
	 */
	$duplicate_span = new WP_HTML_Span( $attribute_start, $attribute_end - $attribute_start );
	if ( null === $this->duplicate_attributes ) {
		$this->duplicate_attributes = array( $comparable_name => array( $duplicate_span ) );
	} elseif ( ! array_key_exists( $comparable_name, $this->duplicate_attributes ) ) {
		$this->duplicate_attributes[ $comparable_name ] = array( $duplicate_span );
	} else {
		$this->duplicate_attributes[ $comparable_name ][] = $duplicate_span;
	}

	return true;
}

Changelog

VersionDescription
6.2.0Introduced.

User Contributed Notes

You must log in before being able to contribute a note or feedback.