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 83b71b81ecaa4..203e4b80a5b39 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
@@ -1187,25 +1187,70 @@ public function class_list() {
return;
}
- $seen = array();
+ return self::parse_class_list( $class, $this->compat_mode );
+ }
- $is_quirks = self::QUIRKS_MODE === $this->compat_mode;
+ /**
+ * Generator for a foreach loop to step through each class name for the matched tag.
+ *
+ * This generator function is designed to be used inside a "foreach" loop.
+ *
+ * Example:
+ *
+ * $class_list = 'free <egg<\tlang-en';
+ * foreach ( WP_HTML_Tag_Processor::parse_class_list( $class_list ) as $class_name ) {
+ * echo "{$class_name} ";
+ * }
+ * // Outputs: "free lang-en "
+ *
+ * The default behavior is normative for HTML5 documents in “no-quirks” mode. For
+ * rare documents with “quirks mode” DOCTYPE declarations, pass {@see self::QUIRKS_MODE}
+ * as the compatibility mode for ASCII-case-insensitive comparison of class names. Use
+ * this only when certain that the containing document is in no-quirks mode.
+ *
+ * Example:
+ *
+ * $class_list = 'wide naRRow WIDE Wide narrow';
+ * $classes = WP_HTML_Tag_Processor::parse_class_list( $class_list );
+ * $classes = iterator_to_array( $classes );
+ * $classes === array( 'wide', 'naRRow', 'WIDE', 'Wide', 'narrow' );
+ *
+ * $class_list = 'wide WIDE Wide';
+ * $classes = WP_HTML_Tag_Processor::parse_class_list( $class_list, WP_HTML_Tag_Processor::QUIRKS_MODE );
+ * $classes = iterator_to_array( $classes );
+ * $classes === array( 'wide', 'naRRow' );
+ *
+ * @since 6.9.0
+ *
+ * @param string $class_list Contains a full decoded HTML `class` attribute, or plain
+ * list of space-separated CSS class names.
+ * @param string|null $compat_mode Optional. Specifies how to compare class names, whether
+ * byte-for-byte or ASCII-case-insensitively. Default is
+ * NO_QUIRKS_MODE, which compares byte for byte.
+ * @return Generator Iterates over each unique CSS class name in the given input list in order.
+ */
+ public static function parse_class_list( $class_list, $compat_mode = self::NO_QUIRKS_MODE ) {
+ if ( '' === $class_list || ! is_string( $class_list ) ) {
+ return;
+ }
- $at = 0;
- while ( $at < strlen( $class ) ) {
+ $seen = array();
+ $is_quirks = self::QUIRKS_MODE === $compat_mode;
+ $at = 0;
+ while ( $at < strlen( $class_list ) ) {
// Skip past any initial boundary characters.
- $at += strspn( $class, " \t\f\r\n", $at );
- if ( $at >= strlen( $class ) ) {
+ $at += strspn( $class_list, " \t\f\r\n", $at );
+ if ( $at >= strlen( $class_list ) ) {
return;
}
// Find the byte length until the next boundary.
- $length = strcspn( $class, " \t\f\r\n", $at );
+ $length = strcspn( $class_list, " \t\f\r\n", $at );
if ( 0 === $length ) {
return;
}
- $name = str_replace( "\x00", "\u{FFFD}", substr( $class, $at, $length ) );
+ $name = str_replace( "\x00", "\u{FFFD}", substr( $class_list, $at, $length ) );
if ( $is_quirks ) {
$name = strtolower( $name );
}