class iWalker { var $tree_type; var $db_fields; //abstract callbacks function istart_lvl($output) { return $output; } function iend_lvl($output) { return $output; } function istart_el($output) { return $output; } function iend_el($output) { return $output; } function iparent_start($output) { return $output; } function iparent_end($output) { return $output; } function iwalk($elements, $to_depth) { $args = array_slice(func_get_args(), 2); $parents = array(); $depth = 1; $previous_element = ''; $output = ''; //padding at the end $last_element->post_parent = 0; $last_element->post_id = 0; $elements[] = $last_element; $id_field = $this->db_fields['id']; $parent_field = $this->db_fields['parent']; $flat = ($to_depth == -1) ? true : false; $top_level = ($depth == 1) ? true : false; foreach ( $elements as $element ) { // If flat, start and end the element and skip the level checks. if ( $flat) { // Start the element. if ( isset($element->$id_field) && $element->$id_field != 0 ) { $cb_args = array_merge( array($output, $element, $depth - 1), $args); $output = call_user_func_array(array(&$this, 'iparent_start'), $cb_args); } // End the element. if ( isset($element->$id_field) && $element->$id_field != 0 ) { $cb_args = array_merge( array($output, $element, $depth - 1), $args); $output = call_user_func_array(array(&$this, 'iparent_end'), $cb_args); } continue; } // Walk the tree. if ( !empty($previous_element) && ($element->$parent_field == $previous_element->$id_field) ) { // Previous element is my parent. Descend a level. array_unshift($parents, $previous_element); if ( !$to_depth || ($depth < $to_depth) ) { //only descend if we're below $to_depth $cb_args = array_merge( array($output, $depth), $args); $output = call_user_func_array(array(&$this, 'start_lvl'), $cb_args); } else if ( $to_depth && $depth == $to_depth ) { // If we've reached depth, end the previous element. $cb_args = array_merge( array($output, $previous_element, $depth), $args); ($depth ==1) ? $output = call_user_func_array(array(&$this, 'iparent_end'), $cb_args) : $output = call_user_func_array(array(&$this, 'end_el'), $cb_args); } $depth++; //always do this so when we start the element further down, we know where we are } else if ( $element->$parent_field == $previous_element->$parent_field) { // On the same level as previous element. if ( !$to_depth || ($depth <= $to_depth) ) { $cb_args = array_merge( array($output, $previous_element, $depth - 1), $args); ($depth ==1) ?$output = call_user_func_array(array(&$this, 'iparent_end'), $cb_args) : $output = call_user_func_array(array(&$this, 'end_el'), $cb_args); } } else if ( $depth > 1 ) { // Ascend one or more levels. if ( !$to_depth || ($depth <= $to_depth) ) { $cb_args = array_merge( array($output, $previous_element, $depth - 1), $args); ($depth ==1) ? $output = call_user_func_array(array(&$this, 'iparent_end'), $cb_args) : $output = call_user_func_array(array(&$this, 'end_el'), $cb_args); } while ( $parent = array_shift($parents) ) { $depth--; if ( !$to_depth || ($depth < $to_depth) ) { $cb_args = array_merge( array($output, $depth), $args); $output = call_user_func_array(array(&$this, 'end_lvl'), $cb_args); $cb_args = array_merge( array($output, $parent, $depth - 1), $args); ($depth ==1 ) ? $output = call_user_func_array(array(&$this, 'iparent_end'), $cb_args) : $output = call_user_func_array(array(&$this, 'end_el'), $cb_args); } if ( $element->$parent_field == $parents[0]->$id_field ) { break; } } } else if ( !empty($previous_element) ) { // Close off previous element. if ( !$to_depth || ($depth <= $to_depth) ) { $cb_args = array_merge( array($output, $previous_element, $depth - 1), $args); ($depth == 1) ? $output = call_user_func_array(array(&$this, 'iparent_end'), $cb_args) : $output = call_user_func_array(array(&$this, 'end_el'), $cb_args); } }