使用PHP将CSS文件拆分为数组

时间:2009-07-31 21:56:13

标签: php css regex parsing

我想用PHP将一个css文件分成一个数组。

例如:

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

进入php数组......

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"
    }
  }
}

有什么想法吗?

哦:编辑:我并不担心这个实例中形成错误的CSS文件。只要它不贪心,就不能防弹

5 个答案:

答案 0 :(得分:11)

这应该这样做:

<?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(trim($attr)) > 0) // for missing semicolon on last element, which is legal
            {
                list($name, $value) = explode(':', $attr);
                $results[$matches[1][$i]][trim($name)] = trim($value);
            }
    return $results;
}
var_dump(BreakCSS($css));

快速说明:正则表达式简单而无聊。它只匹配所有“任何东西,可能的空间,花括号,可能的空间,任何东西,紧密的大括号”。从那里,第一个匹配是选择器,第二个匹配是属性列表。用分号拆分,你留下键/值对。一些trim()在那里摆脱空白,这就是它。

我猜你的下一个最好的选择可能是用逗号分解选择器,这样你就可以合并适用于同一事物的属性等,但我会为你保存。 :)

编辑:如上所述,真正的语法分析器会更实用......但是如果你假设格式良好的CSS,那么除了最简单的“任何{任何东西}”之外你没有必要做任何事情。 。取决于你想用它做什么,真的。

答案 1 :(得分:6)

如果您对具有多选择器和分隔线的CSS规则需要相同的内容:

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

输出:

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"
    }
  }
}

更新:添加了对多个选择器的支持,例如:.box,.element,div {...}

答案 2 :(得分:3)

如果我是你,我会为CSS构建(或找到)一个真正的语法,并以这种方式进行解析。试图为任何非平凡的表达式语言编写一个解析器(而CSS显然不是微不足道的,所有花哨的选择器)让我遇到麻烦的时间超过了足够多。

示例:http://www.phpclasses.org/package/1289-PHP-CSS-parser-class.html

答案 3 :(得分:0)

我建议answer of inakiabt中的选择器ng-if以获得更好的解决方案。

答案 4 :(得分:0)

我也会添加我的解决方案。我不得不将一个包含所有媒体查询的大型 css 文件拆分为单独的文件,因此页面速度的移动评级提高了 10 分,因为它不需要等待整个大型 css 文件。

输出是根据媒体查询拆分的数组。

$path = 'my-css-file.css'
$css = file_get_contents($path);
$cleanCss = [];

// Remove css comments
$clean1 = explode('/*', $css);
foreach($clean1 as $clean2) {
    $clean3 = explode('*/', $clean2);
    $cleanCss[] = $clean3[count($clean3) -1];
}
$css = implode('', $cleanCss);
$temp = explode('}', $css);
$params = [];
$type = 'all';
$nextBracketIsNotMediaEnd = false;
foreach($temp as $tem2) {
    $data = explode('{', $tem2);
    if (count($data) == 1) {
        if ($nextBracketIsNotMediaEnd) {
            $nextBracketIsNotMediaEnd = false;
        } else {
            $type = 'all';
            continue;
        }
    }
    if (count($data) == 3) {
        $typeTemp = trim($data[0]);
        if (substr( $typeTemp, 0, 6 ) === "@media" ) {
            $type = $typeTemp;
            array_shift($data);
        } else {
            $data[1] = $data[0].$data[1];
            $nextBracketIsNotMediaEnd = true;
        }
    }
    if (count($data) == 2) {
        $rows = explode(';',$data[1]);

        $tempData = [];
        foreach($rows as $row) {
            $paramsinline = explode(':', $row);
            if (empty($paramsinline[0]) || empty($paramsinline[1])){
                continue;
            }
            $tempData[trim($paramsinline[0])] = trim($paramsinline[1]);
        }
        $selector = trim($data[0]);
        if (!empty($tempData)) {
            if (empty($params[$type][$selector])) {
                $params[$type][$selector] = $tempData;
            } else {
                $params[$type][$selector] = array_merge($params[$type][$selector], $tempData);
            }
        }
    }
}

// Here you have the output, splitted by media queries
print_r($params);