Nalezení nepoužívaných definic CSS
Pokud máme hromadu starých HTML dokumentů využívajících hromadu starých stylů, tak můžeme chtít najít pravidla, která nejsou využita. Následující třída se přesně o to postará:
<?php /** Nalezení nepoužívaných definic CSS * @copyright Jakub Vrána, https://php.vrana.cz/ */ class NeedlessCss { protected $csses = array(); protected $rules = array(); private function onlyNewlines($match) { return str_repeat("\n", substr_count($match[0], "\n")); } protected function processCss($file, $href) { $file = preg_replace_callback('~/\\*.*\\*/|"([^\\\\]|\\\\.)*"|\'([^\\\\]|\\\\.)*\'|@.*[;{]~isU', array($this, 'onlyNewlines'), $file); preg_match_all('~(\\S[^{]*)\\{[^}]*}~s', $file, $matches, PREG_OFFSET_CAPTURE); foreach ($matches[1] as $val) { foreach (explode(",", $val[0]) as $rule) { $this->rules[trim($rule)][$href][] = ($val[1] ? substr_count($file, "\n", 0, $val[1]) + 1 : 1); } } } /** Nahraje styl * @param string * @param string základ pro $href * @return array */ function loadCss($href, $relative = "") { if (!strpos($href, ":")) { $href = ($href[0] == "/" ? preg_replace('~^([^/]+//[^/]+)?.*~', '\\1', $relative) : preg_replace('~[^/]*$~', '', $relative)) . $href; } $href = preg_replace('~[^/]*/\.\./~', '', $href); if (!isset($this->csses[$href])) { $this->csses[$href] = array($href => true); $file = file_get_contents($href); preg_match_all('~@import\\s+url\\((.*)\\)~U', $file, $matches); //! quotes foreach ($matches[1] as $val) { $this->csses[$href] += $this->loadCss(trim($val, '\'"'), $href); } $this->processCss($file, $href); } return $this->csses[$href]; } /** Nalezne nepoužité definice CSS * @param array ('filename.html') * @return array ('rule' => array('filename.css' => array($line)) */ function processHtmls($htmls) { $dom = new DOMDocument; foreach ($htmls as $filename) { $dom->loadHTMLFile($filename); $html = simplexml_import_dom($dom); $csses = array($filename => true); foreach ($html->xpath("//link[@rel='stylesheet']") as $link) { $csses += $this->loadCss(strval($link["href"]), $filename); } foreach ($html->xpath("//style") as $style) { $this->processCss(strval($style), $filename); // wrong line numbers } foreach ($this->rules as $rule => $definitions) { foreach ($csses as $href => $foo) { if (isset($definitions[$href])) { // can be cached $xpath = $rule; $xpath = preg_replace('~:[-\\w]+~', '', $xpath); // remove pseudo-classes $xpath = preg_replace('~#([-\\w]+)~', "[@id='\\1']", $xpath); $xpath = preg_replace('~\\.([-\\w]+)~', "[@class='\\1']", $xpath); $xpath = preg_replace('~(^|\\s)\\[~', '\\1*[', $xpath); // use * for no tag $xpath = preg_replace('~\\s+~', '//', $xpath); if (count($html->xpath("//$xpath"))) { unset($this->rules[$rule]); } break; } } } } return $this->rules; } } ?>
Použití je jednoduché:
<?php $needlessCss = new NeedlessCss; foreach ($needlessCss->processHtmls(array("a.html", "b.html")) as $rule => $definitions) { foreach ($definitions as $filename => $lines) { foreach ($lines as $line) { echo "$filename:$line:$rule\n"; } } } ?>
Metoda pracuje tak, že načte pravidla ze všech připojených stylů a převede je na dotazy XPath. Pokud tento dotaz něco najde, tak pravidlo označí jako použité a dál s ním nepracuje. Zbylá pravidla vrátí.
Zpracování stylu je poměrně jednoduché, protože nás nezajímají komentáře ani řetězce, takže je jednoduše odstraníme (zachovají se jen konce řádků, protože metoda vrací číslo řádku). Podotýkám ale, že skript rozpoznává jen pravidla CSS 1. Podporu pro složitější pravidla si můžete dopsat sami (např. >
nebo []
půjde poměrně snadno, jiná pravidla možná trochu obtížněji). Navíc dejte pozor na to, že některé definice se mohou používat v JavaScriptu.
Diskuse
v6ak:
Nebo podmínečně v šablonách... zlaté statické typování.

Schmutzka:
Chtěl jsem vyzkoušet a hází mi to chybu "file_get_contents(index.phpstyl.css)... nenalezeno" (vstup mám "index.php"), mám tam nějakou chybu, nebo to php nejdřív musím převést do html (chci-li to opravdu použít)?
Jakub Vrána
:
Ve skriptu byla chyba. Opravil jsem ji, díky za upozornění.


Schmutzka:
Nyní mám tento výpis, nebo už je to konečný? Zobrazují se tam nějaké css styly, ale nevím, zda je to ok. Hodil by se nějaký příklad, nebo také "onlinechecker".
Warning: SimpleXMLElement::xpath() [simplexmlelement.xpath]: Invalid expression in D:\www\...\checkoldcss.class.php on line 71
Warning: SimpleXMLElement::xpath() [simplexmlelement.xpath]: xmlXPathEval: evaluation failed in D:\www\...\checkoldcss.class.php on line 71
styl.css:3:a styl.css:4:a:hover styl.css:6:h1 styl.css:7:h2 styl.css:8:.bold styl.css:10:#main styl.css:14:td styl.css:18:#main_table styl.css:19:#footer styl.css:24:.auto styl.css:25:.bg1 styl.css:26:.collapse styl.css:28:.mensi styl.css:32:.nadpis styl.css:33:.novinky include/menu/anylinkcssmenu.css:1:.selectedanchor include/...
Jakub Vrána
:
Ukázka použití je určena ke spouštění z příkazové řádky – pokud se to má spouštět z webu, tak by bylo vhodné výstup nějak zformátovat.
Jinak až na ty dva warningy to vypadá jako platný výstup nepoužitých definic. Warningy budou nejspíš způsobeny použitím nějakého nepodporovaného CSS pravidla.


habendorf:
Já vím, že tento web je o PHP, ale pokud by někdo přece jen hledal rychlé řešení mimo PHP, bude se mu hodit toto rozšíření Fierefoxu: http://www.sitepoint.com/dustmeselectors/Pokud je k dispozici sitemap (XML nebo HTML), projede spider celý web.
Jan Pejša:
jen přihodím že rozšíření dustmeselectors umí hledat i v případě změn v DOMu (např. webová aplikace, která se mění při používání - po načtení aplikace se použije třeba jen 20% stylů a až při jejím používání se použije třeba 70% stylů - zbylých 30% je pak v tomto případě to "nepoužívané")

Lukas:
Zajímavý, ale nefunguje to.
Diskuse je zrušena z důvodu spamu.

