Completed
Pull Request — master (#381)
by
unknown
01:46
created

class-wp-bootstrap-navwalker.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * WP Bootstrap Navwalker
4
 *
5
 * @package WP-Bootstrap-Navwalker
6
 */
7
8
/*
9
 * Class Name: WP_Bootstrap_Navwalker
10
 * Plugin Name: WP Bootstrap Navwalker
11
 * Plugin URI:  https://github.com/wp-bootstrap/wp-bootstrap-navwalker
12
 * Description: A custom WordPress nav walker class to implement the Bootstrap 4 navigation style in a custom theme using the WordPress built in menu manager.
13
 * Author: Edward McIntyre - @twittem, WP Bootstrap, William Patton - @pattonwebz
14
 * Version: 4.1.0
15
 * Author URI: https://github.com/wp-bootstrap
16
 * GitHub Plugin URI: https://github.com/wp-bootstrap/wp-bootstrap-navwalker
17
 * GitHub Branch: master
18
 * License: GPL-3.0+
19
 * License URI: http://www.gnu.org/licenses/gpl-3.0.txt
20
*/
21
22
/* Check if Class Exists. */
23
if ( ! class_exists( 'WP_Bootstrap_Navwalker' ) ) {
24
	/**
25
	 * WP_Bootstrap_Navwalker class.
26
	 *
27
	 * @extends Walker_Nav_Menu
28
	 */
29
	class WP_Bootstrap_Navwalker extends Walker_Nav_Menu {
30
31
		/**
32
		 * Whether the items_wrap contains schema microdata or not.
33
		 *
34
		 * @since 4.2.0
35
		 * @var type bool
36
		 */
37
		private $has_schema = false;
38
39
		/**
40
		 * Ensure the items_wrap argument contains microdata.
41
		 *
42
		 * @since 4.2.0
43
		 */
44
		public function __construct() {
45
			if ( ! has_filter( 'wp_nav_menu_args', [ $this, 'add_schema_to_navbar_ul' ] ) ) {
46
				add_filter( 'wp_nav_menu_args',  [ $this, 'add_schema_to_navbar_ul' ] );
47
			}
48
		}
49
50
		/**
51
		 * Starts the list before the elements are added.
52
		 *
53
		 * @since WP 3.0.0
54
		 *
55
		 * @see Walker_Nav_Menu::start_lvl()
56
		 *
57
		 * @param string   $output Used to append additional content (passed by reference).
58
		 * @param int      $depth  Depth of menu item. Used for padding.
59
		 * @param stdClass $args   An object of wp_nav_menu() arguments.
60
		 */
61
		public function start_lvl( &$output, $depth = 0, $args = array() ) {
62 View Code Duplication
			if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
63
				$t = '';
64
				$n = '';
65
			} else {
66
				$t = "\t";
67
				$n = "\n";
68
			}
69
			$indent = str_repeat( $t, $depth );
70
			// Default class to add to the file.
71
			$classes = array( 'dropdown-menu' );
72
			/**
73
			 * Filters the CSS class(es) applied to a menu list element.
74
			 *
75
			 * @since WP 4.8.0
76
			 *
77
			 * @param array    $classes The CSS classes that are applied to the menu `<ul>` element.
78
			 * @param stdClass $args    An object of `wp_nav_menu()` arguments.
79
			 * @param int      $depth   Depth of menu item. Used for padding.
80
			 */
81
			$class_names = join( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
82
			$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
83
			/**
84
			 * The `.dropdown-menu` container needs to have a labelledby
85
			 * attribute which points to it's trigger link.
86
			 *
87
			 * Form a string for the labelledby attribute from the the latest
88
			 * link with an id that was added to the $output.
89
			 */
90
			$labelledby = '';
91
			// find all links with an id in the output.
92
			preg_match_all( '/(<a.*?id=\"|\')(.*?)\"|\'.*?>/im', $output, $matches );
93
			// with pointer at end of array check if we got an ID match.
94
			if ( end( $matches[2] ) ) {
95
				// build a string to use as aria-labelledby.
96
				$labelledby = 'aria-labelledby="' . end( $matches[2] ) . '"';
97
			}
98
			$output .= "{$n}{$indent}<ul$class_names $labelledby role=\"menu\">{$n}";
99
		}
100
101
		/**
102
		 * Starts the element output.
103
		 *
104
		 * @since WP 3.0.0
105
		 * @since WP 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
106
		 *
107
		 * @see Walker_Nav_Menu::start_el()
108
		 *
109
		 * @param string   $output Used to append additional content (passed by reference).
110
		 * @param WP_Post  $item   Menu item data object.
111
		 * @param int      $depth  Depth of menu item. Used for padding.
112
		 * @param stdClass $args   An object of wp_nav_menu() arguments.
113
		 * @param int      $id     Current item ID.
114
		 */
115
		public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
116 View Code Duplication
			if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
117
				$t = '';
118
				$n = '';
119
			} else {
120
				$t = "\t";
121
				$n = "\n";
122
			}
123
			$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';
124
125
			if ( false !== strpos( $args->items_wrap, 'itemscope' ) && false === $this->has_schema ) {
126
				$this->has_schema  = true;
0 ignored issues
show
Documentation Bug introduced by
It seems like true of type boolean is incompatible with the declared type object<type> of property $has_schema.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
127
				$args->link_before = '<span itemprop="name">' . $args->link_before;
128
				$args->link_after .= '</span>';
129
			}
130
131
			$classes = empty( $item->classes ) ? array() : (array) $item->classes;
132
133
			// Initialize some holder variables to store specially handled item
134
			// wrappers and icons.
135
			$linkmod_classes = array();
136
			$icon_classes    = array();
137
138
			/**
139
			 * Get an updated $classes array without linkmod or icon classes.
140
			 *
141
			 * NOTE: linkmod and icon class arrays are passed by reference and
142
			 * are maybe modified before being used later in this function.
143
			 */
144
			$classes = self::seporate_linkmods_and_icons_from_classes( $classes, $linkmod_classes, $icon_classes, $depth );
145
146
			// Join any icon classes plucked from $classes into a string.
147
			$icon_class_string = join( ' ', $icon_classes );
148
149
			/**
150
			 * Filters the arguments for a single nav menu item.
151
			 *
152
			 *  WP 4.4.0
153
			 *
154
			 * @param stdClass $args  An object of wp_nav_menu() arguments.
155
			 * @param WP_Post  $item  Menu item data object.
156
			 * @param int      $depth Depth of menu item. Used for padding.
157
			 */
158
			$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );
159
160
			// Add .dropdown or .active classes where they are needed.
161
			if ( isset( $args->has_children ) && $args->has_children ) {
162
				$classes[] = 'dropdown';
163
			}
164
			if ( in_array( 'current-menu-item', $classes, true ) || in_array( 'current-menu-parent', $classes, true ) ) {
165
				$classes[] = 'active';
166
			}
167
168
			// Add some additional default classes to the item.
169
			$classes[] = 'menu-item-' . $item->ID;
170
			$classes[] = 'nav-item';
171
172
			// Allow filtering the classes.
173
			$classes = apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth );
