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 string[]
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 * @since 5.9.0 Renamed `$category` to `$data_object` and `$id` to `$current_object_id`
90 * to match parent class for PHP 8 named parameter support.
91 *
92 * @see Walker::start_el()
93 *
94 * @param string $output Used to append additional content (passed by reference).
95 * @param WP_Term $data_object Category data object.
96 * @param int $depth Optional. Depth of category in reference to parents. Default 0.
97 * @param array $args Optional. An array of arguments. See wp_list_categories().
98 * Default empty array.
99 * @param int $current_object_id Optional. ID of the current category. Default 0.
100 */
101 public function start_el( &$output, $data_object, $depth = 0, $args = array(), $current_object_id = 0 ) {
102 // Restores the more descriptive, specific name for use within this method.
103 $category = $data_object;
104
105 /** This filter is documented in wp-includes/category-template.php */
106 $cat_name = apply_filters( 'list_cats', esc_attr( $category->name ), $category );
107
108 // Don't generate an element if the category name is empty.
109 if ( '' === $cat_name ) {
110 return;
111 }
112
113 $atts = array();
114 $atts['href'] = get_term_link( $category );
115
116 if ( $args['use_desc_for_title'] && ! empty( $category->description ) ) {
117 /**
118 * Filters the category description for display.
119 *
120 * @since 1.2.0
121 *
122 * @param string $description Category description.
123 * @param WP_Term $category Category object.
124 */
125 $atts['title'] = strip_tags( apply_filters( 'category_description', $category->description, $category ) );
126 }
127
128 /**
129 * Filters the HTML attributes applied to a category list item's anchor element.
130 *
131 * @since 5.2.0
132 *
133 * @param array $atts {
134 * The HTML attributes applied to the list item's `<a>` element, empty strings are ignored.
135 *
136 * @type string $href The href attribute.
137 * @type string $title The title attribute.
138 * }
139 * @param WP_Term $category Term data object.
140 * @param int $depth Depth of category, used for padding.
141 * @param array $args An array of arguments.
142 * @param int $current_object_id ID of the current category.
143 */
144 $atts = apply_filters( 'category_list_link_attributes', $atts, $category, $depth, $args, $current_object_id );
145
146 $attributes = '';
147 foreach ( $atts as $attr => $value ) {
148 if ( is_scalar( $value ) && '' !== $value && false !== $value ) {
149 $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
150 $attributes .= ' ' . $attr . '="' . $value . '"';
151 }
152 }
153
154 $link = sprintf(
155 '<a%s>%s</a>',
156 $attributes,
157 $cat_name
158 );
159
160 if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) {
161 $link .= ' ';
162
163 if ( empty( $args['feed_image'] ) ) {
164 $link .= '(';
165 }
166
167 $link .= '<a href="' . esc_url( get_term_feed_link( $category, $category->taxonomy, $args['feed_type'] ) ) . '"';
168
169 if ( empty( $args['feed'] ) ) {
170 /* translators: %s: Category name. */
171 $alt = ' alt="' . sprintf( __( 'Feed for all posts filed under %s' ), $cat_name ) . '"';
172 } else {
173 $alt = ' alt="' . $args['feed'] . '"';
174 $name = $args['feed'];
175 $link .= empty( $args['title'] ) ? '' : $args['title'];
176 }
177
178 $link .= '>';
179
180 if ( empty( $args['feed_image'] ) ) {
181 $link .= $name;
182 } else {
183 $link .= "<img src='" . esc_url( $args['feed_image'] ) . "'$alt" . ' />';
184 }
185
186 $link .= '</a>';
187
188 if ( empty( $args['feed_image'] ) ) {
189 $link .= ')';
190 }
191 }
192
193 if ( ! empty( $args['show_count'] ) ) {
194 $link .= ' (' . number_format_i18n( $category->count ) . ')';
195 }
196
197 if ( 'list' === $args['style'] ) {
198 $output .= "\t<li";
199 $css_classes = array(
200 'cat-item',
201 'cat-item-' . $category->term_id,
202 );
203
204 if ( ! empty( $args['current_category'] ) ) {
205 // 'current_category' can be an array, so we use `get_terms()`.
206 $_current_terms = get_terms(
207 array(
208 'taxonomy' => $category->taxonomy,
209 'include' => $args['current_category'],
210 'hide_empty' => false,
211 )
212 );
213
214 foreach ( $_current_terms as $_current_term ) {
215 if ( $category->term_id === $_current_term->term_id ) {
216 $css_classes[] = 'current-cat';
217 $link = str_replace( '<a', '<a aria-current="page"', $link );
218 } elseif ( $category->term_id === $_current_term->parent ) {
219 $css_classes[] = 'current-cat-parent';
220 }
221
222 while ( $_current_term->parent ) {
223 if ( $category->term_id === $_current_term->parent ) {
224 $css_classes[] = 'current-cat-ancestor';
225 break;
226 }
227
228 $_current_term = get_term( $_current_term->parent, $category->taxonomy );
229 }
230 }
231 }
232
233 /**
234 * Filters the list of CSS classes to include with each category in the list.
235 *
236 * @since 4.2.0
237 *
238 * @see wp_list_categories()
239 *
240 * @param string[] $css_classes An array of CSS classes to be applied to each list item.
241 * @param WP_Term $category Category data object.
242 * @param int $depth Depth of page, used for padding.
243 * @param array $args An array of wp_list_categories() arguments.
244 */
245 $css_classes = implode( ' ', apply_filters( 'category_css_class', $css_classes, $category, $depth, $args ) );
246 $css_classes = $css_classes ? ' class="' . esc_attr( $css_classes ) . '"' : '';
247
248 $output .= $css_classes;
249 $output .= ">$link\n";
250 } elseif ( isset( $args['separator'] ) ) {
251 $output .= "\t$link" . $args['separator'] . "\n";
252 } else {
253 $output .= "\t$link<br />\n";
254 }
255 }
256
257 /**
258 * Ends the element output, if needed.
259 *
260 * @since 2.1.0
261 * @since 5.9.0 Renamed `$page` to `$data_object` to match parent class for PHP 8 named parameter support.
262 *
263 * @see Walker::end_el()
264 *
265 * @param string $output Used to append additional content (passed by reference).
266 * @param object $data_object Category data object. Not used.
267 * @param int $depth Optional. Depth of category. Not used.
268 * @param array $args Optional. An array of arguments. Only uses 'list' for whether should
269 * append to output. See wp_list_categories(). Default empty array.
270 */
271 public function end_el( &$output, $data_object, $depth = 0, $args = array() ) {
272 if ( 'list' !== $args['style'] ) {
273 return;
274 }
275
276 $output .= "</li>\n";
277 }
278}
279