views:

495

answers:

3

I wanted to break a css file into an array with PHP.

Ex:

#selector{ display:block; width:100px; }
#selector a{ float:left; text-decoration:none; }

Into a php array...

array(2) {
  ["#selector"] => array(2) {
    [0] => array(1) {
      ["display"] => string(5) "block"
    }
    [1] => array(1) {
      ["width"] => string(5) "100px"
    }
  }
  ["#selector a"] => array(2) {
    [0] => array(1) {
      ["float"] => string(4) "left"
    }
    [1] => array(1) {
      ["text-decoration"] => string(4) "none"
    }
  }
}

Any ideas?

Oh : Edit : I am not worried about ill formed CSS files in this instance. Doesn't ahve to be bulletproof as long as its not greedy

+2  A: 

If I were you, I would build (or find) a real grammar for CSS, and do parsing that way. Trying to write a parser for any non-trivial expression language (and CSS clearly isn't trivial, with all the fancy selectors) have gotten me into trouble more than enough times.

Example: http://phpclasses.2by2host.com/browse/package/1289.html

Alex
+4  A: 

This should do it:

<?php

$css = <<<CSS
#selector { display:block; width:100px; }
#selector a { float:left; text-decoration:none }
CSS;

//
function BreakCSS($css)
{

    $results = array();

    preg_match_all('/(.+?)\s?\{\s?(.+?)\s?\}/', $css, $matches);
    foreach($matches[0] AS $i=>$original)
     foreach(explode(';', $matches[2][$i]) AS $attr)
      if (strlen($attr) > 0) // for missing semicolon on last element, which is legal
      {
       list($name, $value) = sexplode(':', $attr);
       $results[$matches[1][$i]][trim($name)] = trim($value);
      }
    return $results;
}
var_dump(BreakCSS($css));

Quick Explanation: The regexp is simple and boring. It just matches all "anything, possible space, curly bracket, possible space, anything, close curly bracket". From there, the first match is the selector, the second match is the attribute list. Split that by semicolons, and you're left with key/value pairs. Some trim()'s in there to get rid of whitespace, and that's about it.

I'm guessing that your next best bet would probably be to explode the selector by a comma so that you can consolidate attributes that apply to the same thing etc., but I'll save that for you. :)

Edit: As mentioned above, a real grammar parser would be more practical... but if you're assuming well-formed CSS, there's no reason why you need to do anything beyond the simplest of "anything { anything }". Depends on what you want to do with it, really.

Nathan
+1  A: 

If you need the same for CSS rules with multi selectors and with breaklines:

<?php                      
$css = "
#selector
{ display:block; 
width:100px; 
}
#selector a:hover div.asd #asd h1.asd { 
float:left; 
text-decoration:none; 
}
";
preg_match_all( '/(?ims)([a-z0-9\s\.\:#_\-@]+)\{([^\}]*)\}/', $css, $arr);

$result = array();
foreach ($arr[0] as $i => $x)
{
    $selector = trim($arr[1][$i]);
    $rules = explode(';', trim($arr[2][$i]));
    $result[$selector] = array();
    foreach ($rules as $strRule)
    {
        if (!empty($strRule))
        {
            $rule = explode(":", $strRule);
            $result[$selector][][trim($rule[0])] = trim($rule[1]);
        }
    }
}   

var_dump($result);
?>

output:

array(2) {
  ["#selector"]=>
  array(2) {
    [0]=>
    array(1) {
      ["display"]=>
      string(5) "block"
    }
    [1]=>
    array(1) {
      ["width"]=>
      string(5) "100px"
    }
  }
  ["#selector a:hover div.asd #asd h1.asd"]=>
  array(2) {
    [0]=>
    array(1) {
      ["float"]=>
      string(4) "left"
    }
    [1]=>
    array(1) {
      ["text-decoration"]=>
      string(4) "none"
    }
  }
}
inakiabt