--- a/wp/wp-includes/nav-menu-template.php Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/nav-menu-template.php Fri Sep 05 18:40:08 2025 +0200
@@ -198,6 +198,14 @@
$sorted_menu_items = array();
$menu_items_with_children = array();
foreach ( (array) $menu_items as $menu_item ) {
+ /*
+ * Fix invalid `menu_item_parent`. See: https://core.trac.wordpress.org/ticket/56926.
+ * Compare as strings. Plugins may change the ID to a string.
+ */
+ if ( (string) $menu_item->ID === (string) $menu_item->menu_item_parent ) {
+ $menu_item->menu_item_parent = 0;
+ }
+
$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
if ( $menu_item->menu_item_parent ) {
$menu_items_with_children[ $menu_item->menu_item_parent ] = true;
@@ -333,9 +341,9 @@
if ( is_array( $terms ) ) {
$possible_object_parents = array_merge( $possible_object_parents, $terms );
$term_to_ancestor = array();
- foreach ( (array) $term_hierarchy as $anc => $descs ) {
- foreach ( (array) $descs as $desc ) {
- $term_to_ancestor[ $desc ] = $anc;
+ foreach ( (array) $term_hierarchy as $ancestor => $descendents ) {
+ foreach ( (array) $descendents as $desc ) {
+ $term_to_ancestor[ $desc ] = $ancestor;
}
}
@@ -357,9 +365,9 @@
} elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
$term_hierarchy = _get_term_hierarchy( $queried_object->taxonomy );
$term_to_ancestor = array();
- foreach ( (array) $term_hierarchy as $anc => $descs ) {
- foreach ( (array) $descs as $desc ) {
- $term_to_ancestor[ $desc ] = $anc;
+ foreach ( (array) $term_hierarchy as $ancestor => $descendents ) {
+ foreach ( (array) $descendents as $desc ) {
+ $term_to_ancestor[ $desc ] = $ancestor;
}
}
$desc = $queried_object->term_id;
@@ -410,25 +418,25 @@
// If the menu item corresponds to the currently queried post or taxonomy object.
} elseif (
- $menu_item->object_id == $queried_object_id
+ (int) $menu_item->object_id === $queried_object_id
&& (
( ! empty( $home_page_id ) && 'post_type' === $menu_item->type
- && $wp_query->is_home && $home_page_id == $menu_item->object_id )
+ && $wp_query->is_home && $home_page_id === (int) $menu_item->object_id )
|| ( 'post_type' === $menu_item->type && $wp_query->is_singular )
|| ( 'taxonomy' === $menu_item->type
&& ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax )
- && $queried_object->taxonomy == $menu_item->object )
+ && $queried_object->taxonomy === $menu_item->object )
)
) {
$classes[] = 'current-menu-item';
$menu_items[ $key ]->current = true;
- $_anc_id = (int) $menu_item->db_id;
+ $ancestor_id = (int) $menu_item->db_id;
while (
- ( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) )
- && ! in_array( $_anc_id, $active_ancestor_item_ids, true )
+ ( $ancestor_id = (int) get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) )
+ && ! in_array( $ancestor_id, $active_ancestor_item_ids, true )
) {
- $active_ancestor_item_ids[] = $_anc_id;
+ $active_ancestor_item_ids[] = $ancestor_id;
}
if ( 'post_type' === $menu_item->type && 'page' === $menu_item->object ) {
@@ -449,13 +457,13 @@
) {
$classes[] = 'current-menu-item';
$menu_items[ $key ]->current = true;
- $_anc_id = (int) $menu_item->db_id;
+ $ancestor_id = (int) $menu_item->db_id;
while (
- ( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) )
- && ! in_array( $_anc_id, $active_ancestor_item_ids, true )
+ ( $ancestor_id = (int) get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) )
+ && ! in_array( $ancestor_id, $active_ancestor_item_ids, true )
) {
- $active_ancestor_item_ids[] = $_anc_id;
+ $active_ancestor_item_ids[] = $ancestor_id;
}
$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
@@ -486,13 +494,13 @@
if ( $raw_item_url && in_array( $item_url, $matches, true ) ) {
$classes[] = 'current-menu-item';
$menu_items[ $key ]->current = true;
- $_anc_id = (int) $menu_item->db_id;
+ $ancestor_id = (int) $menu_item->db_id;
while (
- ( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) )
- && ! in_array( $_anc_id, $active_ancestor_item_ids, true )
+ ( $ancestor_id = (int) get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) )
+ && ! in_array( $ancestor_id, $active_ancestor_item_ids, true )
) {
- $active_ancestor_item_ids[] = $_anc_id;
+ $active_ancestor_item_ids[] = $ancestor_id;
}
if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ), true ) ) {
@@ -504,18 +512,18 @@
$active_object = $menu_item->object;
// Give front page item the 'current-menu-item' class when extra query arguments are involved.
- } elseif ( $item_url == $front_page_url && is_front_page() ) {
+ } elseif ( $item_url === $front_page_url && is_front_page() ) {
$classes[] = 'current-menu-item';
}
- if ( untrailingslashit( $item_url ) == home_url() ) {
+ if ( untrailingslashit( $item_url ) === home_url() ) {
$classes[] = 'menu-item-home';
}
}
// Back-compat with wp_page_menu(): add "current_page_parent" to static home page link for any non-page query.
if ( ! empty( $home_page_id ) && 'post_type' === $menu_item->type
- && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id
+ && empty( $wp_query->is_page ) && $home_page_id === (int) $menu_item->object_id
) {
$classes[] = 'current_page_parent';
}
@@ -541,7 +549,7 @@
&& ! empty( $queried_object->post_type )
&& is_post_type_hierarchical( $queried_object->post_type )
&& in_array( (int) $parent_item->object_id, $queried_object->ancestors, true )
- && $parent_item->object != $queried_object->ID
+ && (int) $parent_item->object_id !== $queried_object->ID
) ||
// Ancestral term.
@@ -551,7 +559,7 @@
&& in_array( (int) $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ], true )
&& (
! isset( $queried_object->term_id ) ||
- $parent_item->object_id != $queried_object->term_id
+ (int) $parent_item->object_id !== $queried_object->term_id
)
)
)
@@ -603,7 +611,7 @@
* @return string The HTML list content for the menu items.
*/
function walk_nav_menu_tree( $items, $depth, $args ) {
- $walker = ( empty( $args->walker ) ) ? new Walker_Nav_Menu : $args->walker;
+ $walker = ( empty( $args->walker ) ) ? new Walker_Nav_Menu() : $args->walker;
return $walker->walk( $items, $depth, $args );
}
@@ -629,3 +637,60 @@
return $id;
}
+
+/**
+ * Remove the `menu-item-has-children` class from bottom level menu items.
+ *
+ * This runs on the {@see 'nav_menu_css_class'} filter. The $args and $depth
+ * parameters were added after the filter was originally introduced in
+ * WordPress 3.0.0 so this needs to allow for cases in which the filter is
+ * called without them.
+ *
+ * @see https://core.trac.wordpress.org/ticket/56926
+ *
+ * @since 6.2.0
+ *
+ * @param string[] $classes Array of the CSS classes that are applied to the menu item's `<li>` element.
+ * @param WP_Post $menu_item The current menu item object.
+ * @param stdClass|false $args An object of wp_nav_menu() arguments. Default false ($args unspecified when filter is called).
+ * @param int|false $depth Depth of menu item. Default false ($depth unspecified when filter is called).
+ * @return string[] Modified nav menu classes.
+ */
+function wp_nav_menu_remove_menu_item_has_children_class( $classes, $menu_item, $args = false, $depth = false ) {
+ /*
+ * Account for the filter being called without the $args or $depth parameters.
+ *
+ * This occurs when a theme uses a custom walker calling the `nav_menu_css_class`
+ * filter using the legacy formats prior to the introduction of the $args and
+ * $depth parameters.
+ *
+ * As both of these parameters are required for this function to determine
+ * both the current and maximum depth of the menu tree, the function does not
+ * attempt to remove the `menu-item-has-children` class if these parameters
+ * are not set.
+ */
+ if ( false === $depth || false === $args ) {
+ return $classes;
+ }
+
+ // Max-depth is 1-based.
+ $max_depth = isset( $args->depth ) ? (int) $args->depth : 0;
+ // Depth is 0-based so needs to be increased by one.
+ $depth = $depth + 1;
+
+ // Complete menu tree is displayed.
+ if ( 0 === $max_depth ) {
+ return $classes;
+ }
+
+ /*
+ * Remove the `menu-item-has-children` class from bottom level menu items.
+ * -1 is used to display all menu items in one level so the class should
+ * be removed from all menu items.
+ */
+ if ( -1 === $max_depth || $depth >= $max_depth ) {
+ $classes = array_diff( $classes, array( 'menu-item-has-children' ) );
+ }
+
+ return $classes;
+}