PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

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