PageRenderTime 23ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/class-wp-walker.php

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