174
175
			// Form a string of classes in format: class="class_names".
176
			$class_names = join( ' ', $classes );
177
			$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
178
179
			/**
180
			 * Filters the ID applied to a menu item's list item element.
181
			 *
182
			 * @since WP 3.0.1
183
			 * @since WP 4.1.0 The `$depth` parameter was added.
184
			 *
185
			 * @param string   $menu_id The ID that is applied to the menu item's `<li>` element.
186
			 * @param WP_Post  $item    The current menu item.
187
			 * @param stdClass $args    An object of wp_nav_menu() arguments.
188
			 * @param int      $depth   Depth of menu item. Used for padding.
189
			 */
190
			$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth );
191
			$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
192
193
			$output .= $indent . '<li ' . $id . $class_names . '>';
194
195
			// initialize array for holding the $atts for the link item.
196
			$atts = array();
197
198
			// Set title from item to the $atts array - if title is empty then
199
			// default to item title.
200
			if ( empty( $item->attr_title ) ) {
201
				$atts['title'] = ! empty( $item->title ) ? strip_tags( $item->title ) : '';
202
			} else {
203
				$atts['title'] = $item->attr_title;
204
			}
205
206
			$atts['target'] = ! empty( $item->target ) ? $item->target : '';
207
			$atts['rel']    = ! empty( $item->xfn ) ? $item->xfn : '';
