[ 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 $id_field = $this->db_fields['id']; 139 $id = $element->$id_field; 140 141 // Display this element. 142 $this->has_children = ! empty( $children_elements[ $id ] ); 143 if ( isset( $args[0] ) && is_array( $args[0] ) ) { 144 $args[0]['has_children'] = $this->has_children; // Back-compat. 145 } 146 147 $this->start_el( $output, $element, $depth, ...array_values( $args ) ); 148 149 // Descend only when the depth is right and there are children for this element. 150 if ( ( 0 === $max_depth || $max_depth > $depth + 1 ) && isset( $children_elements[ $id ] ) ) { 151 152 foreach ( $children_elements[ $id ] as $child ) { 153 154 if ( ! isset( $newlevel ) ) { 155 $newlevel = true; 156 // Start the child delimiter. 157 $this->start_lvl( $output, $depth, ...array_values( $args ) ); 158 } 159 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output ); 160 } 161 unset( $children_elements[ $id ] ); 162 } 163 164 if ( isset( $newlevel ) && $newlevel ) { 165 // End the child delimiter. 166 $this->end_lvl( $output, $depth, ...array_values( $args ) ); 167 } 168 169 // End this element. 170 $this->end_el( $output, $element, $depth, ...array_values( $args ) ); 171 } 172 173 /** 174 * Displays array of elements hierarchically. 175 * 176 * Does not assume any existing order of elements. 177 * 178 * $max_depth = -1 means flatly display every element. 179 * $max_depth = 0 means display all levels. 180 * $max_depth > 0 specifies the number of display levels. 181 * 182 * @since 2.1.0 183 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 184 * to the function signature. 185 * 186 * @param array $elements An array of elements. 187 * @param int $max_depth The maximum hierarchical depth. 188 * @param mixed ...$args Optional additional arguments. 189 * @return string The hierarchical item output. 190 */ 191 public function walk( $elements, $max_depth, ...$args ) { 192 $output = ''; 193 194 // Invalid parameter or nothing to walk. 195 if ( $max_depth < -1 || empty( $elements ) ) { 196 return $output; 197 } 198 199 $parent_field = $this->db_fields['parent']; 200 201 // Flat display. 202 if ( -1 === $max_depth ) { 203 $empty_array = array(); 204 foreach ( $elements as $e ) { 205 $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 206 } 207 return $output; 208 } 209 210 /* 211 * Need to display in hierarchical order. 212 * Separate elements into two buckets: top level and children elements. 213 * Children_elements is two dimensional array. Example: 214 * Children_elements[10][] contains all sub-elements whose parent is 10. 215 */ 216 $top_level_elements = array(); 217 $children_elements = array(); 218 foreach ( $elements as $e ) { 219 if ( empty( $e->$parent_field ) ) { 220 $top_level_elements[] = $e; 221 } else { 222 $children_elements[ $e->$parent_field ][] = $e; 223 } 224 } 225 226 /* 227 * When none of the elements is top level. 228 * Assume the first one must be root of the sub elements. 229 */ 230 if ( empty( $top_level_elements ) ) { 231 232 $first = array_slice( $elements, 0, 1 ); 233 $root = $first[0]; 234 235 $top_level_elements = array(); 236 $children_elements = array(); 237 foreach ( $elements as $e ) { 238 if ( $root->$parent_field === $e->$parent_field ) { 239 $top_level_elements[] = $e; 240 } else { 241 $children_elements[ $e->$parent_field ][] = $e; 242 } 243 } 244 } 245 246 foreach ( $top_level_elements as $e ) { 247 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 248 } 249 250 /* 251 * If we are displaying all levels, and remaining children_elements is not empty, 252 * then we got orphans, which should be displayed regardless. 253 */ 254 if ( ( 0 === $max_depth ) && count( $children_elements ) > 0 ) { 255 $empty_array = array(); 256 foreach ( $children_elements as $orphans ) { 257 foreach ( $orphans as $op ) { 258 $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 259 } 260 } 261 } 262 263 return $output; 264 } 265 266 /** 267 * Produces a page of nested elements. 268 * 269 * Given an array of hierarchical elements, the maximum depth, a specific page number, 270 * and number of elements per page, this function first determines all top level root elements 271 * belonging to that page, then lists them and all of their children in hierarchical order. 272 * 273 * $max_depth = 0 means display all levels. 274 * $max_depth > 0 specifies the number of display levels. 275 * 276 * @since 2.7.0 277 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it 278 * to the function signature. 279 * 280 * @param array $elements An array of elements. 281 * @param int $max_depth The maximum hierarchical depth. 282 * @param int $page_num The specific page number, beginning with 1. 283 * @param int $per_page Number of elements per page. 284 * @param mixed ...$args Optional additional arguments. 285 * @return string XHTML of the specified page of elements. 286 */ 287 public function paged_walk( $elements, $max_depth, $page_num, $per_page, ...$args ) { 288 if ( empty( $elements ) || $max_depth < -1 ) { 289 return ''; 290 } 291 292 $output = ''; 293 294 $parent_field = $this->db_fields['parent']; 295 296 $count = -1; 297 if ( -1 === $max_depth ) { 298 $total_top = count( $elements ); 299 } 300 if ( $page_num < 1 || $per_page < 0 ) { 301 // No paging. 302 $paging = false; 303 $start = 0; 304 if ( -1 === $max_depth ) { 305 $end = $total_top; 306 } 307 $this->max_pages = 1; 308 } else { 309 $paging = true; 310 $start = ( (int) $page_num - 1 ) * (int) $per_page; 311 $end = $start + $per_page; 312 if ( -1 === $max_depth ) { 313 $this->max_pages = (int) ceil( $total_top / $per_page ); 314 } 315 } 316 317 // Flat display. 318 if ( -1 === $max_depth ) { 319 if ( ! empty( $args[0]['reverse_top_level'] ) ) { 320 $elements = array_reverse( $elements ); 321 $oldstart = $start; 322 $start = $total_top - $end; 323 $end = $total_top - $oldstart; 324 } 325 326 $empty_array = array(); 327 foreach ( $elements as $e ) { 328 ++$count; 329 if ( $count < $start ) { 330 continue; 331 } 332 if ( $count >= $end ) { 333 break; 334 } 335 $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 336 } 337 return $output; 338 } 339 340 /* 341 * Separate elements into two buckets: top level and children elements. 342 * Children_elements is two dimensional array, e.g. 343 * $children_elements[10][] contains all sub-elements whose parent is 10. 344 */ 345 $top_level_elements = array(); 346 $children_elements = array(); 347 foreach ( $elements as $e ) { 348 if ( empty( $e->$parent_field ) ) { 349 $top_level_elements[] = $e; 350 } else { 351 $children_elements[ $e->$parent_field ][] = $e; 352 } 353 } 354 355 $total_top = count( $top_level_elements ); 356 if ( $paging ) { 357 $this->max_pages = (int) ceil( $total_top / $per_page ); 358 } else { 359 $end = $total_top; 360 } 361 362 if ( ! empty( $args[0]['reverse_top_level'] ) ) { 363 $top_level_elements = array_reverse( $top_level_elements ); 364 $oldstart = $start; 365 $start = $total_top - $end; 366 $end = $total_top - $oldstart; 367 } 368 if ( ! empty( $args[0]['reverse_children'] ) ) { 369 foreach ( $children_elements as $parent => $children ) { 370 $children_elements[ $parent ] = array_reverse( $children ); 371 } 372 } 373 374 foreach ( $top_level_elements as $e ) { 375 ++$count; 376 377 // For the last page, need to unset earlier children in order to keep track of orphans. 378 if ( $end >= $total_top && $count < $start ) { 379 $this->unset_children( $e, $children_elements ); 380 } 381 382 if ( $count < $start ) { 383 continue; 384 } 385 386 if ( $count >= $end ) { 387 break; 388 } 389 390 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 391 } 392 393 if ( $end >= $total_top && count( $children_elements ) > 0 ) { 394 $empty_array = array(); 395 foreach ( $children_elements as $orphans ) { 396 foreach ( $orphans as $op ) { 397 $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 398 } 399 } 400 } 401 402 return $output; 403 } 404 405 /** 406 * Calculates the total number of root elements. 407 * 408 * @since 2.7.0 409 * 410 * @param array $elements Elements to list. 411 * @return int Number of root elements. 412 */ 413 public function get_number_of_root_elements( $elements ) { 414 $num = 0; 415 $parent_field = $this->db_fields['parent']; 416 417 foreach ( $elements as $e ) { 418 if ( empty( $e->$parent_field ) ) { 419 ++$num; 420 } 421 } 422 return $num; 423 } 424 425 /** 426 * Unsets all the children for a given top level element. 427 * 428 * @since 2.7.0 429 * 430 * @param object $element The top level element. 431 * @param array $children_elements The children elements. 432 */ 433 public function unset_children( $element, &$children_elements ) { 434 if ( ! $element || ! $children_elements ) { 435 return; 436 } 437 438 $id_field = $this->db_fields['id']; 439 $id = $element->$id_field; 440 441 if ( ! empty( $children_elements[ $id ] ) && is_array( $children_elements[ $id ] ) ) { 442 foreach ( (array) $children_elements[ $id ] as $child ) { 443 $this->unset_children( $child, $children_elements ); 444 } 445 } 446 447 unset( $children_elements[ $id ] ); 448 } 449 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Apr 25 08:20:02 2024 | Cross-referenced by PHPXref |