Search code examples
phparraysappenddomdocumentprepend

Add "first" and "last" classes to strings containing one or more <p> tags in PHP


I have two strings I'm outputting to a page

# string 1
<p>paragraph1</p>

# string 2
<p>paragraph1</p>
<p>paragraph2</p>
<p>paragraph3</p>

What I'd like to do is turn them into this

# string 1
<p class="first last">paragraph1</p>

# string 2
<p class="first">paragraph1</p>
<p>paragraph2</p>
<p class="last">paragraph3</p>

I'm essentially trying to replicate the css equivalent of first-child and last-child, but I have to physically add them to the tags as I cannot use CSS. The strings are part of a MPDF document and nth-child is not supported on <p> tags.

I can iterate through the strings easy enough to split the <p> tags into an array

$dom = new DOMDocument();
$question_paragraphs = array();
$dom->loadHTML($q['quiz_question']);
foreach($dom->getElementsByTagName('p') as $node)
{
 $question_paragraphs[] = $dom->saveHTML($node);
}

But once I have that array I'm struggling to find a nice clean way to append and prepend the first and last class to either end of the array. I end up with lots of ugly loops and array splicing that feels very messy.

I'm wondering if anyone has any slick ways to do this? Thank you :)

Edit Note: The two strings are outputting within a while(array) loop as they're stored in a database.


Solution

  • You can index the node list with the item() method, so you can add the attribute to the first and last elements in the list.

    $dom = new DOMDocument();
    $question_paragraphs = array();
    $dom->loadHTML($q['quiz_question']);
    $par = $dom->getElementsByTagName('p');
    if ($par->length == 1) {
        $par->item(0)->setAttribute("class", "first last");
    } elseif ($par->length > 1) {
        $par->item(0)->setAttribute("class", "first");
        $par->item($par->length - 1)->setAttribute("class", "last");
    }
        
    foreach($par as $node)
    {
        $question_paragraphs[] = $dom->saveHTML($node);
    }