AriehGlazer
AriehGlazer

Reputation: 2320

Regex that replaces a css value in CSS

Hey. I'm trying to parse a CSS file using PHP.

I'm trying to run this expression:

"/(". $selector . "\\s*{[\\w\\s:\\-;()#]*)(" . $property . ":)([^;}]+)(}?)/Ui"

This regex is supposed to pin down a specific property within a selector and allow me to change it's value/remove it.

rational:

  1. match the selector and it's text preceding to the property
  2. match the property name
  3. match the property value
  4. if the value ends by the } sign - catch it as well

The broken bit is #3 - For some unknown reason, when I run this through preg_match, the value group(#3) only catches the first char.

For example, running this expression:

preg_replace("/(h1\s*{[\w\s:\-;()#]*)(font-size:)(([^;}])+)(}?)/Ui","$1 $4",$css);

(find the font-size property of the h1 selector, and remove the property and value)

on this css group:

h1{
    background:#fff;
    font-size:10px;
    text-align:underline;
    color:#abc;
}

I get:

h1{
    background:#fff;
    /* note that although the property was matched and removed,
       the value only matched the 1st char: 1 */ 
    0px; 
    text-align:underline;
    color:#abc;
}

I've tried to check the expression through some test tools and it worked fine, so I'm guessing this is a preg* specific problem.

any ideas what I'm doing wrong?

Upvotes: 2

Views: 1224

Answers (4)

user557597
user557597

Reputation:

Your torturing yourself with the "ungreedy" modifier '/U', I wouldn't use it.
The real problem is that you are making provisions for 'property:value' ending to be either a ';' or a '}'

So, it can be either [;}] that is your final delimeter. There is really no easy way to implement that while preserving the formatting, I redid your regex a little. Think php does Perl compatible regex, so rearranged and doctored up the last part to a reasonable working model. Its not for the faint of heart.

The modifiers are Perl's /xi stands for expanded and case insensitive.
Don't use php's /U modifier on this! There is only 2 capture groups now, 1 & 2.

Regex as unquoted with variables:

/((?:$selector)\s*\{[\w\s:;()#-]*?)\s*(?:$property)\s*:(?:(?!\s*\})[^;])+(?:(?=;);[^\S\n]*)?(\s*\}?)/i;

Regex catted as a string:

'/((?:' . $selector . ')\s*\{[\w\s:;()#-]*?)\s*(?:' . $property. ')\s*:(?:(?!\s*\})[^;])+(?:(?=;);[^\S\n]*)?(\s*\}?)/xi;'

Test case in Perl:

use strict;
use warnings;

my ($selector, $property) = ( 'h1 | h2', 'font-size' );

my $sample = 
'
h1{
    background:#fff;
    font-size:10px;
    text-align:underline;
    color:#abc;
}
h2{
    text-align:strikethrough;
    background:#fefe;
    color:#dbd;
    font-size:10px
} ';

my $rx = qr/
  (                      # group 1
      (?:$selector)
      \s*
      \{
         [\w\s:;()#-]*?
  )
         \s* (?:$property) \s*: (?: (?!\s*\}) [^;] )+
         (?:
             (?=;) ;[^\S\n]*
         )?
  (                      # group 2
      \s*
      \}?
  )
/xi;

print $rx,"\n\n";

$sample =~ s/$rx/$1$2/g;

print $sample,"\n";

Output:

(?ix-sm:
  (                      # group 1
      (?:h1 | h2)
      \s*
      \{
         [\w\s:;()#-]*?
  )
         \s* (?:font-size) \s*: (?: (?!\s*\}) [^;] )+
         (?:
             (?=;) ;[^\S\n]*
         )?
  (                      # group 2
      \s*
       \}?
  )
)

h1{
    background:#fff;
    text-align:underline;
    color:#abc;
}
h2{
    text-align:strikethrough;
    background:#fefe;
    color:#dbd;
}
/((?:h1 | h2)\s*\{[\w\s:;()#-]*?)\s*(?:font-size)\s*:(?:(?!\s*\})[^;])+(?:(?=;);[^\S\n]*)?(\s*\}?)/xi;

Upvotes: 0

Petr Peller
Petr Peller

Reputation: 8826

Parsing CSS only with regexp is masochism.

You can find some information how to parse CSS in this CSS object model specification. Sorry I was able to find only Editor's draft, the other URLs seems to be broken.

Upvotes: 1

Floern
Floern

Reputation: 33904

(([^;}])+)(}?) does actually match only one char (with modifier U).

You have to use something like this: ([^;}]+)(;|})

Additionally it's not bad to escape curly braces.

The full RegEx:

preg_replace('/('.$selector.'\s*\{[\w\s:\-;\(\)#]*)('.$property.'\s*:)([^;\}]+)(;|\})/Ui', '$1 $4', $css);

Upvotes: 0

davin
davin

Reputation: 45525

dont regroup the 3rd brackets, try:

preg_replace("/(h1\s*{[\w\s:\-;()#]*)(font-size\s*:)([^;}]+)(}?)/Ui","$1 $4",$css);

in addition, i added \s* after the property, to allow it to catch things like

font-size : 10px

Upvotes: 0

Related Questions