Cheetah
Compressor.php
Go to the documentation of this file.
1 <?php
22 
32  public static function process($css, $options = array())
33  {
34  $obj = new Minify_CSS_Compressor($options);
35  return $obj->_process($css);
36  }
37 
41  protected $_options = null;
42 
48  protected $_inHack = false;
49 
50 
58  private function __construct($options) {
59  $this->_options = $options;
60  }
61 
69  protected function _process($css)
70  {
71  $css = str_replace("\r\n", "\n", $css);
72 
73  // preserve empty comment after '>'
74  // http://www.webdevout.net/css-hacks#in_css-selectors
75  $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
76 
77  // preserve empty comment between property and value
78  // http://css-discuss.incutio.com/?page=BoxModelHack
79  $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
80  $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
81 
82  // apply callback to all valid comments (and strip out surrounding ws
83  $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
84  ,array($this, '_commentCB'), $css);
85 
86  // remove ws around { } and last semicolon in declaration block
87  $css = preg_replace('/\\s*{\\s*/', '{', $css);
88  $css = preg_replace('/;?\\s*}\\s*/', '}', $css);
89 
90  // remove ws surrounding semicolons
91  $css = preg_replace('/\\s*;\\s*/', ';', $css);
92 
93  // remove ws around urls
94  $css = preg_replace('/
95  url\\( # url(
96  \\s*
97  ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
98  \\s*
99  \\) # )
100  /x', 'url($1)', $css);
101 
102  // remove ws between rules and colons
103  $css = preg_replace('/
104  \\s*
105  ([{;]) # 1 = beginning of block or rule separator
106  \\s*
107  ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
108  \\s*
109  :
110  \\s*
111  (\\b|[#\'"]) # 3 = first character of a value
112  /x', '$1$2:$3', $css);
113 
114  // remove ws in selectors
115  $css = preg_replace_callback('/
116  (?: # non-capture
117  \\s*
118  [^~>+,\\s]+ # selector part
119  \\s*
120  [,>+~] # combinators
121  )+
122  \\s*
123  [^~>+,\\s]+ # selector part
124  { # open declaration block
125  /x'
126  ,array($this, '_selectorsCB'), $css);
127 
128  // minimize hex colors
129  $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
130  , '$1#$2$3$4$5', $css);
131 
132  // remove spaces between font families
133  $css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
134  ,array($this, '_fontFamilyCB'), $css);
135 
136  $css = preg_replace('/@import\\s+url/', '@import url', $css);
137 
138  // replace any ws involving newlines with a single newline
139  $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
140 
141  // separate common descendent selectors w/ newlines (to limit line lengths)
142  $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
143 
144  // Use newline after 1st numeric value (to limit line lengths).
145  $css = preg_replace('/
146  ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
147  \\s+
148  /x'
149  ,"$1\n", $css);
150 
151  // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
152  $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
153 
154  return trim($css);
155  }
156 
164  protected function _selectorsCB($m)
165  {
166  // remove ws around the combinators
167  return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
168  }
169 
177  protected function _commentCB($m)
178  {
179  $hasSurroundingWs = (trim($m[0]) !== $m[1]);
180  $m = $m[1];
181  // $m is the comment content w/o the surrounding tokens,
182  // but the return value will replace the entire comment.
183  if ($m === 'keep') {
184  return '/**/';
185  }
186  if ($m === '" "') {
187  // component of http://tantek.com/CSS/Examples/midpass.html
188  return '/*" "*/';
189  }
190  if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
191  // component of http://tantek.com/CSS/Examples/midpass.html
192  return '/*";}}/* */';
193  }
194  if ($this->_inHack) {
195  // inversion: feeding only to one browser
196  if (preg_match('@
197  ^/ # comment started like /*/
198  \\s*
199  (\\S[\\s\\S]+?) # has at least some non-ws content
200  \\s*
201  /\\* # ends like /*/ or /**/
202  @x', $m, $n)) {
203  // end hack mode after this comment, but preserve the hack and comment content
204  $this->_inHack = false;
205  return "/*/{$n[1]}/**/";
206  }
207  }
208  if (substr($m, -1) === '\\') { // comment ends like \*/
209  // begin hack mode and preserve hack
210  $this->_inHack = true;
211  return '/*\\*/';
212  }
213  if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
214  // begin hack mode and preserve hack
215  $this->_inHack = true;
216  return '/*/*/';
217  }
218  if ($this->_inHack) {
219  // a regular comment ends hack mode but should be preserved
220  $this->_inHack = false;
221  return '/**/';
222  }
223  // Issue 107: if there's any surrounding whitespace, it may be important, so
224  // replace the comment with a single space
225  return $hasSurroundingWs // remove all other comments
226  ? ' '
227  : '';
228  }
229 
237  protected function _fontFamilyCB($m)
238  {
239  $m[1] = preg_replace('/
240  \\s*
241  (
242  "[^"]+" # 1 = family in double qutoes
243  |\'[^\']+\' # or 1 = family in single quotes
244  |[\\w\\-]+ # or 1 = unquoted family
245  )
246  \\s*
247  /x', '$1', $m[1]);
248  return 'font-family:' . $m[1] . $m[2];
249  }
250 }
Minify_CSS_Compressor\_commentCB
_commentCB($m)
Definition: Compressor.php:177
php
Minify_CSS_Compressor\_fontFamilyCB
_fontFamilyCB($m)
Definition: Compressor.php:237
Minify_CSS_Compressor\$_inHack
$_inHack
Definition: Compressor.php:48
Minify_CSS_Compressor\process
static process($css, $options=array())
Definition: Compressor.php:32
Minify_CSS_Compressor
Definition: Compressor.php:21
Minify_CSS_Compressor\_selectorsCB
_selectorsCB($m)
Definition: Compressor.php:164
Minify_CSS_Compressor\_process
_process($css)
Definition: Compressor.php:69
Minify_CSS_Compressor\$_options
$_options
Definition: Compressor.php:41