Search code examples
phpcss-parsing

Merging multiple CSS files into one in PHP


i'm working on a script that generates multiple CSS into one. And Here is the script.

    $full_string = "";

    foreach($allfiles as $curfile => $file) {           
        $file = $PATH[$curfile] . $file;
        $file_open = fopen($file , 'r');
        $concat = "\n" . fread($file_open , filesize($file)) . "\n";
        $full_string .= $concat;
        fclose($file_open);
    }

    return $full_string;

Here i'm combining all the CSS files into one. But the problem now is i have to compare the current CSS($file) with another css(let's consider it as overrider.css). And if $file is having a style like,

h1 {
    color: white; 
    background: teal; 
    FONT-FAMILY: arial, helvetica, lucida-sans, sans-serif; 
    FONT-SIZE: 18pt; 
    FONT-STYLE: normal; 
    FONT-VARIANT: normal;
}

body 
{
    font-family: arial;
    FONT-SIZE: 14px; 
}

and if overrider.css is having a style like,

body 
{
    font-family: Calibri;
    color: #3E83F1; 
}

Then final CSS(output.css) generated should be,

h1 {
    color: white; 
    background: teal; 
    FONT-FAMILY: arial, helvetica, lucida-sans, sans-serif; 
    FONT-SIZE: 18pt; 
    FONT-STYLE: normal; 
    FONT-VARIANT: normal;
}

body 
{
    font-family: Calibri;
    FONT-SIZE: 14px;
    color: #3E83F1;
}

Here, since style for body in override.css have font-family, it replaces the font-family property in original CSS and since the color is a new property which is not present in ($file) which is original CSS file, so it should add the property to original CSS file. So how to achieve this in PHP, since i don't have any idea on parsing CSS. Any idea on this would be greatly appreciated.

Please note that i need to generate a new CSS file by giving the input as file1($file) and file2(override.css) and we need to genrate output.css with the styles overridden.

Thanks in advance.


Solution

  • There are a few CSS parsers available (google "php css parser"), like this one that I didn't try, but seems interesting. But personally I'd do the parsing myself - following that kind of pseudo-PHP algorithm

    • read all the files into one string Str, with all "\n", "\r" and "\t" replaced by a space (to make parsing (a bit) easier)

    then, function to process (selector => rules)

    func deal with selectors and rules:
    
    rules = array()
    do {
      S = string from current pos to next `{` excluded (selectors)
      R = string from '{' to next '}' (rules)
      r = explode(';', R) 
      lr = make array of rules from r trimmed elements
      s = explode (',', S)
      ls = make array of [selector => lr]
    
      // same sel: latest rule overwrite existing, added if not exist
      merge ls into rules 
    } while (not '}' and not '@' and not EOF); // the '}' would be the closing media
    
    return rules
    

    Main function to deal with medias, and then call the above function

    medias = array();
    
    func deal with Str
    do {
      if (first non blank char is @) {
         media = array of listed medias
         eat fist '{'
      }
      else {
         media = array ('GLOBAL')
      }
      selectorsrules = deal with selectors and rules(rest of Str)
    
      foreach (media as m) {
        merge in medias(m) selectorsrules, same procedure as above
      }
    } while (not EOF);
    

    Interesting project but I don't have the time to implement it fully. Result is in medias array.