Jose Nobile
Jose Nobile

Reputation: 3363

Extract all the used styles from a CSS file in a HTML document using PHP

I want extract all the used styles from a CSS file in a HTML document.

If there is a HTML like:

<div class="blue"></div>

And a CSS file like:

.blue {
  background-color: #2196F3 !important; }
.red {
  background-color: #F44336 !important; }

The PHP Code should produce an output as:

.blue {
  background-color: #2196F3 !important; }

The purpose is bring to CSS styles to the HTML document to prevent a render-blocking CSS.

I think it's needed a HTML and CSS parser.

Upvotes: 2

Views: 2129

Answers (2)

Jose Nobile
Jose Nobile

Reputation: 3363

2021 updated: There are much better tools to perform this task, for example: https://github.com/FullHuman/purgecss

I achieved my exact result, I think this is going to help many people that want to optimize their website as suggested by Google Pagespeed Insights.

Dependencies: https://github.com/paquettg/php-html-parser https://github.com/sabberworm/PHP-CSS-Parser

use PHPHtmlParser\Dom;
ob_start();
?><!doctype html>
<html>
<head>
.....

</body>
</html><?PHP
$html = ob_get_contents();
ob_end_clean();
$md5 = md5($html);

    //add to HTML in style labels the CSS Used and minimized the result
    $dom = new Dom;
    $dom->load($html);
    $style = '';
    foreach($css_files as $css_file){
        $md5CSS = md5_file($css_file);
        
            $cssContent = file_get_contents($css_file);
            $oSettings = Sabberworm\CSS\Settings::create()->withMultibyteSupport(false)/*->beStrict()*/;
            $oCssParser = new Sabberworm\CSS\Parser($cssContent, $oSettings);
            $cssParsed = $oCssParser->parse();
            $memcached->set($md5CSS, $cssParsed);
        }
        foreach ($cssParsed->getContents() as $oItem) {
            if ($oItem instanceof Sabberworm\CSS\CSSList\KeyFrame)
                continue;
             if ($oItem instanceof Sabberworm\CSS\RuleSet\AtRuleSet)
                continue;
            if($oItem instanceof Sabberworm\CSS\RuleSet\DeclarationBlock){
                $oBlock = $oItem;
                $selectors = array();
                foreach($oBlock->getSelectors() as $oSelector)
                    $selectors[] = $oSelector->getSelector();
                if(count($dom->find(implode(",",$selectors))) > 0)
                    $style .= $oBlock->render(Sabberworm\CSS\OutputFormat::createCompact());
            }
            if ($oItem instanceof Sabberworm\CSS\CSSList\AtRuleBlockList) {
                foreach($oItem->getContents() as $oBlock) {
                    $selectors = array();
                    foreach($oBlock->getSelectors() as $oSelector)
                        $selectors[] = $oSelector->getSelector();
                    if(count($dom->find(implode(",",$selectors))) > 0){
                        $style .= $oItem->render(Sabberworm\CSS\OutputFormat::createCompact());
                        break;
                    }
                }
            }            
        }
    }
    $styleLabel = '<style type="text/css">'.$style.'</style>';
    $html = str_replace("</head>", $styleLabel."\n</head>",$html);

Upvotes: 0

Sruit A.Suk
Sruit A.Suk

Reputation: 7263

Just a rough idea,

supposed my html file is like:

<body class="container">
 <div class="row bg-red">
  <div class="col-md-12">
  </div>
 </div>
</body>
  1. Use regex to get string in the class in .html file like:

    <.... class="....." >|/>
    
  2. Substring and split by space

  3. Store it, in multi-dimensional array, (to prepare to store class nesting like: [0] = container, [0][0] = row, [0][0][0] = col-md-12, [0][1] = bg-red)

  4. Read .css file and all <style>

  5. Start read multi-dimensional array, like

    • Check for array [0] = container, then search css, <style> for .container
    • Check for array [0][0] = row, then search for .row
    • Check for array [0] [0][0] = .container .row
    • Check for array [0][0][0] = col-md-12, then search for .col-md-12
    • Check for array [0] [0][0] [0][0][0] = .container .row .col-md-12
    • If found, check it match for all 'dot' in css.

      • ex. '.container' = match
      • ex. '.container .row' = match
      • ex. '#main .container' = match
      • ex. '.container .row .detail' = not match
      • ex. '.container > .row' = match

Note: it just a very rough idea, you need to think more to handle the css that require continue such as .container > .row

Upvotes: 2

Related Questions