PluginProbe
Autoptimize / 1.6.5
Autoptimize v1.6.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.6.5, at classes/autoptimizeStyles.php

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