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