The DOM solution
$dom = new DOMDocument;
$dom->loadHTML( $ul );
$xPath = new DOMXPath( $dom );
$xPath->query( '/html/body/ul/li[last()]/@class' )
->item( 0 )
->value .= ' myClass';
echo $dom->saveXml( $dom->getElementById( 'menu' ) );
If you know the HTML to be valid, you can also use loadXML
instead. That would make DOM not add ther HTML skeleton. Note that you have to change the XPath to '/ul/li[last()]/@class'
then.
In case you are not familiar with XPath queries, you can also use the regular DOM interface, e.g.
$dom = new DOMDocument;
$dom->loadHTML( $ul );
$liElements = $dom->getElementsByTagName( 'li' );
$lastLi = $liElements->item( $liElements->length-1 );
$classes = $lastLi->getAttribute( 'class' ) . ' myClass';
$lastLi->setAttribute( 'class', $classes );
echo $dom->saveXml( $dom->getElementById( 'menu' ) );
EDIT Since you changed the question to have classes for first and last now, here is how to do that using XPath. This assumes your markup is valid XHTML. If not, switch back to loadHTML
(see code above):
$dom = new DOMDocument;
$dom->loadXML( $html );
$xpath = new DOMXPath( $dom );
$first = $xpath->query( '/ul/li[1]/@class' )->item( 0 );
$last = $xpath->query( '/ul/li[last()]/@class' )->item( 0 );
$last->value .= ' last';
$first->value .= ' first';
echo $dom->saveXML( $dom->documentElement );