Reputation: 63
I want to parse (in a special way) a CSS file with PHP.
Example:
cssfile.css
:
#stuff {
background-color: red;
}
#content.postclass-subcontent {
background-color: red;
}
#content2.postclass-subcontent2 {
background-color: red;
}
And I want that PHP returns me each class name that have the postclass in its name.
The result look like an array having in this example:
arrayentry1:
#content.postclass-subcontent
arrayentry2:
#content2.postclass-subcontent2
But I'm worse at regular expressions. somehow search for "postclass" and then grap the hole line and put into an array.
thank you and i used it to parse a css file simliar to a confic file.
$(function () {
$.get('main.css', function (data) {
data = data.match(/(#[a-z0-9]*?\ .?postclass.*?)\s?\{/g);
if (data) {
$.each(data, function (index, value) {
value = value.substring(0, value.length - 2);
$(value.split(' .')[0]).wrapInner('<div class="' + value.split('.')[1] + '" />');
});
}
});
});
was my final code. so i can wrap easily a div around some hardcode-html without editing the layout. so i just have to edit my cssfile and add there something like
and my code searchs for the id and wraps the inner content with an div. i needed that for quickfixes when i just have to add a div around something for a clear or a background.
Upvotes: 6
Views: 29097
Reputation: 207
I was looking for a function to read css data (string or file).
All the solutions listed here are great!!!
But the function that was most useful to me was the from Gabriel Anderson
I renamed the function and extended it a bit, counting curly brakes and so on. The function is now able to read files or strings with css content.
Faulty css data generate error messages that are output under the key 'debug-errors-cssreader'.
You can also use a regExp search pattern to filter the output.
$cssStr = 'path/to/file.css'; // or CSS code
$returnSelectorOnly = true; // (optional) return css selector only
$regExpFilter = '/(your regExp)/'; // your (preg_match) pattern
$css = cssReader($cssStr, $returnSelectorOnly, $regExpFilter);
I have also extended it so that you can only display the selectors in an array.
I use the function, for example, to read out icofont/fontawesome css files, I only output the css selectors and process them further.
I have therefore programmed overview pages to see the icons that I have available.
Here a little example
$css = cssReader('icofont.css', true, '/(.icofont-)(.*?)(:before)/');
echo "<pre>";
echo var_dump($css);
echo "</pre>";
Output
array(2105) {
[0]=>
string(29) ".icofont-angry-monster:before"
[1]=>
string(23) ".icofont-bathtub:before"
[2]=>
string(26) ".icofont-bird-wings:before"
[3]=>
string(19) ".icofont-bow:before"
[4]=>
string(25) ".icofont-brain-alt:before"
[5]=>
string(29) ".icofont-butterfly-alt:before"
[6]=>
string(22) ".icofont-castle:before"
[7]=>
string(23) ".icofont-circuit:before"
[8]=>
string(20) ".icofont-dart:before"
[9]=>
string(24) ".icofont-dice-alt:before"
...
}
Maybe someone else needs the function just like me, below I put the entire function in including the example.
Thanks again for the great function!
function cssReader($cssStr, $returnSelectorOnly = false, $regExpFilter = "") {
$css = false;
$result = array();
$error = array();
$debug = true;
$isfile = false;
$filename = @trim(@pathinfo($cssStr)['filename']);
if ($cssStr != "" && $filename != "") {
$isfile = true;
}
// checking for is file and file exists
if (is_file($cssStr)) {
$cssStr = file_get_contents($cssStr);
$countCurlyBrakes_open = substr_count($cssStr, "{");
$countCurlyBrakes_close = substr_count($cssStr, "}");
if ($countCurlyBrakes_open && $countCurlyBrakes_close) {
if ($countCurlyBrakes_open == $countCurlyBrakes_close) {
$css = $cssStr;
} else {
// debug
$error[] = "#1 File error: The counting of '{' or '}' was different, '{' = ".$countCurlyBrakes_open." and '}' = ".$countCurlyBrakes_close.".";
}
} else {
// debug
$error[] = "#2 File error: Curly braces error, the counting of '{' or '}' was 0 (zero).";
}
} else {
if ($isfile) {
// debug
$error[] = "#3 File error: '".$cssStr."' the file does not exist.";
}
}
// checking for is not a file and has no file extension and is shorter than 2049 characters
// !!! // Technically speaking, your URL should never be longer than 2,048 characters. Any long than this and Internet Explorer won’t be able to load your page
if (!$isfile) {
if (!empty($cssStr)) {
$countCurlyBrakes_open = substr_count($cssStr, "{");
$countCurlyBrakes_close = substr_count($cssStr, "}");
if ($countCurlyBrakes_open && $countCurlyBrakes_close) {
if ($countCurlyBrakes_open == $countCurlyBrakes_close) {
$css = $cssStr;
} else {
// debug
$error[] = "#4 String error: The counting of '{' or '}' was different, '{' = ".$countCurlyBrakes_open." and '}' = ".$countCurlyBrakes_close.".";
}
} else {
// debug
$error[] = "#5 String error: Curly braces error, the counting of '{' or '}' was 0 (zero).";
}
} else {
// debug
$error[] = "#6 String error: (string) $cssStr was empty.";
}
}
// place errors on top of the array
if ($debug && count($error)) {
$result['debug-errors-cssreader'] = $error;
}
$regExp = "/(?ims)([a-z0-9\s\.\:#_\-@,]+)\{([^\}]*)\}/";
preg_match_all(''.$regExp.'', $css, $arr);
foreach ($arr[0] as $i => $x){
$selector = trim($arr[1][$i]);
if ($returnSelectorOnly == false || $returnSelectorOnly == "" || !$returnSelectorOnly) {
$rules = explode(';', trim($arr[2][$i]));
$rules_arr = array();
foreach ($rules as $strRule){
if (!empty($strRule)){
$rule = explode(":", $strRule);
$rules_arr[trim($rule[0])] = trim($rule[1]);
}
}
}
$selectors = explode(',', trim($selector));
foreach ($selectors as $strSel){
if ($returnSelectorOnly == false || $returnSelectorOnly == "" || !$returnSelectorOnly) {
$result[$strSel] = $rules_arr;
} else {
if ($regExpFilter == false || $regExpFilter == "" || !$regExpFilter) {
$result[] = $strSel;
} else {
if (preg_match($regExpFilter, $strSel)) {
$result[] = $strSel;
}
}
}
}
}
return $result;
}
$css = cssReader('icofont.css', true, '/(.icofont-)(.*?)(:before)/');
echo "<pre>";
echo var_dump($css);
echo "</pre>";
Upvotes: 0
Reputation: 1391
I found a solution:
function parse($file){
$css = file_get_contents($file);
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]));
$rules_arr = array();
foreach ($rules as $strRule){
if (!empty($strRule)){
$rule = explode(":", $strRule);
$rules_arr[trim($rule[0])] = trim($rule[1]);
}
}
$selectors = explode(',', trim($selector));
foreach ($selectors as $strSel){
$result[$strSel] = $rules_arr;
}
}
return $result;
}
use:
$css = parse('css/'.$user['blog'].'.php');
$css['#selector']['color'];
Upvotes: 16
Reputation: 2053
Just for completeness there is also another library for parsing CSS: sabberworm / PHP-CSS-Parser.
Homepage: http://www.sabberworm.com/blog/2010/6/10/php-css-parser
GitHub: http://github.com/sabberworm/PHP-CSS-Parser
Gist: http://packagist.org/packages/sabberworm/php-css-parser
Last update: May 31, 2017 (Stating this because the date in the blog entry may mislead you that it isn't updated anymore.)
Unfortunately this project is bit too robust. From quite simple CSS creates very chatty structure. Also before first use you have to deal with composer (I myself did end-up adding require_once for each file into parser.php).
Upvotes: 12
Reputation: 17205
In addition to Gabriel Anderson's answer to handle css @media queries, child selector >
,base64 images, and input[type="button"]:hover
function parse_css_selectors($css,$media_queries=true){
$result = $media_blocks = [];
//---------------parse css media queries------------------
if($media_queries==true){
$media_blocks=parse_css_media_queries($css);
}
if(!empty($media_blocks)){
//---------------get css blocks-----------------
$css_blocks=$css;
foreach($media_blocks as $media_block){
$css_blocks=str_ireplace($media_block,'~£&#'.$media_block.'~£&#',$css_blocks);
}
$css_blocks=explode('~£&#',$css_blocks);
//---------------parse css blocks-----------------
$b=0;
foreach($css_blocks as $css_block){
preg_match('/(\@media[^\{]+)\{(.*)\}\s+/ims',$css_block,$block);
if(isset($block[2])&&!empty($block[2])){
$result[$block[1]]=parse_css_selectors($block[2],false);
}
else{
$result[$b]=parse_css_selectors($css_block,false);
}
++$b;
}
}
else{
//---------------escape base64 images------------------
$css=preg_replace('/(data\:[^;]+);/i','$1~£&#',$css);
//---------------parse css selectors------------------
preg_match_all('/([^\{\}]+)\{([^\}]*)\}/ims', $css, $arr);
foreach ($arr[0] as $i => $x){
$selector = trim($arr[1][$i]);
$rules = explode(';', trim($arr[2][$i]));
$rules_arr = [];
foreach($rules as $strRule){
if(!empty($strRule)){
$rule = explode(":", $strRule,2);
if(isset($rule[1])){
$rules_arr[trim($rule[0])] = str_replace('~£&#',';',trim($rule[1]));
}
else{
//debug
}
}
}
$selectors = explode(',', trim($selector));
foreach ($selectors as $strSel){
if($media_queries===true){
$result[$b][$strSel] = $rules_arr;
}
else{
$result[$strSel] = $rules_arr;
}
}
}
}
return $result;
}
function parse_css_media_queries($css){
$mediaBlocks = array();
$start = 0;
while(($start = strpos($css, "@media", $start)) !== false){
// stack to manage brackets
$s = array();
// get the first opening bracket
$i = strpos($css, "{", $start);
// if $i is false, then there is probably a css syntax error
if ($i !== false){
// push bracket onto stack
array_push($s, $css[$i]);
// move past first bracket
$i++;
while (!empty($s)){
// if the character is an opening bracket, push it onto the stack, otherwise pop the stack
if ($css[$i] == "{"){
array_push($s, "{");
}
elseif ($css[$i] == "}"){
array_pop($s);
}
$i++;
}
// cut the media block out of the css and store
$mediaBlocks[] = substr($css, $start, ($i + 1) - $start);
// set the new $start to the end of the block
$start = $i;
}
}
return $mediaBlocks;
}
Resources
Upvotes: 3
Reputation: 14873
<?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
{
// Explode on the CSS attributes defined
list($name, $value) = explode(':', $attr);
$results[$matches[1][$i]][trim($name)] = trim($value);
}
return $results;
}
var_dump(BreakCSS($css));
Upvotes: 3
Reputation: 620
Here is a quick and dirty standalone hack using regex:
$input = '
#stuff {
background-color: red;
}
#content.postclass-subcontent {
background-color: red;
}
#content2.postclass-subcontent2 {
background-color: red;
}
';
$cssClassName = 'postclass';
preg_match_all('/(#[a-z0-9]*?\.?'.addcslashes($cssClassName, '-').'.*?)\s?\{/', $input, $matches);
var_dump($matches[1]);
Results in:
array(2) {
[0]=>
string(29) "#content.postclass-subcontent"
[1]=>
string(31) "#content2.postclass-subcontent2"
}
Upvotes: 0
Reputation: 46692
There is a very good CSS parser class in PHP. Use it. Here is its sample code:
<?php
include("cssparser.php");
$css = new cssparser();
$css->ParseStr("b {font-weight: bold; color: #777777;} b.test{text-decoration: underline;}");
echo $css->Get("b","color"); // returns #777777
echo $css->Get("b.test","color");// returns #777777
echo $css->Get(".test","color"); // returns an empty string
?>
Upvotes: 15