[ 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 at the 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. Also, the methods are
   8   * not strictly abstract in that the parameter definition needs to be followed.
   9   * The child classes can have additional parameters.
  10   *
  11   * @package WordPress
  12   * @since 2.1.0
  13   * @abstract
  14   */
  15  class Walker {
  16      /**
  17       * What the class handles.
  18       *
  19       * @since 2.1.0
  20       * @var string
  21       * @access public
  22       */
  23      var $tree_type;
  24  
  25      /**
  26       * DB fields to use.
  27       *
  28       * @since 2.1.0
  29       * @var array
  30       * @access protected
  31       */
  32      var $db_fields;
  33  
  34      /**
  35       * Max number of pages walked by the paged walker
  36       *
  37       * @since 2.7.0
  38       * @var int
  39       * @access protected
  40       */
  41      var $max_pages = 1;
  42  
  43      /**
  44       * Starts the list before the elements are added.
  45       *
  46       * Additional parameters are used in child classes. The args parameter holds
  47       * additional values that may be used with the child class methods. This
  48       * method is called at the start of the output list.
  49       *
  50       * @since 2.1.0
  51       * @abstract
  52       *
  53       * @param string $output Passed by reference. Used to append additional content.
  54       */
  55  	function start_lvl( &$output, $depth = 0, $args = array() ) {}
  56  
  57      /**
  58       * Ends the list of after the elements are added.
  59       *
  60       * Additional parameters are used in child classes. The args parameter holds
  61       * additional values that may be used with the child class methods. This
  62       * method finishes the list at the end of output of the elements.
  63       *
  64       * @since 2.1.0
  65       * @abstract
  66       *
  67       * @param string $output Passed by reference. Used to append additional content.
  68       */
  69  	function end_lvl( &$output, $depth = 0, $args = array() )   {}
  70  
  71      /**
  72       * Start the element output.
  73       *
  74       * Additional parameters are used in child classes. The args parameter holds
  75       * additional values that may be used with the child class methods. Includes
  76       * the element output also.
  77       *
  78       * @since 2.1.0
  79       * @abstract
  80       *
  81       * @param string $output Passed by reference. Used to append additional content.
  82       */
  83  	function start_el( &$output, $object, $depth, $args, $current_object_id = 0 )  {}
  84  
  85      /**
  86       * Ends the element output, if needed.
  87       *
  88       * Additional parameters are used in child classes. The args parameter holds
  89       * additional values that may be used with the child class methods.
  90       *
  91       * @since 2.1.0
  92       * @abstract
  93       *
  94       * @param string $output Passed by reference. Used to append additional content.
  95       */
  96  	function end_el( &$output, $object, $depth = 0, $args = array() )    {}
  97  
  98      /**
  99       * Traverse elements to create list from elements.
 100       *
 101       * Display one element if the element doesn't have any children otherwise,
 102       * display the element and its children. Will only traverse up to the max
 103       * depth and no ignore elements under that depth. It is possible to set the
 104       * max depth to include all depths, see walk() method.
 105       *
 106       * This method shouldn't be called directly, use the walk() method instead.
 107       *
 108       * @since 2.5.0
 109       *
 110       * @param object $element Data object
 111       * @param array $children_elements List of elements to continue traversing.
 112       * @param int $max_depth Max depth to traverse.
 113       * @param int $depth Depth of current element.
 114       * @param array $args
 115       * @param string $output Passed by reference. Used to append additional content.
 116       * @return null Null on failure with no changes to parameters.
 117       */
 118  	function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
 119  
 120          if ( !$element )
 121              return;
 122  
 123          $id_field = $this->db_fields['id'];
 124  
 125          //display this element
 126          if ( is_array( $args[0] ) )
 127              $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );
 128          $cb_args = array_merge( array(&$output, $element, $depth), $args);
 129          call_user_func_array(array(&$this, 'start_el'), $cb_args);
 130  
 131          $id = $element->$id_field;
 132  
 133          // descend only when the depth is right and there are childrens for this element
 134          if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {
 135  
 136              foreach( $children_elements[ $id ] as $child ){
 137  
 138                  if ( !isset($newlevel) ) {
 139                      $newlevel = true;
 140                      //start the child delimiter
 141                      $cb_args = array_merge( array(&$output, $depth), $args);
 142                      call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
 143                  }
 144                  $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );
 145              }
 146              unset( $children_elements[ $id ] );
 147          }
 148  
 149          if ( isset($newlevel) && $newlevel ){
 150              //end the child delimiter
 151              $cb_args = array_merge( array(&$output, $depth), $args);
 152              call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
 153          }
 154  
 155          //end this element
 156          $cb_args = array_merge( array(&$output, $element, $depth), $args);
 157          call_user_func_array(array(&$this, 'end_el'), $cb_args);
 158      }
 159  
 160      /**
 161       * Display array of elements hierarchically.
 162       *
 163       * It is a generic function which does not assume any existing order of
 164       * elements. max_depth = -1 means flatly display every element. max_depth =
 165       * 0 means display all levels. max_depth > 0  specifies the number of
 166       * display levels.
 167       *
 168       * @since 2.1.0
 169       *
 170       * @param array $elements
 171       * @param int $max_depth
 172       * @return string
 173       */
 174  	function walk( $elements, $max_depth) {
 175  
 176          $args = array_slice(func_get_args(), 2);
 177          $output = '';
 178  
 179          if ($max_depth < -1) //invalid parameter
 180              return $output;
 181  
 182          if (empty($elements)) //nothing to walk
 183              return $output;
 184  
 185          $id_field = $this->db_fields['id'];
 186          $parent_field = $this->db_fields['parent'];
 187  
 188          // flat display
 189          if ( -1 == $max_depth ) {
 190              $empty_array = array();
 191              foreach ( $elements as $e )
 192                  $this->display_element( $e, $empty_array, 1, 0, $args, $output );
 193              return $output;
 194          }
 195  
 196          /*
 197           * need to display in hierarchical order
 198           * separate elements into two buckets: top level and children elements
 199           * children_elements is two dimensional array, eg.
 200           * children_elements[10][] contains all sub-elements whose parent is 10.
 201           */
 202          $top_level_elements = array();
 203          $children_elements  = array();
 204          foreach ( $elements as $e) {
 205              if ( 0 == $e->$parent_field )
 206                  $top_level_elements[] = $e;
 207              else
 208                  $children_elements[ $e->$parent_field ][] = $e;
 209          }
 210  
 211          /*
 212           * when none of the elements is top level
 213           * assume the first one must be root of the sub elements
 214           */
 215          if ( empty($top_level_elements) ) {
 216  
 217              $first = array_slice( $elements, 0, 1 );
 218              $root = $first[0];
 219  
 220              $top_level_elements = array();
 221              $children_elements  = array();
 222              foreach ( $elements as $e) {
 223                  if ( $root->$parent_field == $e->$parent_field )
 224                      $top_level_elements[] = $e;
 225                  else
 226                      $children_elements[ $e->$parent_field ][] = $e;
 227              }
 228          }
 229  
 230          foreach ( $top_level_elements as $e )
 231              $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
 232  
 233          /*
 234           * if we are displaying all levels, and remaining children_elements is not empty,
 235           * then we got orphans, which should be displayed regardless
 236           */
 237          if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
 238              $empty_array = array();
 239              foreach ( $children_elements as $orphans )
 240                  foreach( $orphans as $op )
 241                      $this->display_element( $op, $empty_array, 1, 0, $args, $output );
 242           }
 243  
 244           return $output;
 245      }
 246  
 247      /**
 248        * paged_walk() - produce a page of nested elements
 249        *
 250        * Given an array of hierarchical elements, the maximum depth, a specific page number,
 251        * and number of elements per page, this function first determines all top level root elements
 252        * belonging to that page, then lists them and all of their children in hierarchical order.
 253        *
 254        * @package WordPress
 255        * @since 2.7
 256        * @param int $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels.
 257        * @param int $page_num the specific page number, beginning with 1.
 258        * @return XHTML of the specified page of elements
 259        */
 260  	function paged_walk( $elements, $max_depth, $page_num, $per_page ) {
 261  
 262          /* sanity check */
 263          if ( empty($elements) || $max_depth < -1 )
 264              return '';
 265  
 266          $args = array_slice( func_get_args(), 4 );
 267          $output = '';
 268  
 269          $id_field = $this->db_fields['id'];
 270          $parent_field = $this->db_fields['parent'];
 271  
 272          $count = -1;
 273          if ( -1 == $max_depth )
 274              $total_top = count( $elements );
 275          if ( $page_num < 1 || $per_page < 0  ) {
 276              // No paging
 277              $paging = false;
 278              $start = 0;
 279              if ( -1 == $max_depth )
 280                  $end = $total_top;
 281              $this->max_pages = 1;
 282          } else {
 283              $paging = true;
 284              $start = ( (int)$page_num - 1 ) * (int)$per_page;
 285              $end   = $start + $per_page;
 286              if ( -1 == $max_depth )
 287                  $this->max_pages = ceil($total_top / $per_page);
 288          }
 289  
 290          // flat display
 291          if ( -1 == $max_depth ) {
 292              if ( !empty($args[0]['reverse_top_level']) ) {
 293                  $elements = array_reverse( $elements );
 294                  $oldstart = $start;
 295                  $start = $total_top - $end;
 296                  $end = $total_top - $oldstart;
 297              }
 298  
 299              $empty_array = array();
 300              foreach ( $elements as $e ) {
 301                  $count++;
 302                  if ( $count < $start )
 303                      continue;
 304                  if ( $count >= $end )
 305                      break;
 306                  $this->display_element( $e, $empty_array, 1, 0, $args, $output );
 307              }
 308              return $output;
 309          }
 310  
 311          /*
 312           * separate elements into two buckets: top level and children elements
 313           * children_elements is two dimensional array, eg.
 314           * children_elements[10][] contains all sub-elements whose parent is 10.
 315           */
 316          $top_level_elements = array();
 317          $children_elements  = array();
 318          foreach ( $elements as $e) {
 319              if ( 0 == $e->$parent_field )
 320                  $top_level_elements[] = $e;
 321              else
 322                  $children_elements[ $e->$parent_field ][] = $e;
 323          }
 324  
 325          $total_top = count( $top_level_elements );
 326          if ( $paging )
 327              $this->max_pages = ceil($total_top / $per_page);
 328          else
 329              $end = $total_top;
 330  
 331          if ( !empty($args[0]['reverse_top_level']) ) {
 332              $top_level_elements = array_reverse( $top_level_elements );
 333              $oldstart = $start;
 334              $start = $total_top - $end;
 335              $end = $total_top - $oldstart;
 336          }
 337          if ( !empty($args[0]['reverse_children']) ) {
 338              foreach ( $children_elements as $parent => $children )
 339                  $children_elements[$parent] = array_reverse( $children );
 340          }
 341  
 342          foreach ( $top_level_elements as $e ) {
 343              $count++;
 344  
 345              //for the last page, need to unset earlier children in order to keep track of orphans
 346              if ( $end >= $total_top && $count < $start )
 347                      $this->unset_children( $e, $children_elements );
 348  
 349              if ( $count < $start )
 350                  continue;
 351  
 352              if ( $count >= $end )
 353                  break;
 354  
 355              $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
 356          }
 357  
 358          if ( $end >= $total_top && count( $children_elements ) > 0 ) {
 359              $empty_array = array();
 360              foreach ( $children_elements as $orphans )
 361                  foreach( $orphans as $op )
 362                      $this->display_element( $op, $empty_array, 1, 0, $args, $output );
 363          }
 364  
 365          return $output;
 366      }
 367  
 368  	function get_number_of_root_elements( $elements ){
 369  
 370          $num = 0;
 371          $parent_field = $this->db_fields['parent'];
 372  
 373          foreach ( $elements as $e) {
 374              if ( 0 == $e->$parent_field )
 375                  $num++;
 376          }
 377          return $num;
 378      }
 379  
 380      // unset all the children for a given top level element
 381  	function unset_children( $e, &$children_elements ){
 382  
 383          if ( !$e || !$children_elements )
 384              return;
 385  
 386          $id_field = $this->db_fields['id'];
 387          $id = $e->$id_field;
 388  
 389          if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) )
 390              foreach ( (array) $children_elements[$id] as $child )
 391                  $this->unset_children( $child, $children_elements );
 392  
 393          if ( isset($children_elements[$id]) )
 394              unset( $children_elements[$id] );
 395  
 396      }
 397  }


Generated: Fri May 25 03:56:23 2012 Hosted by follow the white rabbit.