Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/wp-includes/block-supports/dimensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function wp_render_dimensions_support( $block_content, $block ) {
$tags->set_attribute( 'style', $updated_style );

if ( ! empty( $styles['classnames'] ) ) {
foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
foreach ( WP_HTML_Tag_Processor::parse_class_list( $styles['classnames'] ) as $class_name ) {
if (
str_contains( $class_name, 'aspect-ratio' ) &&
! isset( $block_attributes['style']['dimensions']['aspectRatio'] )
Expand Down
6 changes: 3 additions & 3 deletions src/wp-includes/bookmark-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,11 @@ function wp_list_bookmarks( $args = '' ) {

$output = '';

if ( ! is_array( $parsed_args['class'] ) ) {
$parsed_args['class'] = explode( ' ', $parsed_args['class'] );
if ( is_string( $parsed_args['class'] ) ) {
$parsed_args['class'] = iterator_to_array( WP_HTML_Tag_Processor::parse_class_list( $parsed_args['class'] ) );
}
$parsed_args['class'] = array_map( 'sanitize_html_class', $parsed_args['class'] );
$parsed_args['class'] = trim( implode( ' ', $parsed_args['class'] ) );
$parsed_args['class'] = implode( ' ', $parsed_args['class'] );

if ( $parsed_args['categorize'] ) {
$cats = get_terms(
Expand Down
3 changes: 1 addition & 2 deletions src/wp-includes/class-wp-duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -1180,8 +1180,7 @@ public static function restore_image_outer_container( $block_content ) {
$tags->set_bookmark( 'wrapper-div' );
$tags->next_tag();

$inner_classnames = explode( ' ', $tags->get_attribute( 'class' ) );
foreach ( $inner_classnames as $classname ) {
foreach ( WP_HTML_Tag_Processor::parse_class_list( $tags->get_attribute( 'class' ) ) as $classname ) {
if ( 0 === strpos( $classname, 'wp-duotone' ) ) {
$tags->remove_class( $classname );
$tags->seek( 'wrapper-div' );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ public function value_as_wp_post_nav_menu_item() {

// 'classes' should be an array, as in wp_setup_nav_menu_item().
if ( isset( $item->classes ) && is_scalar( $item->classes ) ) {
$item->classes = explode( ' ', $item->classes );
$item->classes = iterator_to_array( WP_HTML_Tag_Processor::parse_class_list( $item->classes ) );
}

$item->ID = $this->post_id;
Expand Down
61 changes: 53 additions & 8 deletions src/wp-includes/html-api/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 <egg> 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 );
}
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/nav-menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item
update_post_meta( $menu_item_db_id, '_menu_item_object', sanitize_key( $args['menu-item-object'] ) );
update_post_meta( $menu_item_db_id, '_menu_item_target', sanitize_key( $args['menu-item-target'] ) );

$args['menu-item-classes'] = array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-classes'] ) );
$args['menu-item-classes'] = array_map( 'sanitize_html_class', iterator_to_array( WP_HTML_Tag_Processor::parse_class_list( $args['menu-item-classes'] ) ) );
$args['menu-item-xfn'] = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-xfn'] ) ) );
update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );
update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );
Expand Down
20 changes: 7 additions & 13 deletions src/wp-includes/post-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -496,18 +496,15 @@ function get_post_class( $css_class = '', $post = null ) {

$classes = array();

if ( $css_class ) {
if ( ! is_array( $css_class ) ) {
$css_class = preg_split( '#\s+#', $css_class );
}
$classes = array_map( 'esc_attr', $css_class );
} else {
if ( is_string( $css_class ) ) {
$classes = iterator_to_array( WP_HTML_Tag_Processor::parse_class_list( $css_class ) );
} elseif ( ! is_array( $css_class ) ) {
// Ensure that we always coerce class to being an array.
$css_class = array();
$classes = array();
}

if ( ! $post ) {
return $classes;
return array_values( array_unique( array_map( 'esc_attr', $classes ) ) );
}

$classes[] = 'post-' . $post->ID;
Expand Down Expand Up @@ -593,8 +590,6 @@ function get_post_class( $css_class = '', $post = null ) {
}
}

$classes = array_map( 'esc_attr', $classes );

/**
* Filters the list of CSS class names for the current post.
*
Expand All @@ -606,10 +601,9 @@ function get_post_class( $css_class = '', $post = null ) {
*/
$classes = apply_filters( 'post_class', $classes, $css_class, $post->ID );

$classes = array_unique( $classes );
$classes = array_values( $classes );
$classes = array_map( 'esc_attr', $classes );

return $classes;
return array_values( array_unique( $classes ) );
}

/**
Expand Down
Loading