1<?php
2/**
3 * Taxonomy API: Walker_Category class
4 *
5 * @package WordPress
6 * @subpackage Template
7 * @since 4.4.0
8 */
9
10/**
11 * Core class used to create an HTML list of categories.
12 *
13 * @since 2.1.0
14 *
15 * @see Walker
16 */
17class Walker_Category extends Walker {
18
19	/**
20	 * What the class handles.
21	 *
22	 * @since 2.1.0
23	 * @var string
24	 *
25	 * @see Walker::$tree_type
26	 */
27	public $tree_type = 'category';
28
29	/**
30	 * Database fields to use.
31	 *
32	 * @since 2.1.0
33	 * @var array
34	 *
35	 * @see Walker::$db_fields
36	 * @todo Decouple this
37	 */
38	public $db_fields = array(
39		'parent' => 'parent',
40		'id'     => 'term_id',
41	);
42
43	/**
44	 * Starts the list before the elements are added.
45	 *
46	 * @since 2.1.0
47	 *
48	 * @see Walker::start_lvl()
49	 *
50	 * @param string $output Used to append additional content. Passed by reference.
51	 * @param int    $depth  Optional. Depth of category. Used for tab indentation. Default 0.
52	 * @param array  $args   Optional. An array of arguments. Will only append content if style argument
53	 *                       value is 'list'. See wp_list_categories(). Default empty array.
54	 */
55	public function start_lvl( &$output, $depth = 0, $args = array() ) {
56		if ( 'list' !== $args['style'] ) {
57			return;
58		}
59
60		$indent  = str_repeat( "\t", $depth );
61		$output .= "$indent<ul class='children'>\n";
62	}
63
64	/**
65	 * Ends the list of after the elements are added.
66	 *
67	 * @since 2.1.0
68	 *
69	 * @see Walker::end_lvl()
70	 *
71	 * @param string $output Used to append additional content. Passed by reference.
72	 * @param int    $depth  Optional. Depth of category. Used for tab indentation. Default 0.
73	 * @param array  $args   Optional. An array of arguments. Will only append content if style argument
74	 *                       value is 'list'. See wp_list_categories(). Default empty array.
75	 */
76	public function end_lvl( &$output, $depth = 0, $args = array() ) {
77		if ( 'list' !== $args['style'] ) {
78			return;
79		}
80
81		$indent  = str_repeat( "\t", $depth );
82		$output .= "$indent</ul>\n";
83	}
84
85	/**
86	 * Starts the element output.
87	 *
88	 * @since 2.1.0
89	 *
90	 * @see Walker::start_el()
91	 *
92	 * @param string  $output   Used to append additional content (passed by reference).
93	 * @param WP_Term $category Category data object.
94	 * @param int     $depth    Optional. Depth of category in reference to parents. Default 0.
95	 * @param array   $args     Optional. An array of arguments. See wp_list_categories(). Default empty array.
96	 * @param int     $id       Optional. ID of the current category. Default 0.
97	 */
98	public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
99		/** This filter is documented in wp-includes/category-template.php */
100		$cat_name = apply_filters( 'list_cats', esc_attr( $category->name ), $category );
101
102		// Don't generate an element if the category name is empty.
103		if ( '' === $cat_name ) {
104			return;
105		}
106
107		$atts         = array();
108		$atts['href'] = get_term_link( $category );
109
110		if ( $args['use_desc_for_title'] && ! empty( $category->description ) ) {
111			/**
112			 * Filters the category description for display.
113			 *
114			 * @since 1.2.0
115			 *
116			 * @param string  $description Category description.
117			 * @param WP_Term $category    Category object.
118			 */
119			$atts['title'] = strip_tags( apply_filters( 'category_description', $category->description, $category ) );
120		}
121
122		/**
123		 * Filters the HTML attributes applied to a category list item's anchor element.
124		 *
125		 * @since 5.2.0
126		 *
127		 * @param array   $atts {
128		 *     The HTML attributes applied to the list item's `<a>` element, empty strings are ignored.
129		 *
130		 *     @type string $href  The href attribute.
131		 *     @type string $title The title attribute.
132		 * }
133		 * @param WP_Term $category Term data object.
134		 * @param int     $depth    Depth of category, used for padding.
135		 * @param array   $args     An array of arguments.
136		 * @param int     $id       ID of the current category.
137		 */
138		$atts = apply_filters( 'category_list_link_attributes', $atts, $category, $depth, $args, $id );
139
140		$attributes = '';
141		foreach ( $atts as $attr => $value ) {
142			if ( is_scalar( $value ) && '' !== $value && false !== $value ) {
143				$value       = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
144				$attributes .= ' ' . $attr . '="' . $value . '"';
145			}
146		}
147
148		$link = sprintf(
149			'<a%s>%s</a>',
150			$attributes,
151			$cat_name
152		);
153
154		if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) {
155			$link .= ' ';
156
157			if ( empty( $args['feed_image'] ) ) {
158				$link .= '(';
159			}
160
161			$link .= '<a href="' . esc_url( get_term_feed_link( $category->term_id, $category->taxonomy, $args['feed_type'] ) ) . '"';
162
163			if ( empty( $args['feed'] ) ) {
164				/* translators: %s: Category name. */
165				$alt = ' alt="' . sprintf( __( 'Feed for all posts filed under %s' ), $cat_name ) . '"';
166			} else {
167				$alt   = ' alt="' . $args['feed'] . '"';
168				$name  = $args['feed'];
169				$link .= empty( $args['title'] ) ? '' : $args['title'];
170			}
171
172			$link .= '>';
173
174			if ( empty( $args['feed_image'] ) ) {
175				$link .= $name;
176			} else {
177				$link .= "<img src='" . esc_url( $args['feed_image'] ) . "'$alt" . ' />';
178			}
179			$link .= '</a>';
180
181			if ( empty( $args['feed_image'] ) ) {
182				$link .= ')';
183			}
184		}
185
186		if ( ! empty( $args['show_count'] ) ) {
187			$link .= ' (' . number_format_i18n( $category->count ) . ')';
188		}
189		if ( 'list' === $args['style'] ) {
190			$output     .= "\t<li";
191			$css_classes = array(
192				'cat-item',
193				'cat-item-' . $category->term_id,
194			);
195
196			if ( ! empty( $args['current_category'] ) ) {
197				// 'current_category' can be an array, so we use `get_terms()`.
198				$_current_terms = get_terms(
199					array(
200						'taxonomy'   => $category->taxonomy,
201						'include'    => $args['current_category'],
202						'hide_empty' => false,
203					)
204				);
205
206				foreach ( $_current_terms as $_current_term ) {
207					if ( $category->term_id == $_current_term->term_id ) {
208						$css_classes[] = 'current-cat';
209						$link          = str_replace( '<a', '<a aria-current="page"', $link );
210					} elseif ( $category->term_id == $_current_term->parent ) {
211						$css_classes[] = 'current-cat-parent';
212					}
213					while ( $_current_term->parent ) {
214						if ( $category->term_id == $_current_term->parent ) {
215							$css_classes[] = 'current-cat-ancestor';
216							break;
217						}
218						$_current_term = get_term( $_current_term->parent, $category->taxonomy );
219					}
220				}
221			}
222
223			/**
224			 * Filters the list of CSS classes to include with each category in the list.
225			 *
226			 * @since 4.2.0
227			 *
228			 * @see wp_list_categories()
229			 *
230			 * @param string[] $css_classes An array of CSS classes to be applied to each list item.
231			 * @param WP_Term  $category    Category data object.
232			 * @param int      $depth       Depth of page, used for padding.
233			 * @param array    $args        An array of wp_list_categories() arguments.
234			 */
235			$css_classes = implode( ' ', apply_filters( 'category_css_class', $css_classes, $category, $depth, $args ) );
236			$css_classes = $css_classes ? ' class="' . esc_attr( $css_classes ) . '"' : '';
237
238			$output .= $css_classes;
239			$output .= ">$link\n";
240		} elseif ( isset( $args['separator'] ) ) {
241			$output .= "\t$link" . $args['separator'] . "\n";
242		} else {
243			$output .= "\t$link<br />\n";
244		}
245	}
246
247	/**
248	 * Ends the element output, if needed.
249	 *
250	 * @since 2.1.0
251	 *
252	 * @see Walker::end_el()
253	 *
254	 * @param string $output Used to append additional content (passed by reference).
255	 * @param object $page   Not used.
256	 * @param int    $depth  Optional. Depth of category. Not used.
257	 * @param array  $args   Optional. An array of arguments. Only uses 'list' for whether should append
258	 *                       to output. See wp_list_categories(). Default empty array.
259	 */
260	public function end_el( &$output, $page, $depth = 0, $args = array() ) {
261		if ( 'list' !== $args['style'] ) {
262			return;
263		}
264
265		$output .= "</li>\n";
266	}
267
268}
269