PluginProbe
Autoptimize / 1.6.2
Autoptimize v1.6.2
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.2, at classes/autoptimizeStyles.php

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