diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/nav-menu.php --- a/wp/wp-includes/nav-menu.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-includes/nav-menu.php Fri Sep 05 18:40:08 2025 +0200 @@ -143,7 +143,7 @@ * * @global array $_wp_registered_nav_menus * - * @return string[] Associative array of egistered navigation menu descriptions keyed + * @return string[] Associative array of registered navigation menu descriptions keyed * by their location. If none are registered, an empty array. */ function get_registered_nav_menus() { @@ -280,7 +280,7 @@ // Remove this menu from any locations. $locations = get_nav_menu_locations(); foreach ( $locations as $location => $menu_id ) { - if ( $menu_id == $menu->term_id ) { + if ( $menu_id === $menu->term_id ) { $locations[ $location ] = 0; } } @@ -331,7 +331,7 @@ $_possible_existing && ! is_wp_error( $_possible_existing ) && isset( $_possible_existing->term_id ) && - $_possible_existing->term_id != $menu_id + $_possible_existing->term_id !== $menu_id ) { return new WP_Error( 'menu_exists', @@ -458,12 +458,22 @@ $args = wp_parse_args( $menu_item_data, $defaults ); - if ( 0 == $menu_id ) { + if ( 0 === $menu_id ) { $args['menu-item-position'] = 1; - } elseif ( 0 == (int) $args['menu-item-position'] ) { - $menu_items = 0 == $menu_id ? array() : (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) ); - $last_item = array_pop( $menu_items ); - $args['menu-item-position'] = ( $last_item && isset( $last_item->menu_order ) ) ? 1 + $last_item->menu_order : count( $menu_items ); + } elseif ( 0 === (int) $args['menu-item-position'] ) { + $menu_items = array(); + + if ( 0 !== $menu_id ) { + $menu_items = (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) ); + } + + $last_item = array_pop( $menu_items ); + + if ( $last_item && isset( $last_item->menu_order ) ) { + $args['menu-item-position'] = 1 + $last_item->menu_order; + } else { + $args['menu-item-position'] = count( $menu_items ); + } } $original_parent = 0 < $menu_item_db_id ? get_post_field( 'post_parent', $menu_item_db_id ) : 0; @@ -522,7 +532,7 @@ $post['post_date'] = $post_date; } - $update = 0 != $menu_item_db_id; + $update = 0 !== $menu_item_db_id; // New menu item. Default is draft status. if ( ! $update ) { @@ -547,8 +557,10 @@ do_action( 'wp_add_nav_menu_item', $menu_id, $menu_item_db_id, $args ); } - // Associate the menu item with the menu term. - // Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms(). + /* + * Associate the menu item with the menu term. + * Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms(). + */ if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) { $update_terms = wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' ); if ( is_wp_error( $update_terms ) ) { @@ -563,6 +575,11 @@ $menu_item_db_id = (int) $menu_item_db_id; + // Reset invalid `menu_item_parent`. + if ( (int) $args['menu-item-parent-id'] === $menu_item_db_id ) { + $args['menu-item-parent-id'] = 0; + } + update_post_meta( $menu_item_db_id, '_menu_item_type', sanitize_key( $args['menu-item-type'] ) ); update_post_meta( $menu_item_db_id, '_menu_item_menu_item_parent', (string) ( (int) $args['menu-item-parent-id'] ) ); update_post_meta( $menu_item_db_id, '_menu_item_object_id', (string) ( (int) $args['menu-item-object-id'] ) ); @@ -573,9 +590,9 @@ $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'] ); - update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw( $args['menu-item-url'] ) ); + update_post_meta( $menu_item_db_id, '_menu_item_url', sanitize_url( $args['menu-item-url'] ) ); - if ( 0 == $menu_id ) { + if ( 0 === $menu_id ) { update_post_meta( $menu_item_db_id, '_menu_item_orphaned', (string) time() ); } elseif ( get_post_meta( $menu_item_db_id, '_menu_item_orphaned' ) ) { delete_post_meta( $menu_item_db_id, '_menu_item_orphaned' ); @@ -669,18 +686,21 @@ * @param array $args { * Optional. Arguments to pass to get_posts(). * - * @type string $order How to order nav menu items as queried with get_posts(). Will be ignored - * if 'output' is ARRAY_A. Default 'ASC'. - * @type string $orderby Field to order menu items by as retrieved from get_posts(). Supply an orderby - * field via 'output_key' to affect the output order of nav menu items. - * Default 'menu_order'. - * @type string $post_type Menu items post type. Default 'nav_menu_item'. - * @type string $post_status Menu items post status. Default 'publish'. - * @type string $output How to order outputted menu items. Default ARRAY_A. - * @type string $output_key Key to use for ordering the actual menu items that get returned. Note that - * that is not a get_posts() argument and will only affect output of menu items - * processed in this function. Default 'menu_order'. - * @type bool $nopaging Whether to retrieve all menu items (true) or paginate (false). Default true. + * @type string $order How to order nav menu items as queried with get_posts(). + * Will be ignored if 'output' is ARRAY_A. Default 'ASC'. + * @type string $orderby Field to order menu items by as retrieved from get_posts(). + * Supply an orderby field via 'output_key' to affect the + * output order of nav menu items. Default 'menu_order'. + * @type string $post_type Menu items post type. Default 'nav_menu_item'. + * @type string $post_status Menu items post status. Default 'publish'. + * @type string $output How to order outputted menu items. Default ARRAY_A. + * @type string $output_key Key to use for ordering the actual menu items that get + * returned. Note that that is not a get_posts() argument + * and will only affect output of menu items processed in + * this function. Default 'menu_order'. + * @type bool $nopaging Whether to retrieve all menu items (true) or paginate + * (false). Default true. + * @type bool $update_menu_item_cache Whether to update the menu item cache. Default true. * } * @return array|false Array of menu items, otherwise false. */ @@ -691,21 +711,20 @@ return false; } - static $fetched = array(); - if ( ! taxonomy_exists( 'nav_menu' ) ) { return false; } $defaults = array( - 'order' => 'ASC', - 'orderby' => 'menu_order', - 'post_type' => 'nav_menu_item', - 'post_status' => 'publish', - 'output' => ARRAY_A, - 'output_key' => 'menu_order', - 'nopaging' => true, - 'tax_query' => array( + 'order' => 'ASC', + 'orderby' => 'menu_order', + 'post_type' => 'nav_menu_item', + 'post_status' => 'publish', + 'output' => ARRAY_A, + 'output_key' => 'menu_order', + 'nopaging' => true, + 'update_menu_item_cache' => true, + 'tax_query' => array( array( 'taxonomy' => 'nav_menu', 'field' => 'term_taxonomy_id', @@ -720,33 +739,6 @@ $items = array(); } - // Prime posts and terms caches. - if ( empty( $fetched[ $menu->term_id ] ) ) { - $fetched[ $menu->term_id ] = true; - $post_ids = array(); - $term_ids = array(); - foreach ( $items as $item ) { - $object_id = get_post_meta( $item->ID, '_menu_item_object_id', true ); - $type = get_post_meta( $item->ID, '_menu_item_type', true ); - - if ( 'post_type' === $type ) { - $post_ids[] = (int) $object_id; - } elseif ( 'taxonomy' === $type ) { - $term_ids[] = (int) $object_id; - } - } - - if ( ! empty( $post_ids ) ) { - _prime_post_caches( $post_ids, false ); - } - unset( $post_ids ); - - if ( ! empty( $term_ids ) ) { - _prime_term_caches( $term_ids ); - } - unset( $term_ids ); - } - $items = array_map( 'wp_setup_nav_menu_item', $items ); if ( ! is_admin() ) { // Remove invalid items only on front end. @@ -781,6 +773,41 @@ } /** + * Updates post and term caches for all linked objects for a list of menu items. + * + * @since 6.1.0 + * + * @param WP_Post[] $menu_items Array of menu item post objects. + */ +function update_menu_item_cache( $menu_items ) { + $post_ids = array(); + $term_ids = array(); + + foreach ( $menu_items as $menu_item ) { + if ( 'nav_menu_item' !== $menu_item->post_type ) { + continue; + } + + $object_id = get_post_meta( $menu_item->ID, '_menu_item_object_id', true ); + $type = get_post_meta( $menu_item->ID, '_menu_item_type', true ); + + if ( 'post_type' === $type ) { + $post_ids[] = (int) $object_id; + } elseif ( 'taxonomy' === $type ) { + $term_ids[] = (int) $object_id; + } + } + + if ( ! empty( $post_ids ) ) { + _prime_post_caches( $post_ids, false ); + } + + if ( ! empty( $term_ids ) ) { + _prime_term_caches( $term_ids ); + } +} + +/** * Decorates a menu item object with the shared navigation menu item properties. * * Properties: @@ -808,6 +835,24 @@ * @return object The menu item with standard menu item properties. */ function wp_setup_nav_menu_item( $menu_item ) { + + /** + * Filters whether to short-circuit the wp_setup_nav_menu_item() output. + * + * Returning a non-null value from the filter will short-circuit wp_setup_nav_menu_item(), + * returning that value instead. + * + * @since 6.3.0 + * + * @param object|null $modified_menu_item Modified menu item. Default null. + * @param object $menu_item The menu item to modify. + */ + $pre_menu_item = apply_filters( 'pre_wp_setup_nav_menu_item', null, $menu_item ); + + if ( null !== $pre_menu_item ) { + return $pre_menu_item; + } + if ( isset( $menu_item->post_type ) ) { if ( 'nav_menu_item' === $menu_item->post_type ) { $menu_item->db_id = (int) $menu_item->ID; @@ -1003,7 +1048,7 @@ $object_id = (int) $object_id; $menu_item_ids = array(); - $query = new WP_Query; + $query = new WP_Query(); $menu_items = $query->query( array( 'meta_key' => '_menu_item_object_id', @@ -1024,7 +1069,7 @@ } elseif ( 'taxonomy' === $object_type && 'taxonomy' === $menu_item_type && - get_post_meta( $menu_item->ID, '_menu_item_object', true ) == $taxonomy + get_post_meta( $menu_item->ID, '_menu_item_object', true ) === $taxonomy ) { $menu_item_ids[] = (int) $menu_item->ID; } @@ -1111,7 +1156,7 @@ continue; } foreach ( $items as $item ) { - if ( $post->ID == $item->object_id ) { + if ( $post->ID === (int) $item->object_id ) { continue 2; } } @@ -1266,3 +1311,31 @@ return $new_nav_menu_locations; } + +/** + * Prevents menu items from being their own parent. + * + * Resets menu_item_parent to 0 when the parent is set to the item itself. + * For use before saving `_menu_item_menu_item_parent` in nav-menus.php. + * + * @since 6.2.0 + * @access private + * + * @param array $menu_item_data The menu item data array. + * @return array The menu item data with reset menu_item_parent. + */ +function _wp_reset_invalid_menu_item_parent( $menu_item_data ) { + if ( ! is_array( $menu_item_data ) ) { + return $menu_item_data; + } + + if ( + ! empty( $menu_item_data['ID'] ) && + ! empty( $menu_item_data['menu_item_parent'] ) && + (int) $menu_item_data['ID'] === (int) $menu_item_data['menu_item_parent'] + ) { + $menu_item_data['menu_item_parent'] = 0; + } + + return $menu_item_data; +}