208
			// If item has_children add atts to <a>.
209
			if ( isset( $args->has_children ) && $args->has_children && 0 === $depth && $args->depth > 1 ) {
210
				$atts['href']          = '#';
211
				$atts['data-toggle']   = 'dropdown';
212
				$atts['aria-haspopup'] = 'true';
213
				$atts['aria-expanded'] = 'false';
214
				$atts['class']         = 'dropdown-toggle nav-link';
215
				$atts['id']            = 'menu-item-dropdown-' . $item->ID;
216
			} else {
217
				if ( $this->has_schema === true ) {
218
					$atts['itemprop'] = 'url';
219
				}
220
221
				$atts['href'] = ! empty( $item->url ) ? $item->url : '#';
222
				// Items in dropdowns use .dropdown-item instead of .nav-link.
223
				if ( $depth > 0 ) {
224
					$atts['class'] = 'dropdown-item';
225
				} else {
226
					$atts['class'] = 'nav-link';
227
				}
228
			}
229
230
			// update atts of this item based on any custom linkmod classes.
231
			$atts = self::update_atts_for_linkmod_type( $atts, $linkmod_classes );
232
233
			// Allow filtering of the $atts array before using it.
234
			$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
235
236
			// Build a string of html containing all the atts for the item.
237
			$attributes = '';
238
			foreach ( $atts as $attr => $value ) {
239
				if ( ! empty( $value ) ) {
240
					$value       = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
241
					$attributes .= ' ' . $attr . '="' . $value . '"';
242
				}
243
			}
244
245
			/**
246
			 * Set a typeflag to easily test if this is a linkmod or not.
247
			 */
248
			$linkmod_type = self::get_linkmod_type( $linkmod_classes );
249
250
			/**
251
			 * START appending the internal item contents to the output.
252
			 */
253
			$item_output = isset( $args->before ) ? $args->before : '';
254
			/**
255
			 * This is the start of the internal nav item. Depending on what
256
			 * kind of linkmod we have we may need different wrapper elements.
257
			 */
258
			if ( '' !== $linkmod_type ) {
259
				// is linkmod, output the required element opener.
260
				$item_output .= self::linkmod_element_open( $linkmod_type, $attributes );
261
			} else {
262
				// With no link mod type set this must be a standard <a> tag.
263
				$item_output .= '<a' . $attributes . '>';
264
			}
265
266
			/**
267
			 * Initiate empty icon var, then if we have a string containing any
268
			 * icon classes form the icon markup with an <i> element. This is
269
			 * output inside of the item before the $title (the link text).
270
			 */
271
			$icon_html = '';
272
			if ( ! empty( $icon_class_string ) ) {
273
				// append an <i> with the icon classes to what is output before links.
274
				$icon_html = '<i class="' . esc_attr( $icon_class_string ) . '" aria-hidden="true"></i> ';
275
			}
276
277
			/** This filter is documented in wp-includes/post-template.php */
278
			$title = apply_filters( 'the_title', $item->title, $item->ID );
279
280
			/**
281
			 * Filters a menu item's title.
282
			 *
283
			 * @since WP 4.4.0
284
			 *
285
			 * @param string   $title The menu item's title.
286
			 * @param WP_Post  $item  The current menu item.
287
			 * @param stdClass $args  An object of wp_nav_menu() arguments.
288
			 * @param int      $depth Depth of menu item. Used for padding.
289
			 */
290
			$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
291
292
			/**
293
			 * If the .sr-only class was set apply to the nav items text only.
294
			 */
295
			if ( in_array( 'sr-only', $linkmod_classes, true ) ) {
296
				$title         = self::wrap_for_screen_reader( $title );
297
				$keys_to_unset = array_keys( $linkmod_classes, 'sr-only' );
298
				foreach ( $keys_to_unset as $k ) {
299
					unset( $linkmod_classes[ $k ] );
300
				}
301
			}
302
303
			// Put the item contents into $output.
304
			$item_output .= isset( $args->link_before ) ? $args->link_before . $icon_html . $title . $args->link_after : '';
