Created
April 18, 2017 22:02
-
-
Save lukecav/83e5b6d95a0f1f0c2ad274dec877358b to your computer and use it in GitHub Desktop.
W3 Total Cache Fixed - Compressor.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class Minify_CSS_Compressor | |
* @package Minify | |
*/ | |
/** | |
* Compress CSS | |
* | |
* This is a heavy regex-based removal of whitespace, unnecessary | |
* comments and tokens, and some CSS value minimization, where practical. | |
* Many steps have been taken to avoid breaking comment-based hacks, | |
* including the ie5/mac filter (and its inversion), but expect tricky | |
* hacks involving comment tokens in 'content' value strings to break | |
* minimization badly. A test suite is available. | |
* | |
* @package Minify | |
* @author Stephen Clay <[email protected]> | |
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch) | |
*/ | |
class Minify_CSS_Compressor { | |
/** | |
* Minify a CSS string | |
* | |
* @param string $css | |
* | |
* @param array $options (currently ignored) | |
* | |
* @return string | |
*/ | |
public static function process($css, $options = array()) | |
{ | |
$obj = new Minify_CSS_Compressor($options); | |
return $obj->_process($css); | |
} | |
/** | |
* @var array | |
*/ | |
protected $_options = null; | |
/** | |
* Are we "in" a hack? I.e. are some browsers targetted until the next comment? | |
* | |
* @var bool | |
*/ | |
protected $_inHack = false; | |
/** | |
* Constructor | |
* | |
* @param array $options (currently ignored) | |
*/ | |
private function __construct($options) { | |
$this->_options = $options; | |
} | |
/** | |
* Minify a CSS string | |
* | |
* @param string $css | |
* | |
* @return string | |
*/ | |
protected function _process($css) | |
{ | |
$this->_replacementHash = 'MINIFYCSS' . md5($_SERVER['REQUEST_TIME']); | |
$this->_placeholders = array(); | |
$css = preg_replace_callback('~(".*"|\'.*\')~U', array($this, '_removeQuotesCB'), $css); | |
$css = str_replace("\r\n", "\n", $css); | |
// preserve empty comment after '>' | |
// http://www.webdevout.net/css-hacks#in_css-selectors | |
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); | |
// preserve empty comment between property and value | |
// http://css-discuss.incutio.com/?page=BoxModelHack | |
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); | |
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); | |
// apply callback to all valid comments (and strip out surrounding ws | |
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' | |
,array($this, '_commentCB'), $css); | |
// remove ws around { } and last semicolon in declaration block | |
$css = preg_replace('/\\s*{\\s*/', '{', $css); | |
$css = preg_replace('/;?\\s*}\\s*/', '}', $css); | |
// remove ws surrounding semicolons | |
$css = preg_replace('/\\s*;\\s*/', ';', $css); | |
// remove ws around urls | |
$css = preg_replace('/ | |
url\\( # url( | |
\\s* | |
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) | |
\\s* | |
\\) # ) | |
/x', 'url($1)', $css); | |
// remove ws between rules and colons | |
$css = preg_replace('/ | |
\\s* | |
([{;]) # 1 = beginning of block or rule separator | |
\\s* | |
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) | |
\\s* | |
: | |
\\s* | |
(\\b|[#\'"-]) # 3 = first character of a value | |
/x', '$1$2:$3', $css); | |
// remove ws in selectors | |
$css = preg_replace_callback('/ | |
(?: # non-capture | |
\\s* | |
[^~>+,\\s]+ # selector part | |
\\s* | |
[,>+~] # combinators | |
)+ | |
\\s* | |
[^~>+,\\s]+ # selector part | |
{ # open declaration block | |
/x' | |
,array($this, '_selectorsCB'), $css); | |
// minimize hex colors | |
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' | |
, '$1#$2$3$4$5', $css); | |
// remove spaces between font families | |
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/' | |
,array($this, '_fontFamilyCB'), $css); | |
$css = preg_replace('/@import\\s+url/', '@import url', $css); | |
// replace any ws involving newlines with a single newline | |
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); | |
// separate common descendent selectors w/ newlines (to limit line lengths) | |
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); | |
// Use newline after 1st numeric value (to limit line lengths). | |
$css = preg_replace('/ | |
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value | |
\\s+ | |
/x' | |
,"$1\n", $css); | |
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ | |
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); | |
// fill placeholders | |
$css = str_replace( | |
array_keys($this->_placeholders) | |
, array_values($this->_placeholders) | |
, $css | |
); | |
if (isset($this->_options['stripCrlf']) && $this->_options['stripCrlf']) { | |
$css = preg_replace("~[\r\n]+~", ' ', $css); | |
} else { | |
$css = preg_replace("~[\r\n]+~", "\n", $css); | |
} | |
return trim($css); | |
} | |
/** | |
* Replace what looks like a set of selectors | |
* | |
* @param array $m regex matches | |
* | |
* @return string | |
*/ | |
protected function _selectorsCB($m) | |
{ | |
// remove ws around the combinators | |
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); | |
} | |
/** | |
* Process a comment and return a replacement | |
* | |
* @param array $m regex matches | |
* | |
* @return string | |
*/ | |
protected function _commentCB($m) | |
{ | |
$hasSurroundingWs = (trim($m[0]) !== $m[1]); | |
$m = $m[1]; | |
// $m is the comment content w/o the surrounding tokens, | |
// but the return value will replace the entire comment. | |
if ($m === 'keep') { | |
return '/**/'; | |
} | |
if ($m === '" "') { | |
// component of http://tantek.com/CSS/Examples/midpass.html | |
return '/*" "*/'; | |
} | |
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { | |
// component of http://tantek.com/CSS/Examples/midpass.html | |
return '/*";}}/* */'; | |
} | |
if ($this->_inHack) { | |
// inversion: feeding only to one browser | |
if (preg_match('@ | |
^/ # comment started like /*/ | |
\\s* | |
(\\S[\\s\\S]+?) # has at least some non-ws content | |
\\s* | |
/\\* # ends like /*/ or /**/ | |
@x', $m, $n)) { | |
// end hack mode after this comment, but preserve the hack and comment content | |
$this->_inHack = false; | |
return "/*/{$n[1]}/**/"; | |
} | |
} | |
if (substr($m, -1) === '\\') { // comment ends like \*/ | |
// begin hack mode and preserve hack | |
$this->_inHack = true; | |
return '/*\\*/'; | |
} | |
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ | |
// begin hack mode and preserve hack | |
$this->_inHack = true; | |
return '/*/*/'; | |
} | |
if ($this->_inHack) { | |
// a regular comment ends hack mode but should be preserved | |
$this->_inHack = false; | |
return '/**/'; | |
} | |
// Issue 107: if there's any surrounding whitespace, it may be important, so | |
// replace the comment with a single space | |
return $hasSurroundingWs // remove all other comments | |
? ' ' | |
: ''; | |
} | |
/** | |
* Process a font-family listing and return a replacement | |
* | |
* @param array $m regex matches | |
* | |
* @return string | |
*/ | |
protected function _fontFamilyCB($m) | |
{ | |
// Issue 210: must not eliminate WS between words in unquoted families | |
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); | |
$out = 'font-family:'; | |
while (null !== ($piece = array_shift($pieces))) { | |
if ($piece[0] !== '"' && $piece[0] !== "'") { | |
$piece = preg_replace('/\\s+/', ' ', $piece); | |
$piece = preg_replace('/\\s?,\\s?/', ',', $piece); | |
} | |
$out .= $piece; | |
} | |
return $out . $m[2]; | |
} | |
protected function _reservePlace($content) { | |
$placeholder = '"' . $this->_replacementHash . count($this->_placeholders) . '"'; | |
$this->_placeholders[$placeholder] = $content; | |
return $placeholder; | |
} | |
protected function _removeQuotesCB($m) { | |
return $this->_reservePlace($m[1]); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class Minify_CSS_Compressor | |
* @package Minify | |
*/ | |
/** | |
* Compress CSS | |
* | |
* This is a heavy regex-based removal of whitespace, unnecessary | |
* comments and tokens, and some CSS value minimization, where practical. | |
* Many steps have been taken to avoid breaking comment-based hacks, | |
* including the ie5/mac filter (and its inversion), but expect tricky | |
* hacks involving comment tokens in 'content' value strings to break | |
* minimization badly. A test suite is available. | |
* | |
* @package Minify | |
* @author Stephen Clay <[email protected]> | |
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch) | |
*/ | |
class Minify_CSS_Compressor { | |
/** | |
* Minify a CSS string | |
* | |
* @param string $css | |
* | |
* @param array $options (currently ignored) | |
* | |
* @return string | |
*/ | |
public static function process($css, $options = array()) | |
{ | |
$obj = new Minify_CSS_Compressor($options); | |
return $obj->_process($css); | |
} | |
/** | |
* @var array | |
*/ | |
protected $_options = null; | |
/** | |
* Are we "in" a hack? I.e. are some browsers targetted until the next comment? | |
* | |
* @var bool | |
*/ | |
protected $_inHack = false; | |
/** | |
* Constructor | |
* | |
* @param array $options (currently ignored) | |
*/ | |
private function __construct($options) { | |
$this->_options = $options; | |
} | |
/** | |
* Minify a CSS string | |
* | |
* @param string $css | |
* | |
* @return string | |
*/ | |
protected function _process($css) | |
{ | |
$css = str_replace("\r\n", "\n", $css); | |
// preserve empty comment after '>' | |
// http://www.webdevout.net/css-hacks#in_css-selectors | |
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); | |
// preserve empty comment between property and value | |
// http://css-discuss.incutio.com/?page=BoxModelHack | |
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); | |
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); | |
// apply callback to all valid comments (and strip out surrounding ws | |
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' | |
,array($this, '_commentCB'), $css); | |
// remove ws around { } and last semicolon in declaration block | |
$css = preg_replace('/\\s*{\\s*/', '{', $css); | |
$css = preg_replace('/;?\\s*}\\s*/', '}', $css); | |
// remove ws surrounding semicolons | |
$css = preg_replace('/\\s*;\\s*/', ';', $css); | |
// remove ws around urls | |
$css = preg_replace('/ | |
url\\( # url( | |
\\s* | |
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) | |
\\s* | |
\\) # ) | |
/x', 'url($1)', $css); | |
// remove ws between rules and colons | |
$css = preg_replace('/ | |
\\s* | |
([{;]) # 1 = beginning of block or rule separator | |
\\s* | |
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) | |
\\s* | |
: | |
\\s* | |
(\\b|[#\'"-]) # 3 = first character of a value | |
/x', '$1$2:$3', $css); | |
// remove ws in selectors | |
$css = preg_replace_callback('/ | |
(?: # non-capture | |
\\s* | |
[^~>+,\\s]+ # selector part | |
\\s* | |
[,>+~] # combinators | |
)+ | |
\\s* | |
[^~>+,\\s]+ # selector part | |
{ # open declaration block | |
/x' | |
,array($this, '_selectorsCB'), $css); | |
// minimize hex colors | |
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' | |
, '$1#$2$3$4$5', $css); | |
// remove spaces between font families | |
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/' | |
,array($this, '_fontFamilyCB'), $css); | |
$css = preg_replace('/@import\\s+url/', '@import url', $css); | |
// replace any ws involving newlines with a single newline | |
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); | |
// separate common descendent selectors w/ newlines (to limit line lengths) | |
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); | |
// Use newline after 1st numeric value (to limit line lengths). | |
$css = preg_replace('/ | |
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value | |
\\s+ | |
/x' | |
,"$1\n", $css); | |
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ | |
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); | |
return trim($css); | |
} | |
/** | |
* Replace what looks like a set of selectors | |
* | |
* @param array $m regex matches | |
* | |
* @return string | |
*/ | |
protected function _selectorsCB($m) | |
{ | |
// remove ws around the combinators | |
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); | |
} | |
/** | |
* Process a comment and return a replacement | |
* | |
* @param array $m regex matches | |
* | |
* @return string | |
*/ | |
protected function _commentCB($m) | |
{ | |
$hasSurroundingWs = (trim($m[0]) !== $m[1]); | |
$m = $m[1]; | |
// $m is the comment content w/o the surrounding tokens, | |
// but the return value will replace the entire comment. | |
if ($m === 'keep') { | |
return '/**/'; | |
} | |
if ($m === '" "') { | |
// component of http://tantek.com/CSS/Examples/midpass.html | |
return '/*" "*/'; | |
} | |
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { | |
// component of http://tantek.com/CSS/Examples/midpass.html | |
return '/*";}}/* */'; | |
} | |
if ($this->_inHack) { | |
// inversion: feeding only to one browser | |
if (preg_match('@ | |
^/ # comment started like /*/ | |
\\s* | |
(\\S[\\s\\S]+?) # has at least some non-ws content | |
\\s* | |
/\\* # ends like /*/ or /**/ | |
@x', $m, $n)) { | |
// end hack mode after this comment, but preserve the hack and comment content | |
$this->_inHack = false; | |
return "/*/{$n[1]}/**/"; | |
} | |
} | |
if (substr($m, -1) === '\\') { // comment ends like \*/ | |
// begin hack mode and preserve hack | |
$this->_inHack = true; | |
return '/*\\*/'; | |
} | |
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ | |
// begin hack mode and preserve hack | |
$this->_inHack = true; | |
return '/*/*/'; | |
} | |
if ($this->_inHack) { | |
// a regular comment ends hack mode but should be preserved | |
$this->_inHack = false; | |
return '/**/'; | |
} | |
// Issue 107: if there's any surrounding whitespace, it may be important, so | |
// replace the comment with a single space | |
return $hasSurroundingWs // remove all other comments | |
? ' ' | |
: ''; | |
} | |
/** | |
* Process a font-family listing and return a replacement | |
* | |
* @param array $m regex matches | |
* | |
* @return string | |
*/ | |
protected function _fontFamilyCB($m) | |
{ | |
// Issue 210: must not eliminate WS between words in unquoted families | |
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); | |
$out = 'font-family:'; | |
while (null !== ($piece = array_shift($pieces))) { | |
if ($piece[0] !== '"' && $piece[0] !== "'") { | |
$piece = preg_replace('/\\s+/', ' ', $piece); | |
$piece = preg_replace('/\\s?,\\s?/', ',', $piece); | |
} | |
$out .= $piece; | |
} | |
return $out . $m[2]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment