[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-walker.php (source)

   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  }


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref