在php变量中捕获第一个和最后一个<li>

echo $ul; // gives this code:

<ul id="menu">
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class">...</li>
</ul>

How to add some class for the first and the last <li>?

Need a regex solution.

echo $ul; should give (if we add class my_class for the last <li>):

<ul id="menu">
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class my_class">...</li>
</ul>

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 );

Alternatively, you could use "#menu li:last-child" in your CSS instead of a class name, that way you don't have to modify your PHP code.

If you MUST use regex for this(not exactly to be advised). I think this should work...

$replacement1 = "<li\s.*?class="(.*?)".*?>.*?</li>\s</ul>";
$string1 = "$1 class_last";
$ul = preg_replace($ul, $replacement1, $string1);)

$replacement2 = "<ul.*?>\s<li\s.*?class="(.*?)".*?>";
$string2 = "$1 class_first";
$ul = preg_replace($ul, $replacement2, $string2);)

If you really want to do that job with regex, you could try :

$ul = '
<ul id="menu">
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class">...</li>
</ul>
';

// explode input string to an array
$lines = explode("
", $ul);

$found_first = 0;   // is the first li founded
$found_last  = 0;   // index of the last li
$class_first = "class_first"; // class for the first li
$class_last  = "class_last";  // class for the last li

// loop on all lines
for ($i = 0; $i < count($lines); $i++) {
  $line = $lines[$i];

  // the line begins with <li
  if (preg_match("/^<li/", $line)) {

    // is it the first one
    if (!$found_first) {
      // add the class
      $lines[$i] = preg_replace('/ class="([^"]+?)"/', " class=\"$1 $class_first\"", $lines[$i]);
      // the first li have been found
      $found_first = 1;
    }
    // memo the last line proceded
    $found_last = $i;
  }
}

// this will add class_last even if the last li
// is also the first one (ie: only one li)
if ($found_last) {
  $lines[$found_last] = preg_replace('/ class="([^"]+?)"/', " class=\"$1 $class_last\"", $lines[$found_last]);
}

$ul = implode("
", $lines);

echo $ul;

Ouput:

<ul id="menu">
<li id="some_id" class="some_class class_first">...</li>
<li id="some_id" class="some_class">...</li>
<li id="some_id" class="some_class class_last">...</li>
</ul>

You can use counters:

<?php
$list = array('aaa', 'bbb', 'ccc', 'ddd');
$items = count($list); // count items in list
$i = 1; // set counter to one, because first item in list will be item number: 1

echo '<ul>';

// create loop
foreach($list as $value) {

  // first item
  if($i == 1) {
    $class = 'some_class first_class';

  // last item
  } elseif ($i == $items) {
    $class = 'some_class last_class';

  // not first / not last item
  } else {
    $class = 'some_class';
  }

  echo '<li id="some_id" class="'.  $class .'">' . $value . '</li>';
  $i++; // raise $i by one
}

echo '</ul>';
?>

Will output:

<ul>
<li id="some_id" class="some_class first_class">aaa</li>
<li id="some_id" class="some_class">bbb</li>
<li id="some_id" class="some_class">ccc</li>
<li id="some_id" class="some_class last_class">ddd</li>
</ul>

However, my suggestion would be:

<ul id="menu">
  <li>aaa</li>
  <li>bbb</li>
  <li>ccc</li>
  <li>ddd</li>
</ul>

Within your css:

#menu {
}

#menu li:first-child {
}

#menu li {
}

#menu li:last-child {
}