skip to Main Content

I’m using PHP DOMDocument in order to loop through an html string like this:

$htmlString = "<section>
        <p>text</p>
        <p>text</p>
    </section>
    <section>
        <h2>text</h2>
            <p>text</p>
            <p>text</p>
    </section>"

I want to replace each tag with a text value with a custom string (called $vueInterpolation), but for some reason.

ONLY the first tag of each section is getting its contents replaced!

I want all the tags inside each section to be replaced!

My method:

$dom = new DOMDocument();
libxml_use_internal_errors(true);

$htmlString = mb_convert_encoding($htmlString, 'HTML-ENTITIES', 'UTF-8');
$dom->loadHTML($htmlString);

$count = 0;
$keyPattern = 'ccpaRights';

foreach ($dom->getElementsByTagName('section') as $section)
{

    // loop through each child of <section>
    foreach ($section->childNodes as $childNode)
    { 
         $nodeValue = trim($childNode->nodeValue); 

        if ($nodeValue === '')
        {
            continue; 
        }

        $count = $count + 1;
        $key = (string) $keyPattern.'Text'.$count;
        
        $vueInterpolation = ' {{ $t('.'"'.$key.'"'.' }} ';

        $newNode = $dom->createElement($childNode->nodeName, $vueInterpolation );

        $childNode->parentNode->replaceChild($newNode, $childNode);
    }
}


$dom->saveHTML($dom);
$dom->save(public_path('/temp/result.html'));

Just so you know, this is the results I’m getting (notice only first tag of each section was replaced).

<section>
    <p> {{ $t("ccpaRightsText1" }} </p>
    <p>text</p>
</section>
<section>
    <h2> {{ $t("ccpaRightsText2" }} </h2>
        <p>text</p>
        <p>text</p>
</section>

And this is the results I want:

<section>
        <p> {{ $t("ccpaRightsText1" }} </p>
        <p> {{ $t("ccpaRightsText2" }} </p>
    </section>
    <section>
        <h2> {{ $t("ccpaRightsText3" }} </h2>
            <p> {{ $t("ccpaRightsText4" }} </p>
            <p> {{ $t("ccpaRightsText5" }} </p>
    </section>

2

Answers


  1. There is an error with your code because whitespaces between elements are considered nodes (#text).

    enter image description here

    So, try replace this line:

    $nodeValue = $childNode->nodeValue; 
    

    with this line:

    $nodeValue = trim($childNode->nodeValue); 
    

    The whole code:

    $htmlString = mb_convert_encoding($htmlString, 'HTML-ENTITIES', 'UTF-8');
    $dom->loadHTML($htmlString);
    
    $count = 0;
    $keyPattern = 'ccpaRights';
    
    foreach ($dom->getElementsByTagName('section') as $section)
    {
    
        // loop through each child of <section>
        foreach ($section->childNodes as $childNode)
        { 
            $nodeValue = trim($childNode->nodeValue); 
    
            if ($nodeValue === '')
            {
                continue; 
            }
    
            $count = $count + 1;
            $key = (string) $keyPattern.'Text'.$count;
            
            $vueInterpolation = "{{ $t("$key") }}";
            
            $newNode = $dom->createElement($childNode->nodeName, $vueInterpolation);
    
            $childNode->parentNode->replaceChild($newNode, $childNode);
        }
    }
    
    
    echo $dom->saveHTML($dom);
    

    UPDATED ANSWER:

    I tried to use for loop instead of foreach and it works. I am trying to know the reason but I can’t know it.

    $htmlString = mb_convert_encoding($htmlString, 'HTML-ENTITIES', 'UTF-8');
    $dom->loadHTML($htmlString);
    
    $count = 0;
    $keyPattern = 'ccpaRights';
    
    foreach ($dom->getElementsByTagName('section') as $section)
    {
        for ($x = 0; $x < count($section->childNodes); $x++)
        {
            $childNode = $section->childNodes[$x];
            $nodeValue = trim($childNode->nodeValue);
    
            if ($nodeValue === '')
            {
                continue; 
            }
    
            $count = $count + 1;
            $key = (string) $keyPattern.'Text'.$count;
            
            $vueInterpolation = "{{ $t("$key") }}";
            
            $newNode = $dom->createElement($childNode->nodeName, $vueInterpolation);
    
            $childNode->parentNode->replaceChild($newNode, $childNode);
    
        }
    }
    
    
    echo $dom->saveHTML($dom);
    
    Login or Signup to reply.
  2. I recommend:

    1. Wrapping your HTML markup in a div tag so that your section tags belong to a parent element (otherwise broken results occur). Remove the temporary tags after the replacing is done.

    2. Using XPath to target the children of the section tags.

    Code: (Demo)

    $dom = new DOMDocument;
    libxml_use_internal_errors(true);
    $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $xpath = new DOMXpath($dom);
    
    $count = 0;
    foreach ($xpath->query('//section/*') as $node) {
        $node->nodeValue = ' {{ $t("ccpaRightsText' . ++$count . '" }} ';
    }
    echo substr($dom->saveHTML($dom), 5, -7);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search