stefan
stefan

Reputation: 63

Parse a CSS file with PHP

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

id .postclass-class { some styles }

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

Answers (7)

MaZzIMo24
MaZzIMo24

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'.

  1. If the curly brakes are unequal, there is an error message.
  2. If it is a file and it doesn't exist then there is an error message.
  3. If the string empty (string or filename), too.

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

Gabriel Anderson
Gabriel Anderson

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

sumid
sumid

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

RafaSashi
RafaSashi

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

Pramendra Gupta
Pramendra Gupta

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

//see its same

Upvotes: 3

softcr
softcr

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

shamittomar
shamittomar

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

Related Questions