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


Generated: Wed Sep 18 01:00:03 2019 Cross-referenced by PHPXref 0.7.1