marcnyc
marcnyc

Reputation: 585

RegEx help to change properties in CSS

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

Answers (3)

Christoph
Christoph

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);

See the demo

Note that this regex has one flaw: If you have different colors 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

kundan Karn
kundan Karn

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

Nightfirecat
Nightfirecat

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

Related Questions