balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

nav-menu-template.php (23296B)


      1 <?php
      2 /**
      3  * Nav Menu API: Template functions
      4  *
      5  * @package WordPress
      6  * @subpackage Nav_Menus
      7  * @since 3.0.0
      8  */
      9 
     10 /** Walker_Nav_Menu class */
     11 require_once ABSPATH . WPINC . '/class-walker-nav-menu.php';
     12 
     13 /**
     14  * Displays a navigation menu.
     15  *
     16  * @since 3.0.0
     17  * @since 4.7.0 Added the `item_spacing` argument.
     18  * @since 5.5.0 Added the `container_aria_label` argument.
     19  *
     20  * @param array $args {
     21  *     Optional. Array of nav menu arguments.
     22  *
     23  *     @type int|string|WP_Term $menu                 Desired menu. Accepts a menu ID, slug, name, or object.
     24  *                                                    Default empty.
     25  *     @type string             $menu_class           CSS class to use for the ul element which forms the menu.
     26  *                                                    Default 'menu'.
     27  *     @type string             $menu_id              The ID that is applied to the ul element which forms the menu.
     28  *                                                    Default is the menu slug, incremented.
     29  *     @type string             $container            Whether to wrap the ul, and what to wrap it with.
     30  *                                                    Default 'div'.
     31  *     @type string             $container_class      Class that is applied to the container.
     32  *                                                    Default 'menu-{menu slug}-container'.
     33  *     @type string             $container_id         The ID that is applied to the container. Default empty.
     34  *     @type string             $container_aria_label The aria-label attribute that is applied to the container
     35  *                                                    when it's a nav element. Default empty.
     36  *     @type callable|false     $fallback_cb          If the menu doesn't exist, a callback function will fire.
     37  *                                                    Default is 'wp_page_menu'. Set to false for no fallback.
     38  *     @type string             $before               Text before the link markup. Default empty.
     39  *     @type string             $after                Text after the link markup. Default empty.
     40  *     @type string             $link_before          Text before the link text. Default empty.
     41  *     @type string             $link_after           Text after the link text. Default empty.
     42  *     @type bool               $echo                 Whether to echo the menu or return it. Default true.
     43  *     @type int                $depth                How many levels of the hierarchy are to be included.
     44  *                                                    0 means all. Default 0.
     45  *                                                    Default 0.
     46  *     @type object             $walker               Instance of a custom walker class. Default empty.
     47  *     @type string             $theme_location       Theme location to be used. Must be registered with
     48  *                                                    register_nav_menu() in order to be selectable by the user.
     49  *     @type string             $items_wrap           How the list items should be wrapped. Uses printf() format with
     50  *                                                    numbered placeholders. Default is a ul with an id and class.
     51  *     @type string             $item_spacing         Whether to preserve whitespace within the menu's HTML.
     52  *                                                    Accepts 'preserve' or 'discard'. Default 'preserve'.
     53  * }
     54  * @return void|string|false Void if 'echo' argument is true, menu output if 'echo' is false.
     55  *                           False if there are no items or no menu was found.
     56  */
     57 function wp_nav_menu( $args = array() ) {
     58 	static $menu_id_slugs = array();
     59 
     60 	$defaults = array(
     61 		'menu'                 => '',
     62 		'container'            => 'div',
     63 		'container_class'      => '',
     64 		'container_id'         => '',
     65 		'container_aria_label' => '',
     66 		'menu_class'           => 'menu',
     67 		'menu_id'              => '',
     68 		'echo'                 => true,
     69 		'fallback_cb'          => 'wp_page_menu',
     70 		'before'               => '',
     71 		'after'                => '',
     72 		'link_before'          => '',
     73 		'link_after'           => '',
     74 		'items_wrap'           => '<ul id="%1$s" class="%2$s">%3$s</ul>',
     75 		'item_spacing'         => 'preserve',
     76 		'depth'                => 0,
     77 		'walker'               => '',
     78 		'theme_location'       => '',
     79 	);
     80 
     81 	$args = wp_parse_args( $args, $defaults );
     82 
     83 	if ( ! in_array( $args['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
     84 		// Invalid value, fall back to default.
     85 		$args['item_spacing'] = $defaults['item_spacing'];
     86 	}
     87 
     88 	/**
     89 	 * Filters the arguments used to display a navigation menu.
     90 	 *
     91 	 * @since 3.0.0
     92 	 *
     93 	 * @see wp_nav_menu()
     94 	 *
     95 	 * @param array $args Array of wp_nav_menu() arguments.
     96 	 */
     97 	$args = apply_filters( 'wp_nav_menu_args', $args );
     98 	$args = (object) $args;
     99 
    100 	/**
    101 	 * Filters whether to short-circuit the wp_nav_menu() output.
    102 	 *
    103 	 * Returning a non-null value from the filter will short-circuit wp_nav_menu(),
    104 	 * echoing that value if $args->echo is true, returning that value otherwise.
    105 	 *
    106 	 * @since 3.9.0
    107 	 *
    108 	 * @see wp_nav_menu()
    109 	 *
    110 	 * @param string|null $output Nav menu output to short-circuit with. Default null.
    111 	 * @param stdClass    $args   An object containing wp_nav_menu() arguments.
    112 	 */
    113 	$nav_menu = apply_filters( 'pre_wp_nav_menu', null, $args );
    114 
    115 	if ( null !== $nav_menu ) {
    116 		if ( $args->echo ) {
    117 			echo $nav_menu;
    118 			return;
    119 		}
    120 
    121 		return $nav_menu;
    122 	}
    123 
    124 	// Get the nav menu based on the requested menu.
    125 	$menu = wp_get_nav_menu_object( $args->menu );
    126 
    127 	// Get the nav menu based on the theme_location.
    128 	$locations = get_nav_menu_locations();
    129 	if ( ! $menu && $args->theme_location && $locations && isset( $locations[ $args->theme_location ] ) ) {
    130 		$menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
    131 	}
    132 
    133 	// Get the first menu that has items if we still can't find a menu.
    134 	if ( ! $menu && ! $args->theme_location ) {
    135 		$menus = wp_get_nav_menus();
    136 		foreach ( $menus as $menu_maybe ) {
    137 			$menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) );
    138 			if ( $menu_items ) {
    139 				$menu = $menu_maybe;
    140 				break;
    141 			}
    142 		}
    143 	}
    144 
    145 	if ( empty( $args->menu ) ) {
    146 		$args->menu = $menu;
    147 	}
    148 
    149 	// If the menu exists, get its items.
    150 	if ( $menu && ! is_wp_error( $menu ) && ! isset( $menu_items ) ) {
    151 		$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
    152 	}
    153 
    154 	/*
    155 	 * If no menu was found:
    156 	 *  - Fall back (if one was specified), or bail.
    157 	 *
    158 	 * If no menu items were found:
    159 	 *  - Fall back, but only if no theme location was specified.
    160 	 *  - Otherwise, bail.
    161 	 */
    162 	if ( ( ! $menu || is_wp_error( $menu ) || ( isset( $menu_items ) && empty( $menu_items ) && ! $args->theme_location ) )
    163 		&& isset( $args->fallback_cb ) && $args->fallback_cb && is_callable( $args->fallback_cb ) ) {
    164 			return call_user_func( $args->fallback_cb, (array) $args );
    165 	}
    166 
    167 	if ( ! $menu || is_wp_error( $menu ) ) {
    168 		return false;
    169 	}
    170 
    171 	$nav_menu = '';
    172 	$items    = '';
    173 
    174 	$show_container = false;
    175 	if ( $args->container ) {
    176 		/**
    177 		 * Filters the list of HTML tags that are valid for use as menu containers.
    178 		 *
    179 		 * @since 3.0.0
    180 		 *
    181 		 * @param string[] $tags The acceptable HTML tags for use as menu containers.
    182 		 *                       Default is array containing 'div' and 'nav'.
    183 		 */
    184 		$allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav' ) );
    185 
    186 		if ( is_string( $args->container ) && in_array( $args->container, $allowed_tags, true ) ) {
    187 			$show_container = true;
    188 			$class          = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-' . $menu->slug . '-container"';
    189 			$id             = $args->container_id ? ' id="' . esc_attr( $args->container_id ) . '"' : '';
    190 			$aria_label     = ( 'nav' === $args->container && $args->container_aria_label ) ? ' aria-label="' . esc_attr( $args->container_aria_label ) . '"' : '';
    191 			$nav_menu      .= '<' . $args->container . $id . $class . $aria_label . '>';
    192 		}
    193 	}
    194 
    195 	// Set up the $menu_item variables.
    196 	_wp_menu_item_classes_by_context( $menu_items );
    197 
    198 	$sorted_menu_items        = array();
    199 	$menu_items_with_children = array();
    200 	foreach ( (array) $menu_items as $menu_item ) {
    201 		$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
    202 		if ( $menu_item->menu_item_parent ) {
    203 			$menu_items_with_children[ $menu_item->menu_item_parent ] = true;
    204 		}
    205 	}
    206 
    207 	// Add the menu-item-has-children class where applicable.
    208 	if ( $menu_items_with_children ) {
    209 		foreach ( $sorted_menu_items as &$menu_item ) {
    210 			if ( isset( $menu_items_with_children[ $menu_item->ID ] ) ) {
    211 				$menu_item->classes[] = 'menu-item-has-children';
    212 			}
    213 		}
    214 	}
    215 
    216 	unset( $menu_items, $menu_item );
    217 
    218 	/**
    219 	 * Filters the sorted list of menu item objects before generating the menu's HTML.
    220 	 *
    221 	 * @since 3.1.0
    222 	 *
    223 	 * @param array    $sorted_menu_items The menu items, sorted by each menu item's menu order.
    224 	 * @param stdClass $args              An object containing wp_nav_menu() arguments.
    225 	 */
    226 	$sorted_menu_items = apply_filters( 'wp_nav_menu_objects', $sorted_menu_items, $args );
    227 
    228 	$items .= walk_nav_menu_tree( $sorted_menu_items, $args->depth, $args );
    229 	unset( $sorted_menu_items );
    230 
    231 	// Attributes.
    232 	if ( ! empty( $args->menu_id ) ) {
    233 		$wrap_id = $args->menu_id;
    234 	} else {
    235 		$wrap_id = 'menu-' . $menu->slug;
    236 
    237 		while ( in_array( $wrap_id, $menu_id_slugs, true ) ) {
    238 			if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) ) {
    239 				$wrap_id = preg_replace( '#-(\d+)$#', '-' . ++$matches[1], $wrap_id );
    240 			} else {
    241 				$wrap_id = $wrap_id . '-1';
    242 			}
    243 		}
    244 	}
    245 	$menu_id_slugs[] = $wrap_id;
    246 
    247 	$wrap_class = $args->menu_class ? $args->menu_class : '';
    248 
    249 	/**
    250 	 * Filters the HTML list content for navigation menus.
    251 	 *
    252 	 * @since 3.0.0
    253 	 *
    254 	 * @see wp_nav_menu()
    255 	 *
    256 	 * @param string   $items The HTML list content for the menu items.
    257 	 * @param stdClass $args  An object containing wp_nav_menu() arguments.
    258 	 */
    259 	$items = apply_filters( 'wp_nav_menu_items', $items, $args );
    260 	/**
    261 	 * Filters the HTML list content for a specific navigation menu.
    262 	 *
    263 	 * @since 3.0.0
    264 	 *
    265 	 * @see wp_nav_menu()
    266 	 *
    267 	 * @param string   $items The HTML list content for the menu items.
    268 	 * @param stdClass $args  An object containing wp_nav_menu() arguments.
    269 	 */
    270 	$items = apply_filters( "wp_nav_menu_{$menu->slug}_items", $items, $args );
    271 
    272 	// Don't print any markup if there are no items at this point.
    273 	if ( empty( $items ) ) {
    274 		return false;
    275 	}
    276 
    277 	$nav_menu .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items );
    278 	unset( $items );
    279 
    280 	if ( $show_container ) {
    281 		$nav_menu .= '</' . $args->container . '>';
    282 	}
    283 
    284 	/**
    285 	 * Filters the HTML content for navigation menus.
    286 	 *
    287 	 * @since 3.0.0
    288 	 *
    289 	 * @see wp_nav_menu()
    290 	 *
    291 	 * @param string   $nav_menu The HTML content for the navigation menu.
    292 	 * @param stdClass $args     An object containing wp_nav_menu() arguments.
    293 	 */
    294 	$nav_menu = apply_filters( 'wp_nav_menu', $nav_menu, $args );
    295 
    296 	if ( $args->echo ) {
    297 		echo $nav_menu;
    298 	} else {
    299 		return $nav_menu;
    300 	}
    301 }
    302 
    303 /**
    304  * Adds the class property classes for the current context, if applicable.
    305  *
    306  * @access private
    307  * @since 3.0.0
    308  *
    309  * @global WP_Query   $wp_query   WordPress Query object.
    310  * @global WP_Rewrite $wp_rewrite WordPress rewrite component.
    311  *
    312  * @param array $menu_items The current menu item objects to which to add the class property information.
    313  */
    314 function _wp_menu_item_classes_by_context( &$menu_items ) {
    315 	global $wp_query, $wp_rewrite;
    316 
    317 	$queried_object    = $wp_query->get_queried_object();
    318 	$queried_object_id = (int) $wp_query->queried_object_id;
    319 
    320 	$active_object               = '';
    321 	$active_ancestor_item_ids    = array();
    322 	$active_parent_item_ids      = array();
    323 	$active_parent_object_ids    = array();
    324 	$possible_taxonomy_ancestors = array();
    325 	$possible_object_parents     = array();
    326 	$home_page_id                = (int) get_option( 'page_for_posts' );
    327 
    328 	if ( $wp_query->is_singular && ! empty( $queried_object->post_type ) && ! is_post_type_hierarchical( $queried_object->post_type ) ) {
    329 		foreach ( (array) get_object_taxonomies( $queried_object->post_type ) as $taxonomy ) {
    330 			if ( is_taxonomy_hierarchical( $taxonomy ) ) {
    331 				$term_hierarchy = _get_term_hierarchy( $taxonomy );
    332 				$terms          = wp_get_object_terms( $queried_object_id, $taxonomy, array( 'fields' => 'ids' ) );
    333 				if ( is_array( $terms ) ) {
    334 					$possible_object_parents = array_merge( $possible_object_parents, $terms );
    335 					$term_to_ancestor        = array();
    336 					foreach ( (array) $term_hierarchy as $anc => $descs ) {
    337 						foreach ( (array) $descs as $desc ) {
    338 							$term_to_ancestor[ $desc ] = $anc;
    339 						}
    340 					}
    341 
    342 					foreach ( $terms as $desc ) {
    343 						do {
    344 							$possible_taxonomy_ancestors[ $taxonomy ][] = $desc;
    345 							if ( isset( $term_to_ancestor[ $desc ] ) ) {
    346 								$_desc = $term_to_ancestor[ $desc ];
    347 								unset( $term_to_ancestor[ $desc ] );
    348 								$desc = $_desc;
    349 							} else {
    350 								$desc = 0;
    351 							}
    352 						} while ( ! empty( $desc ) );
    353 					}
    354 				}
    355 			}
    356 		}
    357 	} elseif ( ! empty( $queried_object->taxonomy ) && is_taxonomy_hierarchical( $queried_object->taxonomy ) ) {
    358 		$term_hierarchy   = _get_term_hierarchy( $queried_object->taxonomy );
    359 		$term_to_ancestor = array();
    360 		foreach ( (array) $term_hierarchy as $anc => $descs ) {
    361 			foreach ( (array) $descs as $desc ) {
    362 				$term_to_ancestor[ $desc ] = $anc;
    363 			}
    364 		}
    365 		$desc = $queried_object->term_id;
    366 		do {
    367 			$possible_taxonomy_ancestors[ $queried_object->taxonomy ][] = $desc;
    368 			if ( isset( $term_to_ancestor[ $desc ] ) ) {
    369 				$_desc = $term_to_ancestor[ $desc ];
    370 				unset( $term_to_ancestor[ $desc ] );
    371 				$desc = $_desc;
    372 			} else {
    373 				$desc = 0;
    374 			}
    375 		} while ( ! empty( $desc ) );
    376 	}
    377 
    378 	$possible_object_parents = array_filter( $possible_object_parents );
    379 
    380 	$front_page_url         = home_url();
    381 	$front_page_id          = (int) get_option( 'page_on_front' );
    382 	$privacy_policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
    383 
    384 	foreach ( (array) $menu_items as $key => $menu_item ) {
    385 
    386 		$menu_items[ $key ]->current = false;
    387 
    388 		$classes   = (array) $menu_item->classes;
    389 		$classes[] = 'menu-item';
    390 		$classes[] = 'menu-item-type-' . $menu_item->type;
    391 		$classes[] = 'menu-item-object-' . $menu_item->object;
    392 
    393 		// This menu item is set as the 'Front Page'.
    394 		if ( 'post_type' === $menu_item->type && $front_page_id === (int) $menu_item->object_id ) {
    395 			$classes[] = 'menu-item-home';
    396 		}
    397 
    398 		// This menu item is set as the 'Privacy Policy Page'.
    399 		if ( 'post_type' === $menu_item->type && $privacy_policy_page_id === (int) $menu_item->object_id ) {
    400 			$classes[] = 'menu-item-privacy-policy';
    401 		}
    402 
    403 		// If the menu item corresponds to a taxonomy term for the currently queried non-hierarchical post object.
    404 		if ( $wp_query->is_singular && 'taxonomy' === $menu_item->type
    405 			&& in_array( (int) $menu_item->object_id, $possible_object_parents, true )
    406 		) {
    407 			$active_parent_object_ids[] = (int) $menu_item->object_id;
    408 			$active_parent_item_ids[]   = (int) $menu_item->db_id;
    409 			$active_object              = $queried_object->post_type;
    410 
    411 			// If the menu item corresponds to the currently queried post or taxonomy object.
    412 		} elseif (
    413 			$menu_item->object_id == $queried_object_id
    414 			&& (
    415 				( ! empty( $home_page_id ) && 'post_type' === $menu_item->type
    416 					&& $wp_query->is_home && $home_page_id == $menu_item->object_id )
    417 				|| ( 'post_type' === $menu_item->type && $wp_query->is_singular )
    418 				|| ( 'taxonomy' === $menu_item->type
    419 					&& ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax )
    420 					&& $queried_object->taxonomy == $menu_item->object )
    421 			)
    422 		) {
    423 			$classes[]                   = 'current-menu-item';
    424 			$menu_items[ $key ]->current = true;
    425 			$_anc_id                     = (int) $menu_item->db_id;
    426 
    427 			while (
    428 				( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) )
    429 				&& ! in_array( $_anc_id, $active_ancestor_item_ids, true )
    430 			) {
    431 				$active_ancestor_item_ids[] = $_anc_id;
    432 			}
    433 
    434 			if ( 'post_type' === $menu_item->type && 'page' === $menu_item->object ) {
    435 				// Back compat classes for pages to match wp_page_menu().
    436 				$classes[] = 'page_item';
    437 				$classes[] = 'page-item-' . $menu_item->object_id;
    438 				$classes[] = 'current_page_item';
    439 			}
    440 
    441 			$active_parent_item_ids[]   = (int) $menu_item->menu_item_parent;
    442 			$active_parent_object_ids[] = (int) $menu_item->post_parent;
    443 			$active_object              = $menu_item->object;
    444 
    445 			// If the menu item corresponds to the currently queried post type archive.
    446 		} elseif (
    447 			'post_type_archive' === $menu_item->type
    448 			&& is_post_type_archive( array( $menu_item->object ) )
    449 		) {
    450 			$classes[]                   = 'current-menu-item';
    451 			$menu_items[ $key ]->current = true;
    452 			$_anc_id                     = (int) $menu_item->db_id;
    453 
    454 			while (
    455 				( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) )
    456 				&& ! in_array( $_anc_id, $active_ancestor_item_ids, true )
    457 			) {
    458 				$active_ancestor_item_ids[] = $_anc_id;
    459 			}
    460 
    461 			$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
    462 
    463 			// If the menu item corresponds to the currently requested URL.
    464 		} elseif ( 'custom' === $menu_item->object && isset( $_SERVER['HTTP_HOST'] ) ) {
    465 			$_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] );
    466 
    467 			// If it's the customize page then it will strip the query var off the URL before entering the comparison block.
    468 			if ( is_customize_preview() ) {
    469 				$_root_relative_current = strtok( untrailingslashit( $_SERVER['REQUEST_URI'] ), '?' );
    470 			}
    471 
    472 			$current_url        = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_root_relative_current );
    473 			$raw_item_url       = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url;
    474 			$item_url           = set_url_scheme( untrailingslashit( $raw_item_url ) );
    475 			$_indexless_current = untrailingslashit( preg_replace( '/' . preg_quote( $wp_rewrite->index, '/' ) . '$/', '', $current_url ) );
    476 
    477 			$matches = array(
    478 				$current_url,
    479 				urldecode( $current_url ),
    480 				$_indexless_current,
    481 				urldecode( $_indexless_current ),
    482 				$_root_relative_current,
    483 				urldecode( $_root_relative_current ),
    484 			);
    485 
    486 			if ( $raw_item_url && in_array( $item_url, $matches, true ) ) {
    487 				$classes[]                   = 'current-menu-item';
    488 				$menu_items[ $key ]->current = true;
    489 				$_anc_id                     = (int) $menu_item->db_id;
    490 
    491 				while (
    492 					( $_anc_id = (int) get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) )
    493 					&& ! in_array( $_anc_id, $active_ancestor_item_ids, true )
    494 				) {
    495 					$active_ancestor_item_ids[] = $_anc_id;
    496 				}
    497 
    498 				if ( in_array( home_url(), array( untrailingslashit( $current_url ), untrailingslashit( $_indexless_current ) ), true ) ) {
    499 					// Back compat for home link to match wp_page_menu().
    500 					$classes[] = 'current_page_item';
    501 				}
    502 				$active_parent_item_ids[]   = (int) $menu_item->menu_item_parent;
    503 				$active_parent_object_ids[] = (int) $menu_item->post_parent;
    504 				$active_object              = $menu_item->object;
    505 
    506 				// Give front page item the 'current-menu-item' class when extra query arguments are involved.
    507 			} elseif ( $item_url == $front_page_url && is_front_page() ) {
    508 				$classes[] = 'current-menu-item';
    509 			}
    510 
    511 			if ( untrailingslashit( $item_url ) == home_url() ) {
    512 				$classes[] = 'menu-item-home';
    513 			}
    514 		}
    515 
    516 		// Back-compat with wp_page_menu(): add "current_page_parent" to static home page link for any non-page query.
    517 		if ( ! empty( $home_page_id ) && 'post_type' === $menu_item->type
    518 			&& empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id
    519 		) {
    520 			$classes[] = 'current_page_parent';
    521 		}
    522 
    523 		$menu_items[ $key ]->classes = array_unique( $classes );
    524 	}
    525 	$active_ancestor_item_ids = array_filter( array_unique( $active_ancestor_item_ids ) );
    526 	$active_parent_item_ids   = array_filter( array_unique( $active_parent_item_ids ) );
    527 	$active_parent_object_ids = array_filter( array_unique( $active_parent_object_ids ) );
    528 
    529 	// Set parent's class.
    530 	foreach ( (array) $menu_items as $key => $parent_item ) {
    531 		$classes                                   = (array) $parent_item->classes;
    532 		$menu_items[ $key ]->current_item_ancestor = false;
    533 		$menu_items[ $key ]->current_item_parent   = false;
    534 
    535 		if (
    536 			isset( $parent_item->type )
    537 			&& (
    538 				// Ancestral post object.
    539 				(
    540 					'post_type' === $parent_item->type
    541 					&& ! empty( $queried_object->post_type )
    542 					&& is_post_type_hierarchical( $queried_object->post_type )
    543 					&& in_array( (int) $parent_item->object_id, $queried_object->ancestors, true )
    544 					&& $parent_item->object != $queried_object->ID
    545 				) ||
    546 
    547 				// Ancestral term.
    548 				(
    549 					'taxonomy' === $parent_item->type
    550 					&& isset( $possible_taxonomy_ancestors[ $parent_item->object ] )
    551 					&& in_array( (int) $parent_item->object_id, $possible_taxonomy_ancestors[ $parent_item->object ], true )
    552 					&& (
    553 						! isset( $queried_object->term_id ) ||
    554 						$parent_item->object_id != $queried_object->term_id
    555 					)
    556 				)
    557 			)
    558 		) {
    559 			if ( ! empty( $queried_object->taxonomy ) ) {
    560 				$classes[] = 'current-' . $queried_object->taxonomy . '-ancestor';
    561 			} else {
    562 				$classes[] = 'current-' . $queried_object->post_type . '-ancestor';
    563 			}
    564 		}
    565 
    566 		if ( in_array( (int) $parent_item->db_id, $active_ancestor_item_ids, true ) ) {
    567 			$classes[] = 'current-menu-ancestor';
    568 
    569 			$menu_items[ $key ]->current_item_ancestor = true;
    570 		}
    571 		if ( in_array( (int) $parent_item->db_id, $active_parent_item_ids, true ) ) {
    572 			$classes[] = 'current-menu-parent';
    573 
    574 			$menu_items[ $key ]->current_item_parent = true;
    575 		}
    576 		if ( in_array( (int) $parent_item->object_id, $active_parent_object_ids, true ) ) {
    577 			$classes[] = 'current-' . $active_object . '-parent';
    578 		}
    579 
    580 		if ( 'post_type' === $parent_item->type && 'page' === $parent_item->object ) {
    581 			// Back compat classes for pages to match wp_page_menu().
    582 			if ( in_array( 'current-menu-parent', $classes, true ) ) {
    583 				$classes[] = 'current_page_parent';
    584 			}
    585 			if ( in_array( 'current-menu-ancestor', $classes, true ) ) {
    586 				$classes[] = 'current_page_ancestor';
    587 			}
    588 		}
    589 
    590 		$menu_items[ $key ]->classes = array_unique( $classes );
    591 	}
    592 }
    593 
    594 /**
    595  * Retrieves the HTML list content for nav menu items.
    596  *
    597  * @uses Walker_Nav_Menu to create HTML list content.
    598  * @since 3.0.0
    599  *
    600  * @param array    $items The menu items, sorted by each menu item's menu order.
    601  * @param int      $depth Depth of the item in reference to parents.
    602  * @param stdClass $r     An object containing wp_nav_menu() arguments.
    603  * @return string The HTML list content for the menu items.
    604  */
    605 function walk_nav_menu_tree( $items, $depth, $r ) {
    606 	$walker = ( empty( $r->walker ) ) ? new Walker_Nav_Menu : $r->walker;
    607 
    608 	return $walker->walk( $items, $depth, $r );
    609 }
    610 
    611 /**
    612  * Prevents a menu item ID from being used more than once.
    613  *
    614  * @since 3.0.1
    615  * @access private
    616  *
    617  * @param string $id
    618  * @param object $item
    619  * @return string
    620  */
    621 function _nav_menu_item_id_use_once( $id, $item ) {
    622 	static $_used_ids = array();
    623 
    624 	if ( in_array( $item->ID, $_used_ids, true ) ) {
    625 		return '';
    626 	}
    627 
    628 	$_used_ids[] = $item->ID;
    629 
    630 	return $id;
    631 }