[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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 array
  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       * Start the element output.
  81       *
  82       * The $args parameter holds additional values that may be used with the child
  83       * class methods. Includes the element output also.
  84       *
  85       * @since 2.1.0
  86       * @abstract
  87       *
  88       * @param string $output            Used to append additional content (passed by reference).
  89       * @param object $object            The data object.
  90       * @param int    $depth             Depth of the item.
  91       * @param array  $args              An array of additional arguments.
  92       * @param int    $current_object_id ID of the current item.
  93       */
  94  	public function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {}
  95  
  96      /**
  97       * Ends the element output, if needed.
  98       *
  99       * The $args parameter holds additional values that may be used with the child class methods.
 100       *
 101       * @since 2.1.0
 102       * @abstract
 103       *
 104       * @param string $output Used to append additional content (passed by reference).
 105       * @param object $object The data object.
 106       * @param int    $depth  Depth of the item.
 107       * @param array  $args   An array of additional arguments.
 108       */
 109  	public function end_el( &$output, $object, $depth = 0, $args = array() ) {}
 110  
 111      /**
 112       * Traverse elements to create list from elements.
 113       *
 114       * Display one element if the element doesn't have any children otherwise,
 115       * display the element and its children. Will only traverse up to the max
 116       * depth and no ignore elements under that depth. It is possible to set the
 117       * max depth to include all depths, see walk() method.
 118       *
 119       * This method should not be called directly, use the walk() method instead.
 120       *
 121       * @since 2.5.0
 122       *
 123       * @param object $element           Data object.
 124       * @param array  $children_elements List of elements to continue traversing (passed by reference).
 125       * @param int    $max_depth         Max depth to traverse.
 126       * @param int    $depth             Depth of current element.
 127       * @param array  $args              An array of arguments.
 128       * @param string $output            Used to append additional content (passed by reference).
 129       */
 130  	public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
 131          if ( ! $element ) {
 132              return;
 133          }
 134  
 135          $id_field = $this->db_fields['id'];
 136          $id       = $element->$id_field;
 137  
 138          //display this element
 139          $this->has_children = ! empty( $children_elements[ $id ] );
 140          if ( isset( $args[0] ) && is_array( $args[0] ) ) {
 141              $args[0]['has_children'] = $this->has_children; // Back-compat.
 142          }
 143  
 144          $cb_args = array_merge( array( &$output, $element, $depth ), $args );
 145          call_user_func_array( array( $this, 'start_el' ), $cb_args );
 146  
 147          // descend only when the depth is right and there are childrens for this element
 148          if ( ( $max_depth == 0 || $max_depth > $depth + 1 ) && isset( $children_elements[ $id ] ) ) {
 149  
 150              foreach ( $children_elements[ $id ] as $child ) {
 151  
 152                  if ( ! isset( $newlevel ) ) {
 153                      $newlevel = true;
 154                      //start the child delimiter
 155                      $cb_args = array_merge( array( &$output, $depth ), $args );
 156                      call_user_func_array( array( $this, 'start_lvl' ), $cb_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              $cb_args = array_merge( array( &$output, $depth ), $args );
 166              call_user_func_array( array( $this, 'end_lvl' ), $cb_args );
 167          }
 168  
 169          //end this element
 170          $cb_args = array_merge( array( &$output, $element, $depth ), $args );
 171          call_user_func_array( array( $this, 'end_el' ), $cb_args );
 172      }
 173  
 174      /**
 175       * Display array of elements hierarchically.
 176       *
 177       * Does not assume any existing order of elements.
 178       *
 179       * $max_depth = -1 means flatly display every element.
 180       * $max_depth = 0 means display all levels.
 181       * $max_depth > 0 specifies the number of display levels.
 182       *
 183       * @since 2.1.0
 184       *
 185       * @param array $elements  An array of elements.
 186       * @param int   $max_depth The maximum hierarchical depth.
 187       * @return string The hierarchical item output.
 188       */
 189  	public function walk( $elements, $max_depth ) {
 190          $args   = array_slice( func_get_args(), 2 );
 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, eg.
 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 ( ( $max_depth == 0 ) && 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       * paged_walk() - produce 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       *
 277       * @param array $elements
 278       * @param int   $max_depth The maximum hierarchical depth.
 279       * @param int   $page_num The specific page number, beginning with 1.
 280       * @param int   $per_page
 281       * @return string XHTML of the specified page of elements
 282       */
 283  	public function paged_walk( $elements, $max_depth, $page_num, $per_page ) {
 284          if ( empty( $elements ) || $max_depth < -1 ) {
 285              return '';
 286          }
 287  
 288          $args   = array_slice( func_get_args(), 4 );
 289          $output = '';
 290  
 291          $parent_field = $this->db_fields['parent'];
 292  
 293          $count = -1;
 294          if ( -1 == $max_depth ) {
 295              $total_top = count( $elements );
 296          }
 297          if ( $page_num < 1 || $per_page < 0 ) {
 298              // No paging
 299              $paging = false;
 300              $start  = 0;
 301              if ( -1 == $max_depth ) {
 302                  $end = $total_top;
 303              }
 304              $this->max_pages = 1;
 305          } else {
 306              $paging = true;
 307              $start  = ( (int) $page_num - 1 ) * (int) $per_page;
 308              $end    = $start + $per_page;
 309              if ( -1 == $max_depth ) {
 310                  $this->max_pages = ceil( $total_top / $per_page );
 311              }
 312          }
 313  
 314          // flat display
 315          if ( -1 == $max_depth ) {
 316              if ( ! empty( $args[0]['reverse_top_level'] ) ) {
 317                  $elements = array_reverse( $elements );
 318                  $oldstart = $start;
 319                  $start    = $total_top - $end;
 320                  $end      = $total_top - $oldstart;
 321              }
 322  
 323              $empty_array = array();
 324              foreach ( $elements as $e ) {
 325                  $count++;
 326                  if ( $count < $start ) {
 327                      continue;
 328                  }
 329                  if ( $count >= $end ) {
 330                      break;
 331                  }
 332                  $this->display_element( $e, $empty_array, 1, 0, $args, $output );
 333              }
 334              return $output;
 335          }
 336  
 337          /*
 338           * Separate elements into two buckets: top level and children elements.
 339           * Children_elements is two dimensional array, e.g.
 340           * $children_elements[10][] contains all sub-elements whose parent is 10.
 341           */
 342          $top_level_elements = array();
 343          $children_elements  = array();
 344          foreach ( $elements as $e ) {
 345              if ( 0 == $e->$parent_field ) {
 346                  $top_level_elements[] = $e;
 347              } else {
 348                  $children_elements[ $e->$parent_field ][] = $e;
 349              }
 350          }
 351  
 352          $total_top = count( $top_level_elements );
 353          if ( $paging ) {
 354              $this->max_pages = ceil( $total_top / $per_page );
 355          } else {
 356              $end = $total_top;
 357          }
 358  
 359          if ( ! empty( $args[0]['reverse_top_level'] ) ) {
 360              $top_level_elements = array_reverse( $top_level_elements );
 361              $oldstart           = $start;
 362              $start              = $total_top - $end;
 363              $end                = $total_top - $oldstart;
 364          }
 365          if ( ! empty( $args[0]['reverse_children'] ) ) {
 366              foreach ( $children_elements as $parent => $children ) {
 367                  $children_elements[ $parent ] = array_reverse( $children );
 368              }
 369          }
 370  
 371          foreach ( $top_level_elements as $e ) {
 372              $count++;
 373  
 374              // For the last page, need to unset earlier children in order to keep track of orphans.
 375              if ( $end >= $total_top && $count < $start ) {
 376                      $this->unset_children( $e, $children_elements );
 377              }
 378  
 379              if ( $count < $start ) {
 380                  continue;
 381              }
 382  
 383              if ( $count >= $end ) {
 384                  break;
 385              }
 386  
 387              $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
 388          }
 389  
 390          if ( $end >= $total_top && count( $children_elements ) > 0 ) {
 391              $empty_array = array();
 392              foreach ( $children_elements as $orphans ) {
 393                  foreach ( $orphans as $op ) {
 394                      $this->display_element( $op, $empty_array, 1, 0, $args, $output );
 395                  }
 396              }
 397          }
 398  
 399          return $output;
 400      }
 401  
 402      /**
 403       * Calculates the total number of root elements.
 404       *
 405       * @since 2.7.0
 406       *
 407       * @param array $elements Elements to list.
 408       * @return int Number of root elements.
 409       */
 410  	public function get_number_of_root_elements( $elements ) {
 411          $num          = 0;
 412          $parent_field = $this->db_fields['parent'];
 413  
 414          foreach ( $elements as $e ) {
 415              if ( 0 == $e->$parent_field ) {
 416                  $num++;
 417              }
 418          }
 419          return $num;
 420      }
 421  
 422      /**
 423       * Unset all the children for a given top level element.
 424       *
 425       * @since 2.7.0
 426       *
 427       * @param object $e
 428       * @param array $children_elements
 429       */
 430  	public function unset_children( $e, &$children_elements ) {
 431          if ( ! $e || ! $children_elements ) {
 432              return;
 433          }
 434  
 435          $id_field = $this->db_fields['id'];
 436          $id       = $e->$id_field;
 437  
 438          if ( ! empty( $children_elements[ $id ] ) && is_array( $children_elements[ $id ] ) ) {
 439              foreach ( (array) $children_elements[ $id ] as $child ) {
 440                  $this->unset_children( $child, $children_elements );
 441              }
 442          }
 443  
 444          unset( $children_elements[ $id ] );
 445      }
 446  
 447  } // Walker


Generated: Mon Jun 17 08:20:02 2019 Cross-referenced by PHPXref 0.7