Why is current_object_id always 0 when extending the Walker class?

What it says on the tin, really. I’m building my own Walker, and I usually like to look at the default state to get the lay of the land.

I’m calling the walker with wp_nav_menu like this:

wp_nav_menu(array(
	'menu' => 'primary-menu',
	'container' => false,
	'items_wrap' => '%3$s',
	'walker' => new Demo_Nav_Walker(),
));

And the walker class looks like this:

class Demo_Nav_Walker extends Walker_Nav_Menu {
	
    public function start_el(&$output, $data_object, $depth = 0, $args = null, $current_object_id = 0) {
        echo var_dump($current_object_id);
    }

}

This results in each element showing int(0). Now, I’m positive that the elements do have IDs, because if we look at the $data_object instead, there’s an ID for each element (which is a WP_Post object).

I’ve been trying to poke at why this might be, and it seems like the core of it is in the function the Walker class uses to call this function (this is in the display_elements function in in the Walker class):

$this->start_el( $output, $element, $depth, ...array_values( $args ) )

If I’m reading it right, it just…isn’t passing an ID into that function at all, so we’re getting the fallback value of 0.

My question is…does anyone know why? It seems odd that start_el can take an element ID, but just isn’t set up to do so. Is this one of those functionalities that’s meant to be useful if someone was going to do something more complicated and override the display_elements function?

Yes, the 5th parameter is the object ID, if it’s not included when the method is called, you get the specified default value of 0.

If this is the case, do you even need to pass in ID in as a parameter? Is the ID a property of the data_object already?

I think my question was more conceptual. I think my questions were 1) Am I correctly understanding what’s going on here and 2) Does anyone know if there’s a reason why it’s like that? It seems odd to create a function that’s going to be invoked by the class, but not have it pass one of the parameters into it.

I know it doesn’t matter for my use case, but understanding why things are as they are is often more useful than just knowing what to do.

No it will pass the value if $args contains two elements, because of the splat operator.

$args = [1, 2];
function sum ($a, $b) { return $a + $b;}
echo sum(...$args); // 3

It basically makes it so the first element is passed as the first argument to the function, the second element as the second argument, and so on and so forth.

I do see that if we override display_elements, we get that second part of the array as the $current_object_id. So, for example, if we do something like this to extend the class:

class Esb_Nav_Walker extends Walker_Nav_Menu {

    public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
        $args = array(1, 2);
        $this->start_el( $output, $element, $depth, ...array_values( $args ) );
    }

    public function start_el(&$output, $data_object, $depth = 0, $args = null, $current_object_id = 0) {
        echo var_dump($current_object_id);
    }

}

All of our elements produce int(2) instead of the default value.

I think what’s throwing me though, is that if we try this:

class Esb_Nav_Walker extends Walker_Nav_Menu {

    public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
        echo count($args);
    }

}

We only ever get one element in the $args array, which means that when this gets passed off to start_el, we’ll always get 0 as the fallback value for $current_object_id. So I don’t really understand why it’s there at all in the start_el function, if it’s not going to be used.

Explanations I can think of:

  1. There are specific conditions where $args will come into display_elements with a second element in the array, and I just haven’t found how to make that happen.
  2. The default use of display_elements doesn’t pass in a $current_object_id, but there might be cases where someone might want to override that function and pass it in for some specific purpose.
  3. The Walker class is extended in other built-in classes that do use the $current_object_id parameter, so it needs to be there in the abstract class.
  4. It’s a legacy from a previous WordPress version where it was necessary, and it’s stayed in to avoid breaking older code.
  5. Who knows? WordPress is an open source project, so sometimes things are like that because a specific person working on the project thought it was a good idea.

I’m just interested in understanding. Sometimes knowing why something looks odd but is actually important helps you more deeply understand other concepts about something you’re working on.

Agreed. I don’t think this is one of those cases though :slightly_smiling_face:

If you’re facing an issue where current_object_id is always 0 when extending the Walker class, it’s likely because it’s being initialized to 0 in the parent class and not properly updated or overridden in the subclass. In many cases, class variables like current_object_id can be assigned default values (like 0), and if you don’t explicitly modify it in the child class, it will stay at 0.

To fix this, make sure to either pass a new value for current_object_id when creating an instance of the subclass or explicitly set it within the subclass constructor. Also, check that you’re not inadvertently resetting the variable to 0 in the subclass.

Hope this helps!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.