Регулярками конечно можно, но это молотком ювелирку ровнять. Тем более у вас уже подключен свой вокер:
'walker' => new My_Walker_Nav_Menu(),
В нем должен быть метод
start_el()
. Или скопируйте метод из родительского класса
Walker_Nav_Menu
и подправьте код ближе к концу метода, вот этот фрагмент:
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
измените на:
$item_output = $args->before;
$item_output .= '<span>';
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
$item_output .= '</span>';
$item_output .= $args->after;
Но это только в случае если у вас уже есть кастомный вокер. Если нет - все еще проще. Во фрагменте выше обратите внимание на строки:
$item_output = $args->before;
...
$item_output .= $args->after;
Документация нам говорит следующее:
'before'
(string) Text before the link markup.
'after'
(string) Text after the link markup.
Пусть слово "text" вас не смущает, с помощью этих параметров передается и html-код:
wp_nav_menu( [
'menu' => '',
'container' => 'nav',
'container_class' => 'menu',
'echo' => true,
'fallback_cb' => 'wp_page_menu',
'before' => '<span>',
'after' => '</span>',
'items_wrap' => '<ul>%3$s</ul>',
// 'walker' => new My_Walker_Nav_Menu(), // Вот это вам не надо, если вы не знаете что делаете.
] );
То есть, стандартный вокер собирает элемент из таких блоков:
[before] + <a [attributes]> + [link_before] + title + [link_after] + </a> + [after]