1
|
|
|
<?php
|
2
|
|
|
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
3
|
|
|
|
4
|
|
|
class autoptimizeStyles extends autoptimizeBase {
|
5
|
|
|
|
6
|
|
|
const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},]*)/i';
|
7
|
|
|
|
8
|
|
|
private $css = array();
|
9
|
|
|
private $csscode = array();
|
10
|
|
|
private $url = array();
|
11
|
|
|
private $restofcontent = '';
|
12
|
|
|
private $datauris = false;
|
13
|
|
|
private $hashmap = array();
|
14
|
|
|
private $alreadyminified = false;
|
15
|
|
|
private $inline = false;
|
16
|
|
|
private $defer = false;
|
17
|
|
|
private $defer_inline = false;
|
18
|
|
|
private $whitelist = '';
|
19
|
|
|
private $cssinlinesize = '';
|
20
|
|
|
private $cssremovables = array();
|
21
|
|
|
private $include_inline = false;
|
22
|
|
|
private $inject_min_late = '';
|
23
|
|
|
|
24
|
|
|
//Reads the page and collects style tags
|
25
|
|
|
public function read($options) {
|
26
|
|
|
$noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content );
|
27
|
|
|
if ($noptimizeCSS) return false;
|
28
|
|
|
|
29
|
|
|
$whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content );
|
30
|
|
|
if (!empty($whitelistCSS)) {
|
31
|
|
|
$this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS)));
|
|
|
|
|
32
|
|
|
}
|
33
|
|
|
|
34
|
|
|
if ($options['nogooglefont'] == true) {
|
35
|
|
|
$removableCSS = "fonts.googleapis.com";
|
36
|
|
|
} else {
|
37
|
|
|
$removableCSS = "";
|
38
|
|
|
}
|
39
|
|
|
$removableCSS = apply_filters( 'autoptimize_filter_css_removables', $removableCSS);
|
40
|
|
View Code Duplication |
if (!empty($removableCSS)) {
|
|
|
|
|
41
|
|
|
$this->cssremovables = array_filter(array_map('trim',explode(",",$removableCSS)));
|
42
|
|
|
}
|
43
|
|
|
|
44
|
|
|
$this->cssinlinesize = apply_filters('autoptimize_filter_css_inlinesize',256);
|
45
|
|
|
|
46
|
|
|
// filter to "late inject minified CSS", default to true for now (it is faster)
|
47
|
|
|
$this->inject_min_late = apply_filters('autoptimize_filter_css_inject_min_late',true);
|
48
|
|
|
|
49
|
|
|
// Remove everything that's not the header
|
50
|
|
View Code Duplication |
if ( apply_filters('autoptimize_filter_css_justhead',$options['justhead']) == true ) {
|
|
|
|
|
51
|
|
|
$content = explode('</head>',$this->content,2);
|
52
|
|
|
$this->content = $content[0].'</head>';
|
53
|
|
|
$this->restofcontent = $content[1];
|
54
|
|
|
}
|
55
|
|
|
|
56
|
|
|
// include inline?
|
57
|
|
|
if( apply_filters('autoptimize_css_include_inline',$options['include_inline']) == true ) {
|
58
|
|
|
$this->include_inline = true;
|
59
|
|
|
}
|
60
|
|
|
|
61
|
|
|
// what CSS shouldn't be autoptimized
|
62
|
|
|
$excludeCSS = $options['css_exclude'];
|
63
|
|
|
$excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $excludeCSS, $this->content );
|
64
|
|
|
if ($excludeCSS!=="") {
|
65
|
|
|
$this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS)));
|
|
|
|
|
66
|
|
|
} else {
|
67
|
|
|
$this->dontmove = "";
|
68
|
|
|
}
|
69
|
|
|
|
70
|
|
|
// should we defer css?
|
71
|
|
|
// value: true/ false
|
72
|
|
|
$this->defer = $options['defer'];
|
73
|
|
|
$this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content );
|
74
|
|
|
|
75
|
|
|
// should we inline while deferring?
|
76
|
|
|
// value: inlined CSS
|
77
|
|
|
$this->defer_inline = $options['defer_inline'];
|
78
|
|
|
$this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->defer_inline, $this->content );
|
79
|
|
|
|
80
|
|
|
// should we inline?
|
81
|
|
|
// value: true/ false
|
82
|
|
|
$this->inline = $options['inline'];
|
83
|
|
|
$this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline, $this->content );
|
84
|
|
|
|
85
|
|
|
// get cdn url
|
86
|
|
|
$this->cdn_url = $options['cdn_url'];
|
|
|
|
|
87
|
|
|
|
88
|
|
|
// Store data: URIs setting for later use
|
89
|
|
|
$this->datauris = $options['datauris'];
|
90
|
|
|
|
91
|
|
|
// noptimize me
|
92
|
|
|
$this->content = $this->hide_noptimize($this->content);
|
93
|
|
|
|
94
|
|
|
// exclude (no)script, as those may contain CSS which should be left as is
|
95
|
|
View Code Duplication |
if ( strpos( $this->content, '<script' ) !== false ) {
|
|
|
|
|
96
|
|
|
$this->content = preg_replace_callback(
|
97
|
|
|
'#<(?:no)?script.*?<\/(?:no)?script>#is',
|
98
|
|
|
create_function(
|
|
|
|
|
99
|
|
|
'$matches',
|
100
|
|
|
'return "%%SCRIPT%%".base64_encode($matches[0])."%%SCRIPT%%";'
|
101
|
|
|
),
|
102
|
|
|
$this->content
|
103
|
|
|
);
|
104
|
|
|
}
|
105
|
|
|
|
106
|
|
|
// Save IE hacks
|
107
|
|
|
$this->content = $this->hide_iehacks($this->content);
|
108
|
|
|
|
109
|
|
|
// hide comments
|
110
|
|
|
$this->content = $this->hide_comments($this->content);
|
111
|
|
|
|
112
|
|
|
// Get <style> and <link>
|
113
|
|
|
if(preg_match_all('#(<style[^>]*>.*</style>)|(<link[^>]*stylesheet[^>]*>)#Usmi',$this->content,$matches)) {
|
114
|
|
|
foreach($matches[0] as $tag) {
|
115
|
|
|
if ($this->isremovable($tag,$this->cssremovables)) {
|
116
|
|
|
$this->content = str_replace($tag,'',$this->content);
|
117
|
|
|
} else if ($this->ismovable($tag)) {
|
118
|
|
|
// Get the media
|
119
|
|
|
if(strpos($tag,'media=')!==false) {
|
120
|
|
|
preg_match('#media=(?:"|\')([^>]*)(?:"|\')#Ui',$tag,$medias);
|
121
|
|
|
$medias = explode(',',$medias[1]);
|
122
|
|
|
$media = array();
|
123
|
|
|
foreach($medias as $elem) {
|
124
|
|
|
if (empty($elem)) { $elem="all"; }
|
125
|
|
|
$media[] = $elem;
|
126
|
|
|
}
|
127
|
|
|
} else {
|
128
|
|
|
// No media specified - applies to all
|
129
|
|
|
$media = array('all');
|
130
|
|
|
}
|
131
|
|
|
$media = apply_filters( 'autoptimize_filter_css_tagmedia',$media,$tag );
|
132
|
|
|
|
133
|
|
|
if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
|
134
|
|
|
// <link>
|
135
|
|
|
$explUrl = explode('?',$source[2],2);
|
136
|
|
|
$url = $explUrl[0];
|
137
|
|
|
$path = $this->getpath($url);
|
138
|
|
|
|
139
|
|
|
if($path!==false && preg_match('#\.css$#',$path)) {
|
140
|
|
|
// Good link
|
141
|
|
|
$this->css[] = array($media,$path);
|
142
|
|
|
}else{
|
143
|
|
|
// Link is dynamic (.php etc)
|
144
|
|
|
$tag = '';
|
145
|
|
|
}
|
146
|
|
|
} else {
|
147
|
|
|
// inline css in style tags can be wrapped in comment tags, so restore comments
|
148
|
|
|
$tag = $this->restore_comments($tag);
|
149
|
|
|
preg_match('#<style.*>(.*)</style>#Usmi',$tag,$code);
|
150
|
|
|
|
151
|
|
|
// and re-hide them to be able to to the removal based on tag
|
152
|
|
|
$tag = $this->hide_comments($tag);
|
153
|
|
|
|
154
|
|
|
if ( $this->include_inline ) {
|
155
|
|
|
$code = preg_replace('#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm','$1',$code[1]);
|
156
|
|
|
$this->css[] = array($media,'INLINE;'.$code);
|
157
|
|
|
} else {
|
158
|
|
|
$tag = '';
|
159
|
|
|
}
|
160
|
|
|
}
|
161
|
|
|
|
162
|
|
|
// Remove the original style tag
|
163
|
|
|
$this->content = str_replace($tag,'',$this->content);
|
164
|
|
|
} else {
|
165
|
|
|
// excluded CSS, minify if getpath
|
166
|
|
|
if (preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
|
167
|
|
|
$explUrl = explode('?',$source[2],2);
|
168
|
|
|
$url = $explUrl[0];
|
169
|
|
|
$path = $this->getpath($url);
|
170
|
|
|
|
171
|
|
|
if ($path && apply_filters('autoptimize_filter_css_minify_excluded',false)) {
|
172
|
|
|
$_CachedMinifiedUrl = $this->minify_single($path);
|
173
|
|
|
|
174
|
|
|
if (!empty($_CachedMinifiedUrl)) {
|
175
|
|
|
// replace orig URL with URL to cache
|
176
|
|
|
$newTag = str_replace($url, $_CachedMinifiedUrl, $tag);
|
177
|
|
|
} else {
|
178
|
|
|
$newTag = $tag;
|
179
|
|
|
}
|
180
|
|
|
|
181
|
|
|
// remove querystring from URL
|
182
|
|
View Code Duplication |
if ( !empty($explUrl[1]) ) {
|
|
|
|
|
183
|
|
|
$newTag = str_replace("?".$explUrl[1],"",$newTag);
|
184
|
|
|
}
|
185
|
|
|
|
186
|
|
|
// and replace
|
187
|
|
|
$this->content = str_replace($tag,$newTag,$this->content);
|
188
|
|
|
}
|
189
|
|
|
}
|
190
|
|
|
}
|
191
|
|
|
}
|
192
|
|
|
return true;
|
193
|
|
|
}
|
194
|
|
|
// Really, no styles?
|
195
|
|
|
return false;
|
196
|
|
|
}
|
197
|
|
|
|
198
|
|
|
// Joins and optimizes CSS
|
199
|
|
|
public function minify() {
|
200
|
|
|
foreach($this->css as $group) {
|
201
|
|
|
list($media,$css) = $group;
|
202
|
|
|
if(preg_match('#^INLINE;#',$css)) {
|
203
|
|
|
// <style>
|
204
|
|
|
$css = preg_replace('#^INLINE;#','',$css);
|
205
|
|
|
$css = $this->fixurls(ABSPATH.'/index.php',$css);
|
206
|
|
|
$tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, "" );
|
207
|
|
|
if ( has_filter('autoptimize_css_individual_style') && !empty($tmpstyle) ) {
|
208
|
|
|
$css=$tmpstyle;
|
209
|
|
|
$this->alreadyminified=true;
|
210
|
|
|
}
|
211
|
|
|
} else {
|
212
|
|
|
//<link>
|
213
|
|
|
if($css !== false && file_exists($css) && is_readable($css)) {
|
214
|
|
|
$cssPath = $css;
|
215
|
|
|
$css = $this->fixurls($cssPath,file_get_contents($cssPath));
|
216
|
|
|
$css = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$css);
|
217
|
|
|
$tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath );
|
218
|
|
View Code Duplication |
if (has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
|
|
|
|
|
219
|
|
|
$css=$tmpstyle;
|
220
|
|
|
$this->alreadyminified=true;
|
221
|
|
|
} else if ($this->can_inject_late($cssPath,$css)) {
|
222
|
|
|
$css="/*!%%INJECTLATER%%".base64_encode($cssPath)."|".md5($css)."%%INJECTLATER%%*/";
|
223
|
|
|
}
|
224
|
|
|
} else {
|
225
|
|
|
// Couldn't read CSS. Maybe getpath isn't working?
|
226
|
|
|
$css = '';
|
227
|
|
|
}
|
228
|
|
|
}
|
229
|
|
|
|
230
|
|
|
foreach($media as $elem) {
|
231
|
|
|
if(!isset($this->csscode[$elem]))
|
232
|
|
|
$this->csscode[$elem] = '';
|
233
|
|
|
$this->csscode[$elem] .= "\n/*FILESTART*/".$css;
|
234
|
|
|
}
|
235
|
|
|
}
|
236
|
|
|
|
237
|
|
|
// Check for duplicate code
|
238
|
|
|
$md5list = array();
|
239
|
|
|
$tmpcss = $this->csscode;
|
240
|
|
|
foreach($tmpcss as $media => $code) {
|
241
|
|
|
$md5sum = md5($code);
|
242
|
|
|
$medianame = $media;
|
243
|
|
|
foreach($md5list as $med => $sum) {
|
244
|
|
|
// If same code
|
245
|
|
|
if($sum === $md5sum) {
|
246
|
|
|
//Add the merged code
|
247
|
|
|
$medianame = $med.', '.$media;
|
248
|
|
|
$this->csscode[$medianame] = $code;
|
249
|
|
|
$md5list[$medianame] = $md5list[$med];
|
250
|
|
|
unset($this->csscode[$med], $this->csscode[$media]);
|
251
|
|
|
unset($md5list[$med]);
|
252
|
|
|
}
|
253
|
|
|
}
|
254
|
|
|
$md5list[$medianame] = $md5sum;
|
255
|
|
|
}
|
256
|
|
|
unset($tmpcss);
|
257
|
|
|
|
258
|
|
|
// Manage @imports, while is for recursive import management
|
259
|
|
|
foreach ($this->csscode as &$thiscss) {
|
260
|
|
|
// Flag to trigger import reconstitution and var to hold external imports
|
261
|
|
|
$fiximports = false;
|
262
|
|
|
$external_imports = "";
|
263
|
|
|
|
264
|
|
|
// remove comments to avoid importing commented-out imports
|
265
|
|
|
$thiscss_nocomments = preg_replace('#/\*.*\*/#Us','',$thiscss);
|
266
|
|
|
|
267
|
|
|
while(preg_match_all('#@import.*(?:;|$)#Um',$thiscss_nocomments,$matches)) {
|
268
|
|
|
foreach($matches[0] as $import) {
|
269
|
|
|
if ($this->isremovable($import,$this->cssremovables)) {
|
270
|
|
|
$thiscss = str_replace($import,'',$thiscss);
|
271
|
|
|
$import_ok = true;
|
272
|
|
|
} else {
|
273
|
|
|
$url = trim(preg_replace('#^.*((?:https?:|ftp:)?//.*\.css).*$#','$1',trim($import))," \t\n\r\0\x0B\"'");
|
274
|
|
|
$path = $this->getpath($url);
|
275
|
|
|
$import_ok = false;
|
276
|
|
|
if (file_exists($path) && is_readable($path)) {
|
277
|
|
|
$code = addcslashes($this->fixurls($path,file_get_contents($path)),"\\");
|
278
|
|
|
$code = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$code);
|
279
|
|
|
$tmpstyle = apply_filters( 'autoptimize_css_individual_style', $code, "" );
|
280
|
|
View Code Duplication |
if ( has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
|
|
|
|
|
281
|
|
|
$code=$tmpstyle;
|
282
|
|
|
$this->alreadyminified=true;
|
283
|
|
|
} else if ($this->can_inject_late($path,$code)) {
|
284
|
|
|
$code="/*!%%INJECTLATER%%".base64_encode($path)."|".md5($code)."%%INJECTLATER%%*/";
|
285
|
|
|
}
|
286
|
|
|
|
287
|
|
|
if(!empty($code)) {
|
288
|
|
|
$tmp_thiscss = preg_replace('#(/\*FILESTART\*/.*)'.preg_quote($import,'#').'#Us','/*FILESTART2*/'.$code.'$1',$thiscss);
|
289
|
|
|
if (!empty($tmp_thiscss)) {
|
290
|
|
|
$thiscss = $tmp_thiscss;
|
291
|
|
|
$import_ok = true;
|
292
|
|
|
unset($tmp_thiscss);
|
293
|
|
|
}
|
294
|
|
|
unset($code);
|
295
|
|
|
}
|
296
|
|
|
}
|
297
|
|
|
}
|
298
|
|
|
|
299
|
|
|
if (!$import_ok) {
|
300
|
|
|
// external imports and general fall-back
|
301
|
|
|
$external_imports .= $import;
|
302
|
|
|
$thiscss = str_replace($import,'',$thiscss);
|
303
|
|
|
$fiximports = true;
|
304
|
|
|
}
|
305
|
|
|
}
|
306
|
|
|
$thiscss = preg_replace('#/\*FILESTART\*/#','',$thiscss);
|
307
|
|
|
$thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
|
308
|
|
|
|
309
|
|
|
// and update $thiscss_nocomments before going into next iteration in while loop
|
310
|
|
|
$thiscss_nocomments=preg_replace('#/\*.*\*/#Us','',$thiscss);
|
311
|
|
|
}
|
312
|
|
|
unset($thiscss_nocomments);
|
313
|
|
|
|
314
|
|
|
// add external imports to top of aggregated CSS
|
315
|
|
|
if($fiximports) {
|
316
|
|
|
$thiscss=$external_imports.$thiscss;
|
317
|
|
|
}
|
318
|
|
|
}
|
319
|
|
|
unset($thiscss);
|
320
|
|
|
|
321
|
|
|
// $this->csscode has all the uncompressed code now.
|
322
|
|
|
foreach($this->csscode as &$code) {
|
323
|
|
|
// Check for already-minified code
|
324
|
|
|
$hash = md5($code);
|
325
|
|
|
$ccheck = new autoptimizeCache($hash,'css');
|
326
|
|
|
if($ccheck->check()) {
|
327
|
|
|
$code = $ccheck->retrieve();
|
328
|
|
|
$this->hashmap[md5($code)] = $hash;
|
329
|
|
|
continue;
|
330
|
|
|
}
|
331
|
|
|
unset($ccheck);
|
332
|
|
|
|
333
|
|
|
// Do the imaging!
|
334
|
|
|
$imgreplace = array();
|
335
|
|
|
preg_match_all( self::ASSETS_REGEX, $code, $matches );
|
336
|
|
|
|
337
|
|
|
if ( ($this->datauris == true) && (function_exists('base64_encode')) && (is_array($matches)) ) {
|
|
|
|
|
338
|
|
|
foreach($matches[1] as $count => $quotedurl) {
|
339
|
|
|
$iurl = trim($quotedurl," \t\n\r\0\x0B\"'");
|
340
|
|
|
|
341
|
|
|
// if querystring, remove it from url
|
342
|
|
|
if (strpos($iurl,'?') !== false) { $iurl = strtok($iurl,'?'); }
|
343
|
|
|
|
344
|
|
|
$ipath = $this->getpath($iurl);
|
345
|
|
|
|
346
|
|
|
$datauri_max_size = 4096;
|
347
|
|
|
$datauri_max_size = (int) apply_filters( 'autoptimize_filter_css_datauri_maxsize', $datauri_max_size );
|
348
|
|
|
$datauri_exclude = apply_filters( 'autoptimize_filter_css_datauri_exclude', "");
|
349
|
|
|
if (!empty($datauri_exclude)) {
|
350
|
|
|
$no_datauris=array_filter(array_map('trim',explode(",",$datauri_exclude)));
|
351
|
|
|
foreach ($no_datauris as $no_datauri) {
|
352
|
|
|
if (strpos($iurl,$no_datauri)!==false) {
|
353
|
|
|
$ipath=false;
|
354
|
|
|
break;
|
355
|
|
|
}
|
356
|
|
|
}
|
357
|
|
|
}
|
358
|
|
|
|
359
|
|
|
if($ipath != false && preg_match('#\.(jpe?g|png|gif|bmp)$#i',$ipath) && file_exists($ipath) && is_readable($ipath) && filesize($ipath) <= $datauri_max_size) {
|
|
|
|
|
360
|
|
|
$ihash=md5($ipath);
|
361
|
|
|
$icheck = new autoptimizeCache($ihash,'img');
|
362
|
|
|
if($icheck->check()) {
|
363
|
|
|
// we have the base64 image in cache
|
364
|
|
|
$headAndData=$icheck->retrieve();
|
365
|
|
|
$_base64data=explode(";base64,",$headAndData);
|
366
|
|
|
$base64data=$_base64data[1];
|
|
|
|
|
367
|
|
|
} else {
|
368
|
|
|
// It's an image and we don't have it in cache, get the type
|
369
|
|
|
$explA=explode('.',$ipath);
|
370
|
|
|
$type=end($explA);
|
371
|
|
|
|
372
|
|
|
switch($type) {
|
373
|
|
|
case 'jpeg':
|
374
|
|
|
$dataurihead = 'data:image/jpeg;base64,';
|
375
|
|
|
break;
|
376
|
|
|
case 'jpg':
|
377
|
|
|
$dataurihead = 'data:image/jpeg;base64,';
|
378
|
|
|
break;
|
379
|
|
|
case 'gif':
|
380
|
|
|
$dataurihead = 'data:image/gif;base64,';
|
381
|
|
|
break;
|
382
|
|
|
case 'png':
|
383
|
|
|
$dataurihead = 'data:image/png;base64,';
|
384
|
|
|
break;
|
385
|
|
|
case 'bmp':
|
386
|
|
|
$dataurihead = 'data:image/bmp;base64,';
|
387
|
|
|
break;
|
388
|
|
|
default:
|
389
|
|
|
$dataurihead = 'data:application/octet-stream;base64,';
|
390
|
|
|
}
|
391
|
|
|
|
392
|
|
|
// Encode the data
|
393
|
|
|
$base64data = base64_encode(file_get_contents($ipath));
|
394
|
|
|
$headAndData=$dataurihead.$base64data;
|
395
|
|
|
|
396
|
|
|
// Save in cache
|
397
|
|
|
$icheck->cache($headAndData,"text/plain");
|
398
|
|
|
}
|
399
|
|
|
unset($icheck);
|
400
|
|
|
|
401
|
|
|
// Add it to the list for replacement
|
402
|
|
|
$imgreplace[$matches[0][$count]] = str_replace($quotedurl,$headAndData,$matches[0][$count]);
|
403
|
|
|
} else {
|
404
|
|
|
// just cdn the URL if applicable
|
405
|
|
|
if (!empty($this->cdn_url)) {
|
406
|
|
|
$imgreplace[$matches[0][$count]] = str_replace($quotedurl,$this->maybe_cdn_urls($quotedurl),$matches[0][$count]);
|
407
|
|
|
}
|
408
|
|
|
}
|
409
|
|
|
}
|
410
|
|
|
} else if ((is_array($matches)) && (!empty($this->cdn_url))) {
|
411
|
|
|
// change urls to cdn-url
|
412
|
|
|
foreach($matches[1] as $count => $quotedurl) {
|
413
|
|
|
$imgreplace[$matches[0][$count]] = str_replace($quotedurl,$this->maybe_cdn_urls($quotedurl),$matches[0][$count]);
|
414
|
|
|
}
|
415
|
|
|
}
|
416
|
|
|
|
417
|
|
|
if(!empty($imgreplace)) {
|
418
|
|
|
$code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code);
|
419
|
|
|
}
|
420
|
|
|
|
421
|
|
|
// Minify
|
422
|
|
|
if (($this->alreadyminified!==true) && (apply_filters( "autoptimize_css_do_minify", true))) {
|
423
|
|
View Code Duplication |
if (class_exists('Minify_CSS_Compressor')) {
|
|
|
|
|
424
|
|
|
$tmp_code = trim(Minify_CSS_Compressor::process($code));
|
425
|
|
|
} else if(class_exists('CSSmin')) {
|
426
|
|
|
$cssmin = new CSSmin();
|
427
|
|
|
if (method_exists($cssmin,"run")) {
|
428
|
|
|
$tmp_code = trim($cssmin->run($code));
|
429
|
|
|
} elseif (@is_callable(array($cssmin,"minify"))) {
|
430
|
|
|
$tmp_code = trim(CssMin::minify($code));
|
|
|
|
|
431
|
|
|
}
|
432
|
|
|
}
|
433
|
|
|
if (!empty($tmp_code)) {
|
434
|
|
|
$code = $tmp_code;
|
435
|
|
|
unset($tmp_code);
|
436
|
|
|
}
|
437
|
|
|
}
|
438
|
|
|
|
439
|
|
|
$code = $this->inject_minified($code);
|
440
|
|
|
|
441
|
|
|
$tmp_code = apply_filters( 'autoptimize_css_after_minify', $code );
|
442
|
|
|
if (!empty($tmp_code)) {
|
443
|
|
|
$code = $tmp_code;
|
444
|
|
|
unset($tmp_code);
|
445
|
|
|
}
|
446
|
|
|
|
447
|
|
|
$this->hashmap[md5($code)] = $hash;
|
448
|
|
|
}
|
449
|
|
|
unset($code);
|
450
|
|
|
return true;
|
451
|
|
|
}
|
452
|
|
|
|
453
|
|
|
//Caches the CSS in uncompressed, deflated and gzipped form.
|
454
|
|
|
public function cache() {
|
455
|
|
|
// CSS cache
|
456
|
|
|
foreach($this->csscode as $media => $code) {
|
457
|
|
|
$md5 = $this->hashmap[md5($code)];
|
458
|
|
|
|
459
|
|
|
$cache = new autoptimizeCache($md5,'css');
|
460
|
|
|
if(!$cache->check()) {
|
461
|
|
|
// Cache our code
|
462
|
|
|
$cache->cache($code,'text/css');
|
463
|
|
|
}
|
464
|
|
|
$this->url[$media] = AUTOPTIMIZE_CACHE_URL.$cache->getname();
|
465
|
|
|
}
|
466
|
|
|
}
|
467
|
|
|
|
468
|
|
|
//Returns the content
|
469
|
|
|
public function getcontent() {
|
470
|
|
|
// restore IE hacks
|
471
|
|
|
$this->content = $this->restore_iehacks($this->content);
|
472
|
|
|
|
473
|
|
|
// restore comments
|
474
|
|
|
$this->content = $this->restore_comments($this->content);
|
475
|
|
|
|
476
|
|
|
// restore (no)script
|
477
|
|
View Code Duplication |
if ( strpos( $this->content, '%%SCRIPT%%' ) !== false ) {
|
|
|
|
|
478
|
|
|
$this->content = preg_replace_callback(
|
479
|
|
|
'#%%SCRIPT%%(.*?)%%SCRIPT%%#is',
|
480
|
|
|
create_function(
|
|
|
|
|
481
|
|
|
'$matches',
|
482
|
|
|
'return base64_decode($matches[1]);'
|
483
|
|
|
),
|
484
|
|
|
$this->content
|
485
|
|
|
);
|
486
|
|
|
}
|
487
|
|
|
|
488
|
|
|
// restore noptimize
|
489
|
|
|
$this->content = $this->restore_noptimize($this->content);
|
490
|
|
|
|
491
|
|
|
//Restore the full content
|
492
|
|
|
if(!empty($this->restofcontent)) {
|
493
|
|
|
$this->content .= $this->restofcontent;
|
494
|
|
|
$this->restofcontent = '';
|
495
|
|
|
}
|
496
|
|
|
|
497
|
|
|
// Inject the new stylesheets
|
498
|
|
|
$replaceTag = array("<title","before");
|
499
|
|
|
$replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content );
|
500
|
|
|
|
501
|
|
|
if ($this->inline == true) {
|
|
|
|
|
502
|
|
|
foreach($this->csscode as $media => $code) {
|
503
|
|
|
$this->inject_in_html('<style type="text/css" media="'.$media.'">'.$code.'</style>',$replaceTag);
|
504
|
|
|
}
|
505
|
|
|
} else {
|
506
|
|
|
if ($this->defer == true) {
|
|
|
|
|
507
|
|
|
$preloadCssBlock = "";
|
508
|
|
|
$noScriptCssBlock = "<noscript id=\"aonoscrcss\">";
|
509
|
|
|
$defer_inline_code=$this->defer_inline;
|
510
|
|
|
if(!empty($defer_inline_code)){
|
511
|
|
|
if ( apply_filters( 'autoptimize_filter_css_critcss_minify', true ) ) {
|
512
|
|
|
$iCssHash = md5($defer_inline_code);
|
513
|
|
|
$iCssCache = new autoptimizeCache($iCssHash,'css');
|
514
|
|
|
if($iCssCache->check()) {
|
515
|
|
|
// we have the optimized inline CSS in cache
|
516
|
|
|
$defer_inline_code=$iCssCache->retrieve();
|
517
|
|
|
} else {
|
518
|
|
|
if (class_exists('Minify_CSS_Compressor')) {
|
519
|
|
|
$tmp_code = trim(Minify_CSS_Compressor::process($defer_inline_code));
|
|
|
|
|
520
|
|
|
} else if(class_exists('CSSmin')) {
|
521
|
|
|
$cssmin = new CSSmin();
|
522
|
|
|
$tmp_code = trim($cssmin->run($defer_inline_code));
|
|
|
|
|
523
|
|
|
}
|
524
|
|
|
if (!empty($tmp_code)) {
|
525
|
|
|
$defer_inline_code = $tmp_code;
|
526
|
|
|
$iCssCache->cache($defer_inline_code,"text/css");
|
527
|
|
|
unset($tmp_code);
|
528
|
|
|
}
|
529
|
|
|
}
|
530
|
|
|
}
|
531
|
|
|
$code_out='<style type="text/css" id="aoatfcss" media="all">'.$defer_inline_code.'</style>';
|
532
|
|
|
$this->inject_in_html($code_out,$replaceTag);
|
533
|
|
|
}
|
534
|
|
|
}
|
535
|
|
|
|
536
|
|
|
foreach($this->url as $media => $url) {
|
537
|
|
|
$url = $this->url_replace_cdn($url);
|
538
|
|
|
|
539
|
|
|
//Add the stylesheet either deferred (import at bottom) or normal links in head
|
540
|
|
|
if($this->defer == true) {
|
|
|
|
|
541
|
|
|
$preloadCssBlock .= '<link rel="preload" as="style" media="'.$media.'" href="'.$url.'" onload="this.rel=\'stylesheet\'" />';
|
|
|
|
|
542
|
|
|
$noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />';
|
|
|
|
|
543
|
|
|
} else {
|
544
|
|
|
if (strlen($this->csscode[$media]) > $this->cssinlinesize) {
|
545
|
|
|
$this->inject_in_html('<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />',$replaceTag);
|
546
|
|
|
} else if (strlen($this->csscode[$media])>0) {
|
547
|
|
|
$this->inject_in_html('<style type="text/css" media="'.$media.'">'.$this->csscode[$media].'</style>',$replaceTag);
|
548
|
|
|
}
|
549
|
|
|
}
|
550
|
|
|
}
|
551
|
|
|
|
552
|
|
|
if($this->defer == true) {
|
|
|
|
|
553
|
|
|
$preloadPolyfill = '<script data-cfasync=\'false\'>/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
|
554
|
|
|
!function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);
|
555
|
|
|
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
|
556
|
|
|
!function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}}(this);</script>';
|
557
|
|
|
$noScriptCssBlock .= "</noscript>";
|
558
|
|
|
$this->inject_in_html($preloadCssBlock.$noScriptCssBlock,$replaceTag);
|
559
|
|
|
$this->inject_in_html($preloadPolyfill,array('</body>','before'));
|
560
|
|
|
}
|
561
|
|
|
}
|
562
|
|
|
|
563
|
|
|
//Return the modified stylesheet
|
564
|
|
|
return $this->content;
|
565
|
|
|
}
|
566
|
|
|
|
567
|
|
|
static function fixurls($file, $code) {
|
|
|
|
|
568
|
|
|
// Switch all imports to the url() syntax
|
569
|
|
|
$code = preg_replace( '#@import ("|\')(.+?)\.css.*("|\')#', '@import url("${2}.css")', $code );
|
570
|
|
|
|
571
|
|
|
if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) {
|
572
|
|
|
$file = str_replace( WP_ROOT_DIR, '/', $file );
|
573
|
|
|
$dir = dirname( $file ); // Like /themes/expound/css
|
574
|
|
|
|
575
|
|
|
// $dir should not contain backslashes, since it's used to replace
|
576
|
|
|
// urls, but it can contain them when running on Windows because
|
577
|
|
|
// fixurls() is sometimes called with `ABSPATH . 'index.php'`
|
578
|
|
|
$dir = str_replace( '\\', '/', $dir );
|
579
|
|
|
unset( $file ); // not used below at all
|
580
|
|
|
|
581
|
|
|
$replace = array();
|
582
|
|
|
foreach ( $matches[1] as $k => $url ) {
|
583
|
|
|
// Remove quotes
|
584
|
|
|
$url = trim( $url," \t\n\r\0\x0B\"'" );
|
585
|
|
|
$noQurl = trim( $url, "\"'" );
|
586
|
|
|
if ( $url !== $noQurl ) {
|
587
|
|
|
$removedQuotes = true;
|
588
|
|
|
} else {
|
589
|
|
|
$removedQuotes = false;
|
590
|
|
|
}
|
591
|
|
|
|
592
|
|
|
if ( '' === $noQurl ) {
|
593
|
|
|
continue;
|
594
|
|
|
}
|
595
|
|
|
|
596
|
|
|
$url = $noQurl;
|
597
|
|
|
if ( '/' === $url{0} || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
|
598
|
|
|
// URL is protocol-relative, host-relative or something we don't touch
|
599
|
|
|
continue;
|
600
|
|
|
} else {
|
601
|
|
|
// Relative URL
|
602
|
|
|
$newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) );
|
603
|
|
|
|
604
|
|
|
// Hash the url + whatever was behind potentially for replacement
|
605
|
|
|
// We must do this, or different css classes referencing the same bg image (but
|
606
|
|
|
// different parts of it, say, in sprites and such) loose their stuff...
|
607
|
|
|
$hash = md5( $url . $matches[2][$k] );
|
608
|
|
|
$code = str_replace( $matches[0][$k], $hash, $code );
|
609
|
|
|
|
610
|
|
|
if ( $removedQuotes ) {
|
611
|
|
|
$replace[$hash] = "url('" . $newurl . "')" . $matches[2][$k];
|
612
|
|
|
} else {
|
613
|
|
|
$replace[$hash] = 'url(' . $newurl . ')' . $matches[2][$k];
|
614
|
|
|
}
|
615
|
|
|
}
|
616
|
|
|
}
|
617
|
|
|
|
618
|
|
|
if ( ! empty( $replace ) ) {
|
619
|
|
|
// Sort the replacements array by key length in desc order (so that the longest strings are replaced first)
|
620
|
|
|
$keys = array_map( 'strlen', array_keys( $replace ) );
|
621
|
|
|
array_multisort( $keys, SORT_DESC, $replace );
|
622
|
|
|
|
623
|
|
|
// Replace URLs found within $code
|
624
|
|
|
$code = str_replace( array_keys( $replace ), array_values( $replace ), $code );
|
625
|
|
|
}
|
626
|
|
|
}
|
627
|
|
|
|
628
|
|
|
return $code;
|
629
|
|
|
}
|
630
|
|
|
|
631
|
|
|
private function ismovable($tag) {
|
632
|
|
|
if ( apply_filters('autoptimize_filter_css_dontaggregate', false) ) {
|
633
|
|
|
return false;
|
634
|
|
|
} else if (!empty($this->whitelist)) {
|
635
|
|
|
foreach ($this->whitelist as $match) {
|
|
|
|
|
636
|
|
|
if(strpos($tag,$match)!==false) {
|
637
|
|
|
return true;
|
638
|
|
|
}
|
639
|
|
|
}
|
640
|
|
|
// no match with whitelist
|
641
|
|
|
return false;
|
642
|
|
|
} else {
|
643
|
|
|
if (is_array($this->dontmove)) {
|
644
|
|
|
foreach($this->dontmove as $match) {
|
645
|
|
|
if(strpos($tag,$match)!==false) {
|
646
|
|
|
//Matched something
|
647
|
|
|
return false;
|
648
|
|
|
}
|
649
|
|
|
}
|
650
|
|
|
}
|
651
|
|
|
|
652
|
|
|
//If we're here it's safe to move
|
653
|
|
|
return true;
|
654
|
|
|
}
|
655
|
|
|
}
|
656
|
|
|
|
657
|
|
|
private function can_inject_late($cssPath,$css) {
|
658
|
|
|
$consider_minified_array = apply_filters('autoptimize_filter_css_consider_minified', false, $cssPath);
|
659
|
|
|
if ( $this->inject_min_late !== true ) {
|
660
|
|
|
// late-inject turned off
|
661
|
|
|
return false;
|
662
|
|
|
} else if ( (strpos($cssPath,"min.css") === false) && ( str_replace($consider_minified_array, '', $cssPath) === $cssPath ) ) {
|
663
|
|
|
// file not minified based on filename & filter
|
664
|
|
|
return false;
|
665
|
|
|
} else if ( strpos($css,"@import") !== false ) {
|
666
|
|
|
// can't late-inject files with imports as those need to be aggregated
|
667
|
|
|
return false;
|
668
|
|
|
} else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("autoptimize_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) {
|
669
|
|
|
// don't late-inject CSS with font-src's if fonts are set to be CDN'ed
|
670
|
|
|
return false;
|
671
|
|
|
} else if ( (($this->datauris == true) || (!empty($this->cdn_url))) && preg_match("#background[^;}]*url\(#Ui",$css) ) {
|
|
|
|
|
672
|
|
|
// don't late-inject CSS with images if CDN is set OR is image inlining is on
|
673
|
|
|
return false;
|
674
|
|
|
} else {
|
675
|
|
|
// phew, all is safe, we can late-inject
|
676
|
|
|
return true;
|
677
|
|
|
}
|
678
|
|
|
}
|
679
|
|
|
|
680
|
|
|
private function maybe_cdn_urls($inUrl) {
|
681
|
|
|
$url = trim($inUrl," \t\n\r\0\x0B\"'");
|
682
|
|
|
// exclude fonts from CDN except if filter returns true
|
683
|
|
|
if ( !preg_match('#\.(woff2?|eot|ttf|otf)$#i',$url) || apply_filters('autoptimize_filter_css_fonts_cdn',false) ) {
|
684
|
|
|
$cdn_url = $this->url_replace_cdn($url);
|
685
|
|
|
} else {
|
686
|
|
|
$cdn_url = $url;
|
687
|
|
|
}
|
688
|
|
|
return $cdn_url;
|
689
|
|
|
}
|
690
|
|
|
}
|
691
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..