-2

I am trying to insert some text after every paragraph in my content.

I explode my content by </p> It is done using following code:

    $Paragraphs = explode( '</p>', $Content);
    foreach($Paragraphs as $Paragraph){
        // Some code
    }

Now my $Content looks like:

<p></p>
<p></p>
<p></p>
<div><p></p></div>
<p></p>
<p></p>
<div><p></p></div>

I want to split if <p> isn't wrapped inside <div> or <table> of anything else.

You can say that the </p> should have a <p> after it.

I read Regex can be helpful in achieveing it.

Here's the basic regex I built:


$Pattern = '/<p(|\s+[^>]*)>(.*?)<\/p\s*>/';

if(preg_match_all($Pattern, $Content, $keywords)){

}

This regex currently removes the

itself from the array, it keeps content inside p but not the

itself, and it doesn't check for it being either having a

before it or

after it.

Aditya Agarwal
  • 52
  • 1
  • 14
  • 3
    Why do you want to do this using string functions? Wouldn't it be easier to use DomDocument to navigate through the HTML structure and modify the tags as you want? https://www.php.net/manual/en/class.domdocument.php – mrodo Oct 26 '21 at 09:23
  • Not sure what is DomDocument, but goal is to insert an advertisement after every 6th paragraph, but ignore paragraphs wrapped in div or table.. – Aditya Agarwal Oct 26 '21 at 09:26
  • 2
    Time to learn about DomDocument then. This is not a job for string manipulation, but a proper DOM parser – trincot Oct 26 '21 at 09:32
  • There is too much effort asked of contributors between your XY Problem and a professional implementation of a dom parser; not to mention that the [mcve] isn't stellar. I gotta go Needs More Focus. – mickmackusa Oct 26 '21 at 10:26
  • https://stackoverflow.com/a/1732454/1067003 – hanshenrik Oct 26 '21 at 11:44

3 Answers3

2

If i understood your problem you have a string with tags such as:

$string = "
<p> Sometext 1 </p>
<p> Sometext 2 </p>
<p> Sometext 3 </p>
<div><p> Sometext Inside A Div </p> </div>
";

And you want to add another element right after each p that is not contained in any other element. And you want to do that purely through PHP, correct ?

In my opinion your best option is using DOMDocument.

Take a look at the solution below:

$doc = new DOMDocument();
$doc->loadHTML($string);
foreach ($doc->getElementsByTagName('p') as $idx => $item) {
    if($item->parentNode->nodeName == 'body') {
        $fragment = $doc->createDocumentFragment();
        $fragment->appendXML('<div> <div> <img src="image.jpg"/> </div> </div>');
        $item->parentNode->insertBefore($fragment, $item->nextSibling);
    }
}    

echo $doc->saveHTML();

Basically i am taking your string converting it into an HTML DOM then i iterate through all the p elements and if their parent is body then i create a document fragment which will append XML raw data to create your deeply nested structure without creating each element individually. Finnaly i insert the newly create fragment after each iterated p element.

The output will look something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
    <body>
        <p> Sometext 1 </p>
        <div> 
            <div> 
                <img src="image.jpg"> 
            </div> 
        </div>
        <p> Sometext 2 </p>
        <div> 
            <div> 
                <img src="image.jpg"> 
            </div> 
        </div>
        <p> Sometext 3 </p>
        <div> 
            <div> 
                <img src="image.jpg"> 
            </div> 
        </div>
        <div>
            <p> Sometext Inside A Div </p> 
        </div>
    </body>
</html>
Vasilis
  • 121
  • 3
  • 1
    Hi, it worked almost perfectly for me. Really simple, and I didn't know what is DomDocument after reading a bit, I am quite confident that this was the right appraoch. However I am having.bit of a trouble in the lime ```$object = $doc->createElement('span', "some new text");``` I want to insert a div with multiple nested elements, that div has SVGs, imgs paragraphs etc. So how am I supposed to create element such that it contains everything. Also, I wonder how do I add class to this div, I want to add a class to this div called "Revenue".. Please advice. – Aditya Agarwal Oct 28 '21 at 02:47
  • If you want to create a div with multiple elements or even a deeply nested element you need to create each new element and append its children check this doc: [link](https://php.net/manual/en/domnode.appendchild.php). For example $object1 = $doc->createElement('span'); $object2 = $doc->createElement('span'); $object3 = $doc->createElement('span'); and finally $object2->appendChild($object3); $object1->appendChild($object2); this will create something like this: – Vasilis Oct 28 '21 at 17:01
  • As for the class you can use [link](https://www.php.net/manual/en/domelement.setattribute.php). Basicaly you will set the class attribute to the object $newnode->setAttribute("class", "some-class"); – Vasilis Oct 28 '21 at 17:03
  • Yea I got it, but can I insert multiple divs inside it. I mean isn't there a direct way to attach a PHP string containing a lot of HTML. The code that I am trying to insert contains couple of SVGs, IMGs, divs and paragraphs, and has a lot of variables... Could I just add all that html to a html string and do createElement('div', $AllTheHTMLTogether) ? – Aditya Agarwal Oct 29 '21 at 04:25
  • @AdityaAgarwal i editted my answer to fix your issue. Inside the appendXML you can put all the html you want in a string format and it will create the elements automatically. You need to be carefull though. If you are going to use this approach you always have to close your tags. For example on img tags you have to add a '/' at the end of the tag (e.g. '/>') else the code will break. – Vasilis Oct 29 '21 at 08:46
  • Sorry for late reply. I had been traveling. Anyways, I shall test in couple of hours and reply back to you. Regardless, Thanks for your support and guidance. – Aditya Agarwal Oct 30 '21 at 04:49
  • Hey, I checked it worked almost perfectly. Had to use htmlspecialchars() function, but boi did it work nicely – Aditya Agarwal Oct 30 '21 at 06:40
1

For more complex minipulations on the DOM element, I would recommend using DomDocument. (https://www.php.net/manual/en/class.domdocument.php)

PHP Solution You can use the PHP string function str_replace for this. In your loop you can build the replace string and then pass it into the str_replace function as a parameter.

$text = '<p>hello</p> <p>Hi</p>';
$replace = '</p><span style="color: red;">World</span>';

echo str_replace("</p>",$replace,$text);

CSS Solution for simple Content You can just do it with pure css.

p::after { 
  content: " - World";
}
<p>1 x Hello</p>
<p>2 x Hello</p>
Maik Lowrey
  • 15,957
  • 6
  • 40
  • 79
  • The content isn't simple like " - World" and is different for every paragraph. So requires a backend solution... – Aditya Agarwal Oct 26 '21 at 09:42
  • @AdityaAgarwal Ok. Can you share what the content should be? – Maik Lowrey Oct 26 '21 at 09:44
  • Can't, because the content to be inserted after every paragraph is different. Lot's of variables... There's no predefined set of content to be insetlrted, all I can tell is there are 150 plus advertisment blocks, out of which 1 random is selected and inserted after the paragraph – Aditya Agarwal Oct 26 '21 at 09:46
-1
$string = '<p></p>
<p></p>
<p></p>
<div><p></p></div>
<p></p>
<p></p>
<div><p></p></div>';

$ex = explode("\n",$string);

foreach($ex as $k => $p){
    if(str_contains($p,"<div>") || str_contains($p,"<table>")){
        unset($ex[$k]);
    }
}

print_r($ex);