PluginProbe
WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript / trunk
WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript vtrunk
trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.4 1.5 1.5.1 1.6 2.0 2.0.1
wp-super-minify / includes / min / lib / Minify / CSS / Compressor.php

Compressor.php in WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript trunk, at includes/min/lib/Minify/CSS/Compressor.php

276 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Minify_CSS_Compressor
4 * @package Minify
5 */
6
7 /**
8 * Compress CSS
9 *
10 * This is a heavy regex-based removal of whitespace, unnecessary
11 * comments and tokens, and some CSS value minimization, where practical.
12 * Many steps have been taken to avoid breaking comment-based hacks,
13 * including the ie5/mac filter (and its inversion), but expect tricky
14 * hacks involving comment tokens in 'content' value strings to break
15 * minimization badly. A test suite is available.
16 *
17 * Note: This replaces a lot of spaces with line breaks. It's rumored
18 * (https://github.com/yui/yuicompressor/blob/master/README.md#global-options)
19 * that some source control tools and old browsers don't like very long lines.
20 * Compressed files with shorter lines are also easier to diff. If this is
21 * unacceptable please use CSSmin instead.
22 *
23 * @package Minify
24 * @author Stephen Clay <steve@mrclay.org>
25 * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
26 *
27 * @deprecated Use CSSmin (tubalmartin/cssmin)
28 */
29 class Minify_CSS_Compressor
30 {
31
32 /**
33 * Minify a CSS string
34 *
35 * @param string $css
36 *
37 * @param array $options (currently ignored)
38 *
39 * @return string
40 */
41 public static function process($css, $options = array())
42 {
43 $obj = new Minify_CSS_Compressor($options);
44
45 return $obj->_process($css);
46 }
47
48 /**
49 * @var array
50 */
51 protected $_options = null;
52
53 /**
54 * Are we "in" a hack? I.e. are some browsers targetted until the next comment?
55 *
56 * @var bool
57 */
58 protected $_inHack = false;
59
60 /**
61 * Constructor
62 *
63 * @param array $options (currently ignored)
64 */
65 private function __construct($options)
66 {
67 $this->_options = $options;
68 }
69
70 /**
71 * Minify a CSS string
72 *
73 * @param string $css
74 *
75 * @return string
76 */
77 protected function _process($css)
78 {
79 $css = str_replace("\r\n", "\n", $css);
80
81 // preserve empty comment after '>'
82 // http://www.webdevout.net/css-hacks#in_css-selectors
83 $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
84
85 // preserve empty comment between property and value
86 // http://css-discuss.incutio.com/?page=BoxModelHack
87 $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
88 $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
89
90 // apply callback to all valid comments (and strip out surrounding ws
91 $pattern = '@\\s*/\\*([\\s\\S]*?)\\*/\\s*@';
92 $css = preg_replace_callback($pattern, array($this, '_commentCB'), $css);
93
94 // remove ws around { } and last semicolon in declaration block
95 $css = preg_replace('/\\s*{\\s*/', '{', $css);
96 $css = preg_replace('/;?\\s*}\\s*/', '}', $css);
97
98 // remove ws surrounding semicolons
99 $css = preg_replace('/\\s*;\\s*/', ';', $css);
100
101 // remove ws around urls
102 $pattern = '/
103 url\\( # url(
104 \\s*
105 ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
106 \\s*
107 \\) # )
108 /x';
109 $css = preg_replace($pattern, 'url($1)', $css);
110
111 // remove ws between rules and colons
112 $pattern = '/
113 \\s*
114 ([{;]) # 1 = beginning of block or rule separator
115 \\s*
116 ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
117 \\s*
118 :
119 \\s*
120 (\\b|[#\'"-]) # 3 = first character of a value
121 /x';
122 $css = preg_replace($pattern, '$1$2:$3', $css);
123
124 // remove ws in selectors
125 $pattern = '/
126 (?: # non-capture
127 \\s*
128 [^~>+,\\s]+ # selector part
129 \\s*
130 [,>+~] # combinators
131 )+
132 \\s*
133 [^~>+,\\s]+ # selector part
134 { # open declaration block
135 /x';
136 $css = preg_replace_callback($pattern, array($this, '_selectorsCB'), $css);
137
138 // minimize hex colors
139 $pattern = '/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i';
140 $css = preg_replace($pattern, '$1#$2$3$4$5', $css);
141
142 // remove spaces between font families
143 $pattern = '/font-family:([^;}]+)([;}])/';
144 $css = preg_replace_callback($pattern, array($this, '_fontFamilyCB'), $css);
145
146 $css = preg_replace('/@import\\s+url/', '@import url', $css);
147
148 // replace any ws involving newlines with a single newline
149 $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
150
151 // separate common descendent selectors w/ newlines (to limit line lengths)
152 $pattern = '/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/';
153 $css = preg_replace($pattern, "$1\n$2{", $css);
154
155 // Use newline after 1st numeric value (to limit line lengths).
156 $pattern = '/
157 ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
158 \\s+
159 /x';
160 $css = preg_replace($pattern, "$1\n", $css);
161
162 // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
163 $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
164
165 return trim($css);
166 }
167
168 /**
169 * Replace what looks like a set of selectors
170 *
171 * @param array $m regex matches
172 *
173 * @return string
174 */
175 protected function _selectorsCB($m)
176 {
177 // remove ws around the combinators
178 return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
179 }
180
181 /**
182 * Process a comment and return a replacement
183 *
184 * @param array $m regex matches
185 *
186 * @return string
187 */
188 protected function _commentCB($m)
189 {
190 $hasSurroundingWs = (trim($m[0]) !== $m[1]);
191 $m = $m[1];
192 // $m is the comment content w/o the surrounding tokens,
193 // but the return value will replace the entire comment.
194 if ($m === 'keep') {
195 return '/**/';
196 }
197
198 if ($m === '" "') {
199 // component of http://tantek.com/CSS/Examples/midpass.html
200 return '/*" "*/';
201 }
202
203 if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
204 // component of http://tantek.com/CSS/Examples/midpass.html
205 return '/*";}}/* */';
206 }
207
208 if ($this->_inHack) {
209 // inversion: feeding only to one browser
210 $pattern = '@
211 ^/ # comment started like /*/
212 \\s*
213 (\\S[\\s\\S]+?) # has at least some non-ws content
214 \\s*
215 /\\* # ends like /*/ or /**/
216 @x';
217 if (preg_match($pattern, $m, $n)) {
218 // end hack mode after this comment, but preserve the hack and comment content
219 $this->_inHack = false;
220
221 return "/*/{$n[1]}/**/";
222 }
223 }
224
225 if (substr($m, -1) === '\\') { // comment ends like \*/
226 // begin hack mode and preserve hack
227 $this->_inHack = true;
228
229 return '/*\\*/';
230 }
231
232 if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
233 // begin hack mode and preserve hack
234 $this->_inHack = true;
235
236 return '/*/*/';
237 }
238
239 if ($this->_inHack) {
240 // a regular comment ends hack mode but should be preserved
241 $this->_inHack = false;
242
243 return '/**/';
244 }
245
246 // Issue 107: if there's any surrounding whitespace, it may be important, so
247 // replace the comment with a single space
248 return $hasSurroundingWs ? ' ' : ''; // remove all other comments
249 }
250
251 /**
252 * Process a font-family listing and return a replacement
253 *
254 * @param array $m regex matches
255 *
256 * @return string
257 */
258 protected function _fontFamilyCB($m)
259 {
260 // Issue 210: must not eliminate WS between words in unquoted families
261 $flags = PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY;
262 $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], -1, $flags);
263 $out = 'font-family:';
264
265 while (null !== ($piece = array_shift($pieces))) {
266 if ($piece[0] !== '"' && $piece[0] !== "'") {
267 $piece = preg_replace('/\\s+/', ' ', $piece);
268 $piece = preg_replace('/\\s?,\\s?/', ',', $piece);
269 }
270 $out .= $piece;
271 }
272
273 return $out . $m[2];
274 }
275 }
276