Reputation: 3363
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
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
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>
Use regex to get string in the class in .html
file like:
<.... class="....." >|/>
Substring and split by space
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
)
Read .css
file and all <style>
Start read multi-dimensional array, like
array [0] = container
, then search css, <style>
for .container
array [0][0] = row
, then search for .row
array [0] [0][0] = .container .row
array [0][0][0] = col-md-12
, then search for .col-md-12
array [0] [0][0] [0][0][0] = .container .row .col-md-12
If found, check it match for all 'dot' in css.
'.container' = match
'.container .row' = match
'#main .container' = match
'.container .row .detail' = not match
'.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