PluginProbe
Autoptimize / 1.5
Autoptimize v1.5
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.5, at classes/autoptimizeStyles.php

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