305
			/**
306
			 * This is the end of the internal nav item. We need to close the
307
			 * correct element depending on the type of link or link mod.
308
			 */
309
			if ( '' !== $linkmod_type ) {
310
				// is linkmod, output the required element opener.
311
				$item_output .= self::linkmod_element_close( $linkmod_type, $attributes );
312
			} else {
313
				// With no link mod type set this must be a standard <a> tag.
314
				$item_output .= '</a>';
315
			}
316
317
			$item_output .= isset( $args->after ) ? $args->after : '';
318
319
			/**
320
			 * END appending the internal item contents to the output.
321
			 */
322
			$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
323
324
		}
325
326
		/**
327
		 * Traverse elements to create list from elements.
328
		 *
329
		 * Display one element if the element doesn't have any children otherwise,
330
		 * display the element and its children. Will only traverse up to the max
331
		 * depth and no ignore elements under that depth. It is possible to set the
332
		 * max depth to include all depths, see walk() method.
333
		 *
334
		 * This method should not be called directly, use the walk() method instead.
335
		 *
336
		 * @since WP 2.5.0
337
		 *
338
		 * @see Walker::start_lvl()
339
		 *
340
		 * @param object $element           Data object.
341
		 * @param array  $children_elements List of elements to continue traversing (passed by reference).
342
		 * @param int    $max_depth         Max depth to traverse.
343
		 * @param int    $depth             Depth of current element.
344
		 * @param array  $args              An array of arguments.
345
		 * @param string $output            Used to append additional content (passed by reference).
346
		 */
347
		public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
348
			if ( ! $element ) {
349
				return; }
350
			$id_field = $this->db_fields['id'];
351
			// Display this element.
352
			if ( is_object( $args[0] ) ) {
353
				$args[0]->has_children = ! empty( $children_elements[ $element->$id_field ] ); }
354
			parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
355
		}
356
357
		/**
358
		 * Menu Fallback
359
		 * =============
360
		 * If this function is assigned to the wp_nav_menu's fallback_cb variable
361
		 * and a menu has not been assigned to the theme location in the WordPress
362
		 * menu manager the function with display nothing to a non-logged in user,
363
		 * and will add a link to the WordPress menu manager if logged in as an admin.
364
		 *
365
		 * @param array $args passed from the wp_nav_menu function.
366
		 */
367
		public static function fallback( $args ) {
368
			if ( current_user_can( 'edit_theme_options' ) ) {
369
370
				/* Get Arguments. */
371
				$container       = $args['container'];
372
				$container_id    = $args['container_id'];
373
				$container_class = $args['container_class'];
374
				$menu_class      = $args['menu_class'];
375
				$menu_id         = $args['menu_id'];
376
377
				// initialize var to store fallback html.
378
				$fallback_output = '';
379
380
				if ( $container ) {
381
					$fallback_output .= '<' . esc_attr( $container );
382
					if ( $container_id ) {
383
						$fallback_output .= ' id="' . esc_attr( $container_id ) . '"';
384
					}
385
					if ( $container_class ) {
386
						$fallback_output .= ' class="' . esc_attr( $container_class ) . '"';
387
					}
388
					$fallback_output .= '>';
389
				}
390
				$fallback_output .= '<ul';
391
				if ( $menu_id ) {
392
					$fallback_output .= ' id="' . esc_attr( $menu_id ) . '"'; }
393
				if ( $menu_class ) {
394
					$fallback_output .= ' class="' . esc_attr( $menu_class ) . '"'; }
395
				$fallback_output .= '>';
396
				$fallback_output .= '<li><a href="' . esc_url( admin_url( 'nav-menus.php' ) ) . '" title="' . esc_attr__( 'Add a menu', 'wp-bootstrap-navwalker' ) . '">' . esc_html__( 'Add a menu', 'wp-bootstrap-navwalker' ) . '</a></li>';
397
				$fallback_output .= '</ul>';
398
				if ( $container ) {
399
					$fallback_output .= '</' . esc_attr( $container ) . '>';
400
				}
401
402
				// if $args has 'echo' key and it's true echo, otherwise return.
403
				if ( array_key_exists( 'echo', $args ) && $args['echo'] ) {
404
					echo $fallback_output; // WPCS: XSS OK.
405
				} else {
406
					return $fallback_output;
407
				}
408
			}
409
		}
