Walker Classes
Abstract pattern for traversing and rendering hierarchical tree structures.
Since: 2.1.0
Source: wp-includes/class-wp-walker.php, wp-includes/class-walker-*.php
Components
| Component | Description |
|---|---|
| class-wp-walker.md | Base abstract Walker class |
| class-walker-category.md | HTML list of categories |
| class-walker-category-dropdown.md | Category dropdown <select> |
| class-walker-comment.md | Threaded comment list |
| class-walker-page.md | Hierarchical page list |
| class-walker-page-dropdown.md | Page dropdown <select> |
| hooks.md | Walker-related filters |
The Walker Pattern
Walker is WordPress’s implementation of the Visitor pattern for tree traversal. It separates:
- Traversal logic — How to walk the tree (handled by base class)
- Rendering logic — How to output each element (handled by child classes)
Walker (base)
├── walk() → Entry point, builds parent/child buckets
├── paged_walk() → Paginated traversal
├── display_element() → Recursive descent
│ ├── start_el() → Open element
│ ├── start_lvl() → Open child list
│ │ └── [recurse children]
│ ├── end_lvl() → Close child list
│ └── end_el() → Close element
└── get_number_of_root_elements()
Tree Traversal Algorithm
1. Bucket Sort
walk() first separates elements into two buckets:
$top_level_elements = []; // parent field is empty
$children_elements = []; // keyed by parent ID
2. Recursive Descent
For each top-level element, display_element() recursively:
- Calls
start_el()to open the element - If children exist and depth allows:
- Calls
start_lvl()to open the child container - Recursively processes each child
- Calls
end_lvl()to close the child container
- Calls
- Calls
end_el()to close the element
3. Orphan Handling
Elements whose parents don’t exist in the set are displayed as root elements (orphan recovery).
Depth Control
$max_depth |
Behavior |
|---|---|
-1 |
Flat display (no hierarchy) |
0 |
Display all levels (unlimited) |
> 0 |
Display that many levels |
Required Properties
Child classes must define:
public $tree_type = 'category'; // What type of data
public $db_fields = [
'parent' => 'parent', // Field containing parent ID
'id' => 'term_id', // Field containing element ID
];
Output Pattern
All methods append to $output by reference:
public function start_el( &$output, $data_object, $depth = 0, $args = [], $current_object_id = 0 ) {
$output .= '<li>' . esc_html( $data_object->name );
}
Creating a Custom Walker
class My_Walker extends Walker {
public $tree_type = 'my_type';
public $db_fields = [
'parent' => 'parent_id',
'id' => 'id',
];
public function start_lvl( &$output, $depth = 0, $args = [] ) {
$output .= '<ul class="depth-' . $depth . '">';
}
public function end_lvl( &$output, $depth = 0, $args = [] ) {
$output .= '</ul>';
}
public function start_el( &$output, $data_object, $depth = 0, $args = [], $current_object_id = 0 ) {
$output .= '<li>';
$output .= esc_html( $data_object->title );
}
public function end_el( &$output, $data_object, $depth = 0, $args = [] ) {
$output .= '</li>';
}
}
// Usage
$walker = new My_Walker();
echo $walker->walk( $items, 0 ); // 0 = unlimited depth
Built-in Walkers Comparison
| Walker | Data Type | Output Format | Used By |
|---|---|---|---|
Walker_Category |
WP_Term |
<ul>/<li> or separator |
wp_list_categories() |
Walker_CategoryDropdown |
WP_Term |
<option> |
wp_dropdown_categories() |
Walker_Comment |
WP_Comment |
<ul>/<li> or <div> |
wp_list_comments() |
Walker_Page |
WP_Post |
<ul>/<li> |
wp_list_pages() |
Walker_PageDropdown |
WP_Post |
<option> |
wp_dropdown_pages() |
Walker_Nav_Menu |
WP_Post |
<ul>/<li> |
wp_nav_menu() |
Paged Walking
For paginated tree display (e.g., threaded comments):
$walker = new Walker_Comment();
$output = $walker->paged_walk(
$comments,
$max_depth, // Max nesting depth
$page_num, // Current page (1-indexed)
$per_page, // Items per page
$args
);
// After walking, check total pages:
$total_pages = $walker->max_pages;