[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * A class for displaying various tree-like structures. 4 * 5 * Extend the Walker class to use it, see examples below. Child classes 6 * do not need to implement all of the abstract methods in the class. The child 7 * only needs to implement the methods that are needed. 8 * 9 * @since 2.1.0 10 * 11 * @package WordPress 12 * @abstract 13 */ 14 class Walker { 15 /** 16 * What the class handles. 17 * 18 * @since 2.1.0 19 * @var string 20 */ 21 public $tree_type; 22 23 /** 24 * DB fields to use. 25 * 26 * @since 2.1.0 27 * @var string[] 28 */ 29 public $db_fields; 30 31 /** 32 * Max number of pages walked by the paged walker. 33 * 34 * @since 2.7.0 35 * @var int 36 */ 37 public $max_pages = 1; 38 39 /** 40 * Whether the current element has children or not. 41 * 42 * To be used in start_el(). 43 * 44 * @since 4.0.0 45 * @var bool 46 */ 47 public $has_children; 48 49 /** 50 * Starts the list before the elements are added. 51 * 52 * The $args parameter holds additional values that may be used with the child 53 * class methods. This method is called at the start of the output list. 54 * 55 * @since 2.1.0 56 * @abstract 57 * 58 * @param string $output Used to append additional content (passed by reference). 59 * @param int $depth Depth of the item. 60 * @param array $args An array of additional arguments. 61 */ 62 public function start_lvl( &$output, $depth = 0, $args = array() ) {} 63 64 /** 65 * Ends the list of after the elements are added. 66 * 67 * The $args parameter holds additional values that may be used with the child 68 * class methods. This method finishes the list at the end of output of the elements. 69 * 70 * @since 2.1.0 71 * @abstract 72 * 73 * @param string $output Used to append additional content (passed by reference). 74 * @param int $depth Depth of the item. 75 * @param array $args An array of additional arguments. 76 */ 77 public function end_lvl( &$output, $depth = 0, $args = array() ) {} 78 79 /** 80 * Starts the element output. 81 * 82 * The $args parameter holds additional values that may be used with the child 83 * class methods. Also includes the element output. 84 * 85 * @since 2.1.0 86 * @since 5.9.0 Renamed `$object` (a PHP reserved keyword) to `$data_object` for PHP 8 named parameter support. 87 * @abstract 88 * 89 * @param string $output Used to append additional content (passed by reference). 90 * @param object $data_object The data object. 91 * @param int $depth Depth of the item. 92 * @param array $args An array of additional arguments. 93 * @param int $current_object_id Optional. ID of the current item. Default 0. 94 */ 95 public function start_el( &$output, $data_object, $depth = 0, $args = array(), $current_object_id = 0 ) {} 96 97 /** 98 * Ends the element output, if needed. 99 * 100 * The $args parameter holds additional values that may be used with the child class methods. 101 * 102 * @since 2.1.0 103 * @since 5.9.0 Renamed `$object` (a PHP reserved keyword) to `$data_object` for PHP 8 named parameter support. 104 * @abstract 105 * 106 * @param string $output Used to append additional content (passed by reference). 107 * @param object $data_object The data object. 108 * @param int $depth Depth of the item. 109 * @param array $args An array of additional arguments. 110 */ 111 public function end_el( &$output, $data_object, $depth = 0, $args = array() ) {} 112 113 /** 114 * Traverses elements to create list from elements. 115 * 116 * Display one element if the element doesn't have any children otherwise, 117 * display the element and its children. Will only traverse up to the max 118 * depth and no ignore elements under that depth. It is possible to set the 119 * max depth to include all depths, see walk() method. 120 * 121 * This method should not be called directly, use the walk() method instead. 122 * 123 * @since 2.5.0 124 * 125 * @param object $element Data object. 126 * @param array $children_elements List of elements to continue traversing (passed by reference). 127 * @param int $max_depth Max depth to traverse. 128 * @param int $depth Depth of current element. 129 * @param array $args An array of arguments. 130 * @param string $output Used to append additional content (passed by reference). 131 */ 132 public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) { 133 if ( ! $element ) { 134 return; 135 } 136 137 $id_field = $this->db_fields['id']; 138 $id = $element->$id_field; 139 140 // Display this element. 141 $this->has_children = ! empty( $children_elements[ $id ] ); 142 if ( isset( $args[0] ) && is_array( $args[0] ) ) { 143 $args[0]['has_children'] = $this->has_children; // Back-compat. 144 } 145 146 $this->start_el( $output, $element, $depth, ...array_values( $args ) ); 147 148 // Descend only when the depth is right and there are children for this element. 149 if ( ( 0 == $max_depth || $max_depth > $depth + 1 ) && isset( $children_elements[ $id ] ) ) { 150 151 foreach ( $children_elements[ $id ] as $child ) { 152 153 if ( ! isset( $newlevel ) ) { 154 $newlevel = true; 155 // Start the child delimiter. 156 $this->start_lvl( $output, $depth, ...array_values( $args ) ); 157 } 158 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output ); 159 } 160 unset( $children_elements[ $id ] ); 161 } 162 163 if ( isset( $newlevel ) && $newlevel ) { 164 // End the child delimiter. 165 $this->end_lvl( $output, $depth, ...array_values( $args ) ); 166 } 167 168 // End this element. 169 $this->end_el( $output, $element, $depth, ...array_values( $args ) ); 170 } 171 172 /** 173 * Displays array of elements hierarchically. 174 * 175 * Does not assume any existing order of elements. 176 * 177 * $max_depth = -1 means flatly display every element. 178 * $max_depth = 0 means display all levels. 179 * $max_depth > 0 specifies the number of display levels. 180 * 181 * @since 2.1.0 182 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 183 * to the function signature. 184 * 185 * @param array $elements An array of elements. 186 * @param int $max_depth The maximum hierarchical depth. 187 * @param mixed ...$args Optional additional arguments. 188 * @return string The hierarchical item output. 189 */ 190 public function walk( $elements, $max_depth, ...$args ) { 191 $output = ''; 192 193 // Invalid parameter or nothing to walk. 194 if ( $max_depth < -1 || empty( $elements ) ) { 195 return $output; 196 } 197 198 $parent_field = $this->db_fields['parent']; 199 200 // Flat display. 201 if ( -1 == $max_depth ) { 202 $empty_array = array(); 203 foreach ( $elements as $e ) { 204 $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 205 } 206 return $output; 207 } 208 209 /* 210 * Need to display in hierarchical order. 211 * Separate elements into two buckets: top level and children elements. 212 * Children_elements is two dimensional array. Example: 213 * Children_elements[10][] contains all sub-elements whose parent is 10. 214 */ 215 $top_level_elements = array(); 216 $children_elements = array(); 217 foreach ( $elements as $e ) { 218 if ( empty( $e->$parent_field ) ) { 219 $top_level_elements[] = $e; 220 } else { 221 $children_elements[ $e->$parent_field ][] = $e; 222 } 223 } 224 225 /* 226 * When none of the elements is top level. 227 * Assume the first one must be root of the sub elements. 228 */ 229 if ( empty( $top_level_elements ) ) { 230 231 $first = array_slice( $elements, 0, 1 ); 232 $root = $first[0]; 233 234 $top_level_elements = array(); 235 $children_elements = array(); 236 foreach ( $elements as $e ) { 237 if ( $root->$parent_field == $e->$parent_field ) { 238 $top_level_elements[] = $e; 239 } else { 240 $children_elements[ $e->$parent_field ][] = $e; 241 } 242 } 243 } 244 245 foreach ( $top_level_elements as $e ) { 246 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 247 } 248 249 /* 250 * If we are displaying all levels, and remaining children_elements is not empty, 251 * then we got orphans, which should be displayed regardless. 252 */ 253 if ( ( 0 == $max_depth ) && count( $children_elements ) > 0 ) { 254 $empty_array = array(); 255 foreach ( $children_elements as $orphans ) { 256 foreach ( $orphans as $op ) { 257 $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 258 } 259 } 260 } 261 262 return $output; 263 } 264 265 /** 266 * Produces a page of nested elements. 267 * 268 * Given an array of hierarchical elements, the maximum depth, a specific page number, 269 * and number of elements per page, this function first determines all top level root elements 270 * belonging to that page, then lists them and all of their children in hierarchical order. 271 * 272 * $max_depth = 0 means display all levels. 273 * $max_depth > 0 specifies the number of display levels. 274 * 275 * @since 2.7.0 276 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 277 * to the function signature. 278 * 279 * @param array $elements An array of elements. 280 * @param int $max_depth The maximum hierarchical depth. 281 * @param int $page_num The specific page number, beginning with 1. 282 * @param int $per_page Number of elements per page. 283 * @param mixed ...$args Optional additional arguments. 284 * @return string XHTML of the specified page of elements. 285 */ 286 public function paged_walk( $elements, $max_depth, $page_num, $per_page, ...$args ) { 287 if ( empty( $elements ) || $max_depth < -1 ) { 288 return ''; 289 } 290 291 $output = ''; 292 293 $parent_field = $this->db_fields['parent']; 294 295 $count = -1; 296 if ( -1 == $max_depth ) { 297 $total_top = count( $elements ); 298 } 299 if ( $page_num < 1 || $per_page < 0 ) { 300 // No paging. 301 $paging = false; 302 $start = 0; 303 if ( -1 == $max_depth ) { 304 $end = $total_top; 305 } 306 $this->max_pages = 1; 307 } else { 308 $paging = true; 309 $start = ( (int) $page_num - 1 ) * (int) $per_page; 310 $end = $start + $per_page; 311 if ( -1 == $max_depth ) { 312 $this->max_pages = ceil( $total_top / $per_page ); 313 } 314 } 315 316 // Flat display. 317 if ( -1 == $max_depth ) { 318 if ( ! empty( $args[0]['reverse_top_level'] ) ) { 319 $elements = array_reverse( $elements ); 320 $oldstart = $start; 321 $start = $total_top - $end; 322 $end = $total_top - $oldstart; 323 } 324 325 $empty_array = array(); 326 foreach ( $elements as $e ) { 327 $count++; 328 if ( $count < $start ) { 329 continue; 330 } 331 if ( $count >= $end ) { 332 break; 333 } 334 $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 335 } 336 return $output; 337 } 338 339 /* 340 * Separate elements into two buckets: top level and children elements. 341 * Children_elements is two dimensional array, e.g. 342 * $children_elements[10][] contains all sub-elements whose parent is 10. 343 */ 344 $top_level_elements = array(); 345 $children_elements = array(); 346 foreach ( $elements as $e ) { 347 if ( empty( $e->$parent_field ) ) { 348 $top_level_elements[] = $e; 349 } else { 350 $children_elements[ $e->$parent_field ][] = $e; 351 } 352 } 353 354 $total_top = count( $top_level_elements ); 355 if ( $paging ) { 356 $this->max_pages = ceil( $total_top / $per_page ); 357 } else { 358 $end = $total_top; 359 } 360 361 if ( ! empty( $args[0]['reverse_top_level'] ) ) { 362 $top_level_elements = array_reverse( $top_level_elements ); 363 $oldstart = $start; 364 $start = $total_top - $end; 365 $end = $total_top - $oldstart; 366 } 367 if ( ! empty( $args[0]['reverse_children'] ) ) { 368 foreach ( $children_elements as $parent => $children ) { 369 $children_elements[ $parent ] = array_reverse( $children ); 370 } 371 } 372 373 foreach ( $top_level_elements as $e ) { 374 $count++; 375 376 // For the last page, need to unset earlier children in order to keep track of orphans. 377 if ( $end >= $total_top && $count < $start ) { 378 $this->unset_children( $e, $children_elements ); 379 } 380 381 if ( $count < $start ) { 382 continue; 383 } 384 385 if ( $count >= $end ) { 386 break; 387 } 388 389 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 390 } 391 392 if ( $end >= $total_top && count( $children_elements ) > 0 ) { 393 $empty_array = array(); 394 foreach ( $children_elements as $orphans ) { 395 foreach ( $orphans as $op ) { 396 $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 397 } 398 } 399 } 400 401 return $output; 402 } 403 404 /** 405 * Calculates the total number of root elements. 406 * 407 * @since 2.7.0 408 * 409 * @param array $elements Elements to list. 410 * @return int Number of root elements. 411 */ 412 public function get_number_of_root_elements( $elements ) { 413 $num = 0; 414 $parent_field = $this->db_fields['parent']; 415 416 foreach ( $elements as $e ) { 417 if ( empty( $e->$parent_field ) ) { 418 $num++; 419 } 420 } 421 return $num; 422 } 423 424 /** 425 * Unsets all the children for a given top level element. 426 * 427 * @since 2.7.0 428 * 429 * @param object $element The top level element. 430 * @param array $children_elements The children elements. 431 */ 432 public function unset_children( $element, &$children_elements ) { 433 if ( ! $element || ! $children_elements ) { 434 return; 435 } 436 437 $id_field = $this->db_fields['id']; 438 $id = $element->$id_field; 439 440 if ( ! empty( $children_elements[ $id ] ) && is_array( $children_elements[ $id ] ) ) { 441 foreach ( (array) $children_elements[ $id ] as $child ) { 442 $this->unset_children( $child, $children_elements ); 443 } 444 } 445 446 unset( $children_elements[ $id ] ); 447 } 448 449 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |