PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
wpsso / lib / ext / compressor.php

compressor.php in WPSSO Core – Complete Schema Markup and Meta Tags 22.7.0, at lib/ext/compressor.php

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