[ Index ]

PHP Cross Reference of WordPress

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  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  }


Generated: Fri Apr 26 01:00:03 2024 Cross-referenced by PHPXref 0.7.1