PluginProbe
Autoptimize / 1.6.6
Autoptimize v1.6.6
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / autoptimizeStyles.php

autoptimizeStyles.php in Autoptimize 1.6.6, at classes/autoptimizeStyles.php

364 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3 class autoptimizeStyles extends autoptimizeBase
4 {
5 private $css = array();
6 private $csscode = array();
7 private $url = array();
8 private $restofcontent = '';
9 private $mhtml = '';
10 private $datauris = false;
11 private $yui = false;
12 private $hashmap = array();
13
14 //Reads the page and collects style tags
15 public function read($options)
16 {
17 //Remove everything that's not the header
18 if($options['justhead'] == true)
19 {
20 $content = explode('</head>',$this->content,2);
21 $this->content = $content[0].'</head>';
22 $this->restofcontent = $content[1];
23 }
24
25 //Store data: URIs setting for later use
26 $this->datauris = $options['datauris'];
27
28 //Do we use yui?
29 $this->yui = $options['yui'];
30 // noptimize me
31 $this->content = $this->hide_noptimize($this->content);
32 //Save IE hacks
33 $this->content = preg_replace('#(<\!--\[if.*\]>.*<\!\[endif\]-->)#Usie','\'%%IEHACK%%\'.base64_encode("$1").\'%%IEHACK%%\'',$this->content);
34
35 //Get <style> and <link>
36 if(preg_match_all('#(<style[^>]*>.*</style>)|(<link[^>]*text/css[^>]*>)#Usmi',$this->content,$matches))
37 {
38 foreach($matches[0] as $tag)
39 {
40 if (strpos($tag,"admin-bar.min.css")===false) {
41 //Get the media
42 if(strpos($tag,'media=')!==false)
43 {
44 preg_match('#media=(?:"|\')([^>]*)(?:"|\')#Ui',$tag,$medias);
45 $medias = explode(',',$medias[1]);
46 $media = array();
47 foreach($medias as $elem)
48 {
49 // $media[] = current(explode(' ',trim($elem),2));
50 $media[] = $elem;
51 }
52 }else{
53 //No media specified - applies to all
54 $media = array('all');
55 }
56
57 if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source))
58 {
59 //<link>
60 $url = current(explode('?',$source[2],2));
61 $path = $this->getpath($url);
62
63 if($path !==false && preg_match('#\.css$#',$path))
64 {
65 //Good link
66 $this->css[] = array($media,$path);
67 }else{
68 //Link is dynamic (.php etc)
69 $tag = '';
70 }
71 }else{
72 //<style>
73 preg_match('#<style.*>(.*)</style>#Usmi',$tag,$code);
74 $code = preg_replace('#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm','$1',$code[1]);
75 $this->css[] = array($media,'INLINE;'.$code);
76 }
77
78 //Remove the original style tag
79 $this->content = str_replace($tag,'',$this->content);
80 }
81 }
82
83 return true;
84 }
85
86 //No styles :(
87 return false;
88 }
89
90 //Joins and optimizes CSS
91 public function minify()
92 {
93 foreach($this->css as $group)
94 {
95 list($media,$css) = $group;
96 if(preg_match('#^INLINE;#',$css))
97 {
98 //<style>
99 $css = preg_replace('#^INLINE;#','',$css);
100 $css = $this->fixurls(ABSPATH.'/index.php',$css);
101 }else{
102 //<link>
103 if($css !== false && file_exists($css) && is_readable($css))
104 {
105 $css = $this->fixurls($css,file_get_contents($css));
106 }else{
107 //Couldn't read CSS. Maybe getpath isn't working?
108 $css = '';
109 }
110 }
111
112 foreach($media as $elem)
113 {
114 if(!isset($this->csscode[$elem]))
115 $this->csscode[$elem] = '';
116 $this->csscode[$elem] .= "\n/*FILESTART*/".$css;
117 }
118 }
119
120 //Check for duplicate code
121 $md5list = array();
122 $tmpcss = $this->csscode;
123 foreach($tmpcss as $media => $code)
124 {
125 $md5sum = md5($code);
126 $medianame = $media;
127 foreach($md5list as $med => $sum)
128 {
129 //If same code
130 if($sum === $md5sum)
131 {
132 //Add the merged code
133 $medianame = $med.', '.$media;
134 $this->csscode[$medianame] = $code;
135 $md5list[$medianame] = $md5list[$med];
136 unset($this->csscode[$med], $this->csscode[$media]);
137 unset($md5list[$med]);
138 }
139 }
140 $md5list[$medianame] = $md5sum;
141 }
142 unset($tmpcss);
143
144 //Manage @imports, while is for recursive import management
145 foreach($this->csscode as &$thiscss)
146 {
147 //Flag to trigger import reconstitution
148 $fiximports = false;
149 while(preg_match_all('#@import.*(?:;|$)#Um',$thiscss,$matches))
150 {
151 foreach($matches[0] as $import)
152 {
153 $url = trim(preg_replace('#^.*((?:https?|ftp)://.*\.css).*$#','$1',$import)," \t\n\r\0\x0B\"'");
154 $path = $this->getpath($url);
155 if(file_exists($path) && is_readable($path))
156 {
157 $code = addcslashes($this->fixurls($path,file_get_contents($path)),"\\");
158 $thiscss = preg_replace('#(/\*FILESTART\*/.*)'.preg_quote($import,'#').'#Us','/*FILESTART2*/'.$code.'$1',$thiscss);
159 }else{
160 //getpath is not working?
161 //Encode so preg_match doesn't see it
162 $thiscss = str_replace($import,'/*IMPORT*/'.base64_encode($import).'/*IMPORT*/',$thiscss);
163 $fiximports = true;
164 }
165 }
166 $thiscss = preg_replace('#/\*FILESTART\*/#','',$thiscss);
167 $thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
168 }
169
170 //Recover imports
171 if($fiximports)
172 {
173 $thiscss = preg_replace('#/\*IMPORT\*/(.*)/\*IMPORT\*/#Use','base64_decode("$1")',$thiscss);
174 }
175 }
176 unset($thiscss);
177
178 //$this->csscode has all the uncompressed code now.
179 $mhtmlcount = 0;
180 foreach($this->csscode as &$code)
181 {
182 //Check for already-minified code
183 $hash = md5($code);
184 $ccheck = new autoptimizeCache($hash,'css');
185 if($ccheck->check())
186 {
187 $code = $ccheck->retrieve();
188 $this->hashmap[md5($code)] = $hash;
189 continue;
190 }
191 unset($ccheck);
192
193 $imgreplace = array();
194 //Do the imaging!
195 if($this->datauris == true && function_exists('base64_encode') && preg_match_all('#(background[^;}]*url\((?!data)(.*)\)[^;}]*)(?:;|$|})#Usm',$code,$matches))
196 {
197 foreach($matches[2] as $count => $quotedurl)
198 {
199 $url = trim($quotedurl," \t\n\r\0\x0B\"'");
200 // fgo: if querystring, remove it from url
201 if (strpos($url,'?') !== false) { $url = reset(explode('?',$url)); }
202 $path = $this->getpath($url);
203
204 // fgo: jpe?j should be jpe?g I guess + 5KB seems like a lot, lower to 2.5KB
205 if($path != false && preg_match('#\.(jpe?g|png|gif|bmp)$#',$path) && file_exists($path) && is_readable($path) && filesize($path) <= 2560)
206 {
207 //It's an image
208 //Get type
209 $type=end(explode('.',$path));
210 switch($type)
211 {
212 // fgo: jpeg and jpg
213 case 'jpeg':
214 $dataurihead = 'data:image/jpeg;base64,';
215 break;
216 case 'jpg':
217 $dataurihead = 'data:image/jpeg;base64,';
218 break;
219 case 'gif':
220 $dataurihead = 'data:image/gif;base64,';
221 break;
222 case 'png':
223 $dataurihead = 'data:image/png;base64,';
224 break;
225 case 'bmp':
226 $dataurihead = 'data:image/bmp;base64,';
227 break;
228 default:
229 $dataurihead = 'data:application/octet-stream;base64,';
230 }
231
232 //Encode the data
233 $base64data = base64_encode(file_get_contents($path));
234
235 //Add it to the list for replacement
236 $imgreplace[$matches[1][$count]] = str_replace($quotedurl,$dataurihead.$base64data,$matches[1][$count]).";\n*".str_replace($quotedurl,'mhtml:%%MHTML%%!'.$mhtmlcount,$matches[1][$count]).";\n_".$matches[1][$count].';';
237
238 //Store image on the mhtml document
239 $this->mhtml .= "--_\r\nContent-Location:{$mhtmlcount}\r\nContent-Transfer-Encoding:base64\r\n\r\n{$base64data}\r\n";
240 $mhtmlcount++;
241 }
242 }
243 //Replace the images
244 $code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code);
245 }
246
247 //Minify
248 if($this->yui == false && class_exists('Minify_CSS_Compressor'))
249 {
250 $code = trim(Minify_CSS_Compressor::process($code));
251 }elseif($this->yui == true && autoptimizeYUI::available()){
252 $code = autoptimizeYUI::compress('css',$code);
253 }
254
255 $this->hashmap[md5($code)] = $hash;
256 }
257 unset($code);
258 return true;
259 }
260
261 //Caches the CSS in uncompressed, deflated and gzipped form.
262 public function cache()
263 {
264 if($this->datauris)
265 {
266 //MHTML Preparation
267 $this->mhtml = "/*\r\nContent-Type: multipart/related; boundary=\"_\"\r\n\r\n".$this->mhtml."*/\r\n";
268 $md5 = md5($this->mhtml);
269 $cache = new autoptimizeCache($md5,'txt');
270 if(!$cache->check())
271 {
272 //Cache our images for IE
273 $cache->cache($this->mhtml,'text/plain');
274 }
275 $mhtml = AUTOPTIMIZE_CACHE_URL.$cache->getname();
276 }
277
278 //CSS cache
279 foreach($this->csscode as $media => $code)
280 {
281 // fgo, moved from below to prevent empty md5 resulting in filenames without hash autoptimize_.php
282 $md5 = $this->hashmap[md5($code)];
283
284 if($this->datauris)
285 {
286 //Images for ie! Get the right url
287 $code = str_replace('%%MHTML%%',$mhtml,$code);
288 }
289
290 // $md5 = $this->hashmap[md5($code)];
291 $cache = new autoptimizeCache($md5,'css');
292 if(!$cache->check())
293 {
294 //Cache our code
295 $cache->cache($code,'text/css');
296 }
297 $this->url[$media] = AUTOPTIMIZE_CACHE_URL.$cache->getname();
298 }
299 }
300
301 //Returns the content
302 public function getcontent()
303 {
304 //Restore IE hacks
305 // fgo: added stripslashes as e modifier escapes stuff
306 $this->content = preg_replace('#%%IEHACK%%(.*)%%IEHACK%%#Usie','stripslashes(base64_decode("$1"))',$this->content);
307 // restore noptimize
308 $this->content = $this->restore_noptimize($this->content);
309 //Restore the full content
310 if(!empty($this->restofcontent))
311 {
312 $this->content .= $this->restofcontent;
313 $this->restofcontent = '';
314 }
315
316 //Add the new stylesheets
317 foreach($this->url as $media => $url)
318 {
319 // fgo: these were added before </head> but that overrides iehack-stylesheets, so adding before <title>
320 $this->content = str_replace('<title>','<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" /><title>',$this->content);
321 }
322
323 //Return the modified stylesheet
324 return $this->content;
325 }
326
327 private function fixurls($file,$code)
328 {
329 // $file = str_replace(ABSPATH,'/',$file); //Sth like /wp-content/file.css
330 $file = str_replace(WP_ROOT_DIR,'/',$file);
331 $dir = dirname($file); //Like /wp-content
332
333 // quick fix for import-troubles in e.g. arras theme
334 $code=preg_replace('#@import ("|\')(.+?)\.css("|\')#','@import url("${2}.css")',$code);
335
336 if(preg_match_all('#url?\((?!data)(.*)\)#Usi',$code,$matches))
337 {
338 $replace = array();
339 foreach($matches[1] as $k => $url)
340 {
341 //Remove quotes
342 $url = trim($url," \t\n\r\0\x0B\"'");
343 if(substr($url,0,1)=='/' || preg_match('#^(https?|ftp)://#i',$url))
344 {
345 //URL is absolute
346 continue;
347 }else{
348 //relative URL. Let's fix it!
349 // $newurl = get_settings('home').str_replace('//','/',$dir.'/'.$url); //http://yourblog.com/wp-content/../image.png
350 $newurl = WP_ROOT_URL.str_replace('//','/',$dir.'/'.$url);
351 $hash = md5($url);
352 $code = str_replace($matches[0][$k],$hash,$code);
353 $replace[$hash] = 'url('.$newurl.')';
354 }
355 }
356
357 //Do the replacing here to avoid breaking URLs
358 $code = str_replace(array_keys($replace),array_values($replace),$code);
359 }
360
361 return $code;
362 }
363 }
364