fragment.
* Pure DOM/string parsing, no WordPress calls.
*
* @package Esipher
*/
namespace Esipher;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Maps a raw ... HTML fragment into an ordered list of
* ['label' => string, 'href' => string, 'children' => array] items.
*/
class Nav_Mapper {
/**
* Map a fragment to a nested link list.
*
* @param string $nav_html Raw outerHTML of a single element, as
* produced by Html_Parser::parse()->nav_html.
*
* @return array[]
*/
public function map( $nav_html ) {
$dom = new \DOMDocument();
$previous_setting = libxml_use_internal_errors( true );
$dom->loadHTML(
'' . $nav_html,
LIBXML_NOWARNING | LIBXML_NOERROR
);
libxml_clear_errors();
libxml_use_internal_errors( $previous_setting );
$xpath = new \DOMXPath( $dom );
$nav_nodes = $xpath->query( '//nav' );
if ( ! $nav_nodes || 0 === $nav_nodes->length ) {
return array();
}
$nav = $nav_nodes->item( 0 );
$ul = $xpath->query( './ul', $nav );
if ( $ul && $ul->length ) {
return $this->map_list( $xpath, $ul->item( 0 ) );
}
// No wrapping — fall back to any tags directly under .
return $this->map_links( $xpath, $nav );
}
/**
* Map every direct child of a into a link item, recursing
* into nested s for dropdown children.
*
* @param \DOMXPath $xpath Query interface.
* @param \DOMElement $ul The element.
*
* @return array[]
*/
private function map_list( \DOMXPath $xpath, \DOMElement $ul ) {
$items = array();
foreach ( $xpath->query( './li', $ul ) as $li ) {
$items[] = $this->map_item( $xpath, $li );
}
return $items;
}
/**
* Map a single : its own link plus any nested dropdown.
*
* @param \DOMXPath $xpath Query interface.
* @param \DOMElement $li The element.
*
* @return array{label: string, href: string, children: array[]}
*/
private function map_item( \DOMXPath $xpath, \DOMElement $li ) {
$label = '';
$href = '';
$anchor_nodes = $xpath->query( './a', $li );
if ( $anchor_nodes && $anchor_nodes->length ) {
$anchor = $anchor_nodes->item( 0 );
$label = trim( $anchor->textContent );
$href = $anchor->getAttribute( 'href' );
}
$children = array();
$child_ul = $xpath->query( './ul', $li );
if ( $child_ul && $child_ul->length ) {
$children = $this->map_list( $xpath, $child_ul->item( 0 ) );
}
return array(
'label' => $label,
'href' => $href,
'children' => $children,
);
}
/**
* Map tags that are direct children of (no wrapper).
*
* @param \DOMXPath $xpath Query interface.
* @param \DOMElement $nav The element.
*
* @return array[]
*/
private function map_links( \DOMXPath $xpath, \DOMElement $nav ) {
$items = array();
foreach ( $xpath->query( './a', $nav ) as $anchor ) {
$items[] = array(
'label' => trim( $anchor->textContent ),
'href' => $anchor->getAttribute( 'href' ),
'children' => array(),
);
}
return $items;
}
}