410
411
		/**
412
		 * Filter to ensure the items_Wrap argument contains microdata.
413
		 *
414
		 * @since 4.2.0
415
		 *
416
		 * @param  array $args The nav instance arguments.
417
		 * @return array $args The altered nav instance arguments.
418
		 */
419
		public function add_schema_to_navbar_ul( $args ) {
420
			$wrap = $args['items_wrap'];
421
			if ( strpos( $wrap, 'SiteNavigationElement' ) === false ) {
422
				$args['items_wrap'] = preg_replace( '/(>).*>?\%3\$s/', ' itemscope itemtype="http://www.schema.org/SiteNavigationElement"'."$0", $wrap );
423
			}
424
425
			return $args;
426
		}
427
428
		/**
429
		 * Find any custom linkmod or icon classes and store in their holder
430
		 * arrays then remove them from the main classes array.
431
		 *
432
		 * Supported linkmods: .disabled, .dropdown-header, .dropdown-divider, .sr-only
433
		 * Supported iconsets: Font Awesome 4/5, Glypicons
434
		 *
435
		 * NOTE: This accepts the linkmod and icon arrays by reference.
436
		 *
437
		 * @since 4.0.0
438
		 *
439
		 * @param array   $classes         an array of classes currently assigned to the item.
440
		 * @param array   $linkmod_classes an array to hold linkmod classes.
441
		 * @param array   $icon_classes    an array to hold icon classes.
442
		 * @param integer $depth           an integer holding current depth level.
443
		 *
444
		 * @return array  $classes         a maybe modified array of classnames.
445
		 */
446
		private function seporate_linkmods_and_icons_from_classes( $classes, &$linkmod_classes, &$icon_classes, $depth ) {
447
			// Loop through $classes array to find linkmod or icon classes.
448
			foreach ( $classes as $key => $class ) {
449
				// If any special classes are found, store the class in it's
450
				// holder array and and unset the item from $classes.
451
				if ( preg_match( '/^disabled|^sr-only/i', $class ) ) {
452
					// Test for .disabled or .sr-only classes.
453
					$linkmod_classes[] = $class;
454
					unset( $classes[ $key ] );
455
				} elseif ( preg_match( '/^dropdown-header|^dropdown-divider|^dropdown-item-text/i', $class ) && $depth > 0 ) {
456
					// Test for .dropdown-header or .dropdown-divider and a
457
					// depth greater than 0 - IE inside a dropdown.
458
					$linkmod_classes[] = $class;
459
					unset( $classes[ $key ] );
460
				} elseif ( preg_match( '/^fa-(\S*)?|^fa(s|r|l|b)?(\s?)?$/i', $class ) ) {
461
					// Font Awesome.
462
					$icon_classes[] = $class;
463
					unset( $classes[ $key ] );
464
				} elseif ( preg_match( '/^glyphicon-(\S*)?|^glyphicon(\s?)$/i', $class ) ) {
465
					// Glyphicons.
466
					$icon_classes[] = $class;
467
					unset( $classes[ $key ] );
468
				}
469
			}
470
471
			return $classes;
472
		}
473
474
		/**
475
		 * Return a string containing a linkmod type and update $atts array
476
		 * accordingly depending on the decided.
477
		 *
478
		 * @since 4.0.0
479
		 *
480
		 * @param array $linkmod_classes array of any link modifier classes.
481
		 *
482
		 * @return string                empty for default, a linkmod type string otherwise.
483
		 */
484
		private function get_linkmod_type( $linkmod_classes = array() ) {
485
			$linkmod_type = '';
486
			// Loop through array of linkmod classes to handle their $atts.
487
			if ( ! empty( $linkmod_classes ) ) {
488
				foreach ( $linkmod_classes as $link_class ) {
489
					if ( ! empty( $link_class ) ) {
490
491
						// check for special class types and set a flag for them.
492
						if ( 'dropdown-header' === $link_class ) {
493
							$linkmod_type = 'dropdown-header';
494
						} elseif ( 'dropdown-divider' === $link_class ) {
495
							$linkmod_type = 'dropdown-divider';
496
						} elseif ( 'dropdown-item-text' === $link_class ) {
497
							$linkmod_type = 'dropdown-item-text';
498
						}
499
					}
500
				}
501
			}
502
			return $linkmod_type;
503
		}
