Jump to content

can you combine these three preg_replace into one?


Recommended Posts

Looking for strings that start with <li and end with either value="pass">, value="fail"> or just >.

I need to replace each of these strings with a different variable, and also the result must include whatever was in between the start and end of the string.

 

I managed to do it, but I'm using three different preg_replace. Surely this can be done more efficiently in one single pre_replace. Any ideas?

 

Thanks!

$string='
<li id="5691_3" value="pass"> Some text here</li>
<li id="5691_4" value="pass"> Some text here</li>
<li id="5691_6"> Some text here</li>
<li id="5691_7" value="fail"> Some text here</li>
'

$varPass= "You passed! - "; $varFail = "You failed! - ";  varBlank="Its blank! - ";

$string= preg_replace('/<li ([^>]*) value="pass">/', '<br>Result for $1 ' . $varPass, $string)
$string= preg_replace('/<li ([^>]*) value="fail">/', '<br>Result for $1 '.$varFail, $string)
$string= preg_replace('/<li ([^>]*)>/', '<br>Result for $1 '.$varBlank, $string); 
		
$string=str_replace("</li>","",$string);		


// this results into...

$string='
<br>Result for id="5691_3" - You passed! Some text here
<br>Result for id="5691_4" - You passed! Some text here
<br>Result for id="5691_6" - Its blank! Some text here
<br>Result for id="5691_7" - You failed! Some text here
'







Edited by lfernando

Use an array for the expressions and the replacements. I made some other improvements as well to account for other parameters being in the LI tags. Although it still requires that the id parameter comes before the value parameter. It could be written to work with them in any order, but I was too lazy too look up the format.

$string='
<li id="5691_3" value="pass"> Some text hereA</li>
<li id="5691_4" value="pass"> Some text hereB</li>
<li id="5691_6"> Some text hereC</li>
<li id="5691_7" value="fail"> Some text hereD</li>
';

$patterns = array(
    '#<li.*?id="([^"]*)".*?value="pass"[^>]*>([^<]*)</li>#',
    '#<li.*?id="([^"]*)".*?value="fail"[^>]*>([^<]*)</li>#',
    '#<li.*?id="([^"]*)"[^>]*>([^<]*)</li>#'
    );
$replacements = array(
    'Result for ID "$1" You passed! - $2<br>',
    'Result for ID "$1" You failed! - $2<br>',
    'Result for ID "$1" Its blank! - $2<br>'
    );

$string= preg_replace($patterns, $replacements, $string);
echo $string;

Output

 

Result for ID "5691_3" You passed! - Some text hereA
Result for ID "5691_4" You passed! - Some text hereB
Result for ID "5691_6" Its blank! - Some text hereC
Result for ID "5691_7" You failed! - Some text hereD
Edited by Psycho

Alternatively, you can use preg_replace_callback. However.. this would be better achieved with a DOM parser.

 

For shits and grins, here is the preg_replace_callback version:

 

$string=<<<EOC
<li id="5691_3" value="pass"> Some text here</li>
<li id="5691_4" value="pass"> Some text here</li>
<li id="5691_6"> Some text here</li>
<li id="5691_7" value="fail"> Some text here</li>
EOC;

$string = preg_replace_callback(
  '~<li[^>]*(id="[^"]+")(?:[^>]*value="([^"]+)")?[^>]*>(.*?)</li>~',
  function ($m) {
    $r = "<br>Result for {$m[1]} - ";
    $r.= (!empty($m[2]))? "You {$m[2]}ed!" : "It's blank!";
    $r.= " {$m[3]}";
    return $r;  
  },
  $string
);

echo $string;
I too will decline making this "fool proof" as far as order of attributes, spacing, etc.. because as I said, a DOM parser is better suited for this sort of thing.

Speaking of DOM...

 

It's definitely longer but it's more flexible than string matching. It's also a matter of principle: avoid using regular expressions on HTML.

$html = <<<HTML
...
<ul>
	<li id="5691_3" value="pass"> Some text here</li>
	<li id="5691_4" value="pass"> Some text here</li>
	<li id="5691_6"> Some text here</li>
	<li id="5691_7" value="fail"> Some text here</li>
</ul>
...
HTML;

$dom = new DOMDocument();
$dom->loadHTML($html);

$lis = $dom->getElementsByTagName("li");
for ($i = 0; $i < $lis->length; ) { // no $i++
	$li = $lis->item($i);
	$parent = $li->parentNode;

	if ($li->getAttribute("value") == "pass") {
		$text = "You passed!";
	} else if ($li->getAttribute("value") == "fail") {
		$text = "You failed!";
	} else {
		$text = "It's blank!";
		// $parent->removeChild($li);
		// continue;
	}

	$id = $li->getAttribute("id");
	$text = "Result for id={$id} - {$text}{$li->nodeValue}";

	$textnode = $dom->createTextNode($text);
	$parent->replaceChild($textnode, $li);
	$parent->insertBefore($dom->createElement("br"), $textnode);
}

echo $dom->saveHTML();
Regarding $i: when removing nodes, like with removeChild() or replaceChild(), the node you had will be removed from everywhere. That includes $lis. So $i shouldn't advance beyond 0 because it'll always want to look at the first item in the list.

...unless you decide that a node shouldn't be removed, for whatever reason, in which case you $i++ and then continue.

I supplied a link in my previous post that explains why you should avoid regex for html. I labeled it "DOM Parser" because the link also gives suggestions for DOM Parsers you can use.

 

http://forums.phpfreaks.com/topic/273123-readme-php-regex-resources-faqs/?do=findComment&comment=1405513

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.