Reputation: 585
I ned some help with RegEx, not sure what I am doing wrong. What I would like to achieve is to change the value of one CSS property in a CSS file without altering anything within that CSS file.
<?php
$var = '
#css {
text-size: 14px;
color: red;
background: orange;
}
';
echo preg_replace('/#css(.*)color: (.*);(.*)}/is','#css$1color: black;$3 }',$var);
?>
The result I am hoping to see is this:
#css {
text-size: 14px;
color: red;
background: orange;
}
However what I get is this:
#css {
text-size: 14px;
color: black;
}
I am not an expert in RegEx at all but after reading manuals and examples online I thought I could use backreferences to do this and that $1 would be the result of the first match, $2 of the second, $3 of the third etc... In my example $1 matches everything between '#css' and 'color: ' and then $2 I don't use because I want to replace 'red' with 'black'. I thought $3 would be the result of everything between ';' and '}' but it gets lost somewhere or, more likely, I am lost somewhere ;-)
Thanks for the advice and support.
Upvotes: 1
Views: 804
Reputation: 51181
First of all, using the greedy .*
is always problematic. In your case, it cosumes too much symbols so that you lose the background because it matches the last ;
in your code (which comes after the background declaration). Instead use a negative character class which matches until the very next symbol you know should not be included in the match - in your case the ;
. So the character class should look like: [^;]*
. The same is true with matching the }
symbol - use a negative character class instead.
Secondly, I would try to reduce the usage of capture groups.
And finally, I would reduce the clutter and put everything you don't want to replace into the capturegroups before and after so that you get a very simple result: '$1black$2'
Try the following regex:
preg_replace('/(#css.*?color:)[^;]*([^}]*)/is','$1black$2',$var);
Note that this regex has one flaw: If you have different color
s in your decplarations (background-color, border-color,...), it will break! So you should include an additional whitespace to make sure it only captures the "real" color
declaration:
/(#css.*?\scolor:)[^;]*([^}]*)/is
This still might break if (which should not happen) per accident you have multiple color:xyz;
declarations in your rule block. Only the first one gets replace then.
Upvotes: 3
Reputation: 224
As I understand your problem, I think this will solve your issue.
$var = '
#css {
text-size: 14px;
color: red;
background: orange;
}
';
preg_match_all('/(#css\s*\{[\sA-z0-9#:;-]*color\s*:\s*[A-z0-9#]*)/', $var, $output);
$temp = preg_replace('/(color\s*:\s*[A-z0-9#]*)/im', 'color: black', $output[0][0]);
echo preg_replace('/(#css\s*\{[\sA-z0-9#:;-]*color\s*:\s*[A-z0-9#]*)/im', $temp, $var);
?>
Upvotes: 0
Reputation: 11610
The problem here is that you're matching greedily for your second capture group. In doing so, it consumes everything up until the final semicolon in your text.
What you should be doing instead is using a lazy matching group instead which will match as few characters as possible. (eg. only up until the first semicolon, rather than the last)
preg_replace('/#css(.*)color: .*?;(.*)}/is','#css$1color: black;$2 }',$var);
Upvotes: 1