504
505
		/**
506
		 * Update the attributes of a nav item depending on the limkmod classes.
507
		 *
508
		 * @since 4.0.0
509
		 *
510
		 * @param array $atts            array of atts for the current link in nav item.
511
		 * @param array $linkmod_classes an array of classes that modify link or nav item behaviors or displays.
512
		 *
513
		 * @return array                 maybe updated array of attributes for item.
514
		 */
515
		private function update_atts_for_linkmod_type( $atts = array(), $linkmod_classes = array() ) {
516
			if ( ! empty( $linkmod_classes ) ) {
517
				foreach ( $linkmod_classes as $link_class ) {
518
					if ( ! empty( $link_class ) ) {
519
						// update $atts with a space and the extra classname...
520
						// so long as it's not a sr-only class.
521
						if ( 'sr-only' !== $link_class ) {
522
							$atts['class'] .= ' ' . esc_attr( $link_class );
523
						}
524
						// check for special class types we need additional handling for.
525
						if ( 'disabled' === $link_class ) {
526
							// Convert link to '#' and unset open targets.
527
							$atts['href'] = '#';
528
							unset( $atts['target'] );
529
						} elseif ( 'dropdown-header' === $link_class || 'dropdown-divider' === $link_class || 'dropdown-item-text' === $link_class ) {
530
							// Store a type flag and unset href and target.
531
							unset( $atts['href'] );
532
							unset( $atts['target'] );
533
						}
534
					}
535
				}
536
			}
537
			return $atts;
538
		}
539
540
		/**
541
		 * Wraps the passed text in a screen reader only class.
542
		 *
543
		 * @since 4.0.0
544
		 *
545
		 * @param string $text the string of text to be wrapped in a screen reader class.
546
		 * @return string      the string wrapped in a span with the class.
547
		 */
548
		private function wrap_for_screen_reader( $text = '' ) {
549
			if ( $text ) {
550
				$text = '<span class="sr-only">' . $text . '</span>';
551
			}
552
			return $text;
553
		}
554
555
		/**
556
		 * Returns the correct opening element and attributes for a linkmod.
557
		 *
558
		 * @since 4.0.0
559
		 *
560
		 * @param string $linkmod_type a sting containing a linkmod type flag.
561
		 * @param string $attributes   a string of attributes to add to the element.
562
		 *
563
		 * @return string              a string with the openign tag for the element with attribibutes added.
564
		 */
565
		private function linkmod_element_open( $linkmod_type, $attributes = '' ) {
566
			$output = '';
567
			if ( 'dropdown-item-text' === $linkmod_type ) {
568
				$output .= '<span class="dropdown-item-text"' . $attributes . '>';
569
			} elseif ( 'dropdown-header' === $linkmod_type ) {
570
				// For a header use a span with the .h6 class instead of a real
571
				// header tag so that it doesn't confuse screen readers.
572
				$output .= '<span class="dropdown-header h6"' . $attributes . '>';
573
			} elseif ( 'dropdown-divider' === $linkmod_type ) {
574
				// this is a divider.
575
				$output .= '<div class="dropdown-divider"' . $attributes . '>';
576
			}
577
			return $output;
578
		}
579
580
		/**
581
		 * Return the correct closing tag for the linkmod element.
582
		 *
583
		 * @since 4.0.0
584
		 *
585
		 * @param string $linkmod_type a string containing a special linkmod type.
586
		 *
587
		 * @return string              a string with the closing tag for this linkmod type.
588
		 */
589
		private function linkmod_element_close( $linkmod_type ) {
590
			$output = '';
591
			if ( 'dropdown-header' === $linkmod_type || 'dropdown-item-text' === $linkmod_type ) {
592
				// For a header use a span with the .h6 class instead of a real
593
				// header tag so that it doesn't confuse screen readers.
594
				$output .= '</span>';
595
			} elseif ( 'dropdown-divider' === $linkmod_type ) {
596
				// this is a divider.
597
				$output .= '</div>';
598
			}
599
			return $output;
600
		}
601
	}
602
}
603