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

10

我想用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文件的格式不正确。只要不是贪婪的,它就不需要做到完美。


哎呀,我写反了。 - Tyler Carter
5个回答

16
这应该可以做到:
<?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
sexplode()?我瞪大了眼睛,检查了一下手册 :P - Dan Lugg
我在我的一个项目的迁移过程中将其翻译成了Python,如果您感兴趣,请查看 - Ale
2
注释规则失败了 :-( - sumid

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{ ... }。

如果选择器是多个,它将无法正常工作:.box,.element,div{ ... }。 - setty
2
不错,但无法处理 background-image: url("data:image/png;base64,........ 类型的值。 - Alex G
1
这个能和 Bootstrap 的 @media 一起使用吗?我试过了,但它似乎被忽略了。 - Resheil Agarwal

3
如果我是你,我会构建(或找到)一个真正的CSS语法,并通过这种方式进行解析。试图为任何非平凡表达式语言编写解析器(而CSS显然不平凡,其中包含所有花哨的选择器)已经让我陷入麻烦很多次了。
示例:http://www.phpclasses.org/package/1289-PHP-CSS-parser-class.html

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

0
我建议在inakiabt的答案中使用选择器(?ims)([a-z0-9\s\,\.\:#_\-@*()\[\]"=]+)\{([^\}]*)\}来得到更好的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接