Skip to content

Instantly share code, notes, and snippets.

@hpbuniat
Created June 6, 2011 10:53
Show Gist options
  • Save hpbuniat/1010068 to your computer and use it in GitHub Desktop.
Save hpbuniat/1010068 to your computer and use it in GitHub Desktop.
Although named CssSpecificity, it finds dupes. Aims to find over-specificityfied (and so unused) rules.
<?php
/**
* Some Css-Tests
*/
class CssSpecificity {
/**
* Css-Content
*
* @var string
*/
private $_sCss;
/**
* Found selectors
*
* @var array
*/
private $_aSelectors;
/**
* Found dupes
*
* @var array
*/
private $_aDupes = array(
'selector' => array(),
'rule' => array(),
);
/**
* Load a css-file
*
* @param string $sFile
*
* @return CssSpecificity
*/
public function load($sFile) {
if (file_exists($sFile)) {
$this->_sCss .= file_get_contents($sFile);
}
return $this;
}
/**
* Run
*
* @return CssSpecificity
*/
public function run() {
$this->_cleanup()->_parse()->_dupes();
// print_r($this->_aSelectors);
print_r($this->_aDupes);
return $this;
}
/**
* Cleanup the css
*
* @return CssSpecificity
*/
private function _cleanup() {
$v = str_replace("\r\n", "\n", trim($this->_sCss));
$search = array(
"/\/\*[\d\D]*?\*\/|\t+/",
"/\s+/",
"/\}\s+/"
);
$replace = array(
null,
" ",
"}\n"
);
$v = preg_replace($search, $replace, $v);
$search = array(
"/\\;\s/",
"/\s+\{\\s+/",
"/\\:\s+\\#/",
"/,\s+/i",
"/\\:\s+\\\'/i",
"/\\:\s+([0-9]+|[A-F]+)/i"
);
$replace = array(
";",
"{",
":#",
",",
":\'",
":$1"
);
$v = preg_replace($search, $replace, $v);
$v = str_replace("\n", null, $v);
$this->_sCss = str_replace('}', '}' . PHP_EOL, $v);
return $this;
}
/**
* Parse the css to selectors, specificity and rules
*
* @return CssSpecificity
*/
private function _parse() {
$aRules = explode(PHP_EOL, $this->_sCss);
foreach ($aRules as $iLine => $sRule) {
$aParts = explode('{', $sRule);
$aSelectors = explode(',', $aParts[0]);
foreach ($aSelectors as $sSelector) {
$sSelector = trim($sSelector);
if (strlen($sSelector) > 0) {
$aStyles = explode(';', str_replace('}', '', $aParts[1]));
sort($aStyles);
$this->_aSelectors[] = array(
'selector' => $sSelector,
'specificity' => $this->_specificityfy($sSelector),
'rule' => implode('; ', $aStyles),
'line' => $iLine + 1,
);
}
}
}
return $this;
}
/**
* Determine the specificity of a selector
*
* @param string $sSelector
*
* @return int
*/
private function _specificityfy($sSelector) {
$aParts = explode(' ', $sSelector);
$iSpecificity = 0;
foreach ($aParts as $sPart) {
$sPart = trim($sPart);
$iSpecificity += (substr_count($sPart, '#') * 100);
$iSpecificity += (substr_count($sPart, '.') * 10);
$iSpecificity += (substr_count($sPart, ':') * 10);
if (in_array($sPart{0}, array(
'#',
':',
'.'
)) === false) {
$iSpecificity++;
}
}
return $iSpecificity;
}
/**
* Determine dupes
*
* @return CssSpecificity
*/
private function _dupes() {
$aStorage = array(
'selector' => array(),
'rule' => array()
);
$aTypes = array_keys($aStorage);
foreach ($this->_aSelectors as $aSelector) {
foreach ($aTypes as $sType) {
if (isset($aStorage[$sType][$aSelector[$sType]]) !== true or $aStorage[$sType][$aSelector[$sType]] === $aSelector['line']) {
$aStorage[$sType][$aSelector[$sType]] = $aSelector['line'];
}
else {
$this->_addDupe($aSelector[$sType], $aStorage[$sType][$aSelector[$sType]], $aSelector['line'], $sType);
}
}
}
return $this;
}
/**
* Add a dupe
*
* @param string $sString
* @param int $iLineA
* @param int $iLineB
* @param string $sWhat
*
* @return CssSpecificity
*/
private function _addDupe($sString, $iLineA, $iLineB, $sWhat = 'selector') {
$this->_aDupes[$sWhat][] = array(
'content' => $sString,
'first' => $iLineA,
'dupe' => $iLineB,
);
return $this;
}
}
$aArgs = getopt('f:');
if (isset($aArgs['f']) !== true) {
die('File(s) are missing' . PHP_EOL);
}
$aFiles = explode(',', $aArgs['f']);
$o = new CssSpecificity();
foreach ($aFiles as $sFile) {
$o->load($sFile);
}
$o->run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment