4 * EditArea PHP compressor
\r
5 * Developped by Christophe Dolivet
\r
6 * Released under LGPL, Apache and BSD licenses
\r
7 * v1.1.3 (2007/01/18)
\r
12 $param['cache_duration']= 3600 * 24 * 10; // 10 days util client cache expires
\r
13 $param['compress'] = true; // enable the code compression, should be activated but it can be usefull to desactivate it for easier error retrieving (true or false)
\r
14 $param['debug'] = false; // Enable this option if you need debuging info
\r
15 $param['use_disk_cache']= true; // If you enable this option gzip files will be cached on disk.
\r
16 $param['use_gzip']= true; // Enable gzip compression
\r
19 $compressor= new Compressor($param);
\r
24 function compressor($param)
\r
26 $this->__construct($param);
\r
29 function __construct($param)
\r
31 $this->start_time= $this->get_microtime();
\r
32 $this->file_loaded_size=0;
\r
33 $this->param= $param;
\r
34 $this->script_list="";
\r
35 $this->path= dirname(__FILE__)."/";
\r
36 if(isset($_GET['plugins'])){
\r
37 $this->load_all_plugins= true;
\r
38 $this->full_cache_file= $this->path."edit_area_full_with_plugins.js";
\r
39 $this->gzip_cache_file= $this->path."edit_area_full_with_plugins.gz";
\r
41 $this->load_all_plugins= false;
\r
42 $this->full_cache_file= $this->path."edit_area_full.js";
\r
43 $this->gzip_cache_file= $this->path."edit_area_full.gz";
\r
46 $this->check_gzip_use();
\r
47 $this->send_headers();
\r
48 $this->check_cache();
\r
49 $this->load_files();
\r
50 $this->send_datas();
\r
53 function send_headers()
\r
55 header("Content-type: text/javascript; charset: UTF-8");
\r
56 header("Vary: Accept-Encoding"); // Handle proxies
\r
57 header(sprintf("Expires: %s GMT", gmdate("D, d M Y H:i:s", time() + $this->param['cache_duration'])) );
\r
59 header("Content-Encoding: ".$this->gzip_enc_header);
\r
62 function check_gzip_use()
\r
64 $encodings = array();
\r
65 $desactivate_gzip=false;
\r
67 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
\r
68 $encodings = explode(',', strtolower(preg_replace("/\s+/", "", $_SERVER['HTTP_ACCEPT_ENCODING'])));
\r
70 // desactivate gzip for IE version < 7
\r
71 if(preg_match("/(?:msie )([0-9.]+)/i", $_SERVER['HTTP_USER_AGENT'], $ie))
\r
74 $desactivate_gzip=true;
\r
77 // Check for gzip header or northon internet securities
\r
78 if (!$desactivate_gzip && $this->param['use_gzip'] && (in_array('gzip', $encodings) || in_array('x-gzip', $encodings) || isset($_SERVER['---------------'])) && function_exists('ob_gzhandler') && !ini_get('zlib.output_compression')) {
\r
79 $this->gzip_enc_header= in_array('x-gzip', $encodings) ? "x-gzip" : "gzip";
\r
80 $this->use_gzip=true;
\r
81 $this->cache_file=$this->gzip_cache_file;
\r
83 $this->use_gzip=false;
\r
84 $this->cache_file=$this->full_cache_file;
\r
88 function check_cache()
\r
90 // Only gzip the contents if clients and server support it
\r
91 if (file_exists($this->cache_file)) {
\r
92 // check if cache file must be updated
\r
94 if ($dir = opendir($this->path)) {
\r
95 while (($file = readdir($dir)) !== false) {
\r
96 if(is_file($this->path.$file) && $file!="." && $file!="..")
\r
97 $cache_date= max($cache_date, filemtime($this->path.$file));
\r
101 if($this->load_all_plugins){
\r
102 $plug_path= $this->path."plugins/";
\r
103 if (($dir = @opendir($plug_path)) !== false)
\r
105 while (($file = readdir($dir)) !== false)
\r
107 if ($file !== "." && $file !== "..")
\r
109 if(is_dir($plug_path.$file) && file_exists($plug_path.$file."/".$file.".js"))
\r
110 $cache_date= max($cache_date, filemtime("plugins/".$file."/".$file.".js"));
\r
117 if(filemtime($this->cache_file) >= $cache_date){
\r
118 // if cache file is up to date
\r
119 $last_modified = gmdate("D, d M Y H:i:s",filemtime($this->cache_file))." GMT";
\r
120 if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && strcasecmp($_SERVER["HTTP_IF_MODIFIED_SINCE"], $last_modified) === 0)
\r
122 header("HTTP/1.1 304 Not Modified");
\r
123 header("Last-modified: ".$last_modified);
\r
124 header("Cache-Control: Public"); // Tells HTTP 1.1 clients to cache
\r
125 header("Pragma:"); // Tells HTTP 1.0 clients to cache
\r
129 header("Last-modified: ".$last_modified);
\r
130 header("Cache-Control: Public"); // Tells HTTP 1.1 clients to cache
\r
131 header("Pragma:"); // Tells HTTP 1.0 clients to cache
\r
132 header('Content-Length: '.filesize($this->cache_file));
\r
133 echo file_get_contents($this->cache_file);
\r
141 function load_files()
\r
143 $loader= $this->get_content("edit_area_loader.js")."\n";
\r
145 // get the list of other files to load
\r
146 $loader= preg_replace("/(t\.scripts_to_load=\s*)\[([^\]]*)\];/e"
\r
147 , "\$this->replace_scripts('script_list', '\\1', '\\2')"
\r
150 $loader= preg_replace("/(t\.sub_scripts_to_load=\s*)\[([^\]]*)\];/e"
\r
151 , "\$this->replace_scripts('sub_script_list', '\\1', '\\2')"
\r
154 $this->datas= $loader;
\r
155 $this->compress_javascript($this->datas);
\r
157 // load other scripts needed for the loader
\r
158 preg_match_all('/"([^"]*)"/', $this->script_list, $match);
\r
159 foreach($match[1] as $key => $value)
\r
161 $content= $this->get_content(preg_replace("/\\|\//i", "", $value).".js");
\r
162 $this->compress_javascript($content);
\r
163 $this->datas.= $content."\n";
\r
166 //$this->datas= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', "", $this->datas);
\r
168 // improved compression step 1/2
\r
169 $this->datas= preg_replace(array("/(\b)EditAreaLoader(\b)/", "/(\b)editAreaLoader(\b)/", "/(\b)editAreas(\b)/"), array("EAL", "eAL", "eAs"), $this->datas);
\r
170 //$this->datas= str_replace(array("EditAreaLoader", "editAreaLoader", "editAreas"), array("EAL", "eAL", "eAs"), $this->datas);
\r
171 $this->datas.= "var editAreaLoader= eAL;var editAreas=eAs;EditAreaLoader=EAL;";
\r
173 // load sub scripts
\r
175 $sub_scripts_list= array();
\r
176 preg_match_all('/"([^"]*)"/', $this->sub_script_list, $match);
\r
177 foreach($match[1] as $value){
\r
178 $sub_scripts_list[]= preg_replace("/\\|\//i", "", $value).".js";
\r
181 if($this->load_all_plugins){
\r
182 // load plugins scripts
\r
183 $plug_path= $this->path."plugins/";
\r
184 if (($dir = @opendir($plug_path)) !== false)
\r
186 while (($file = readdir($dir)) !== false)
\r
188 if ($file !== "." && $file !== "..")
\r
190 if(is_dir($plug_path.$file) && file_exists($plug_path.$file."/".$file.".js"))
\r
191 $sub_scripts_list[]= "plugins/".$file."/".$file.".js";
\r
198 foreach($sub_scripts_list as $value){
\r
199 $sub_scripts.= $this->get_javascript_content($value);
\r
201 // improved compression step 2/2
\r
202 $sub_scripts= preg_replace(array("/(\b)editAreaLoader(\b)/", "/(\b)editAreas(\b)/", "/(\b)editArea(\b)/", "/(\b)EditArea(\b)/"), array("eAL", "eAs", "eA", "EA"), $sub_scripts);
\r
203 // $sub_scripts= str_replace(array("editAreaLoader", "editAreas", "editArea", "EditArea"), array("eAL", "eAs", "eA", "EA"), $sub_scripts);
\r
204 $sub_scripts.= "var editArea= eA;EditArea=EA;";
\r
208 // $this->datas.= sprintf("editAreaLoader.iframe_script= \"<script type='text/javascript'>%s</script>\";\n", $sub_scripts);
\r
211 // add the script and use a last compression
\r
212 if( $this->param['compress'] )
\r
214 $last_comp = array( 'Á' => 'this',
\r
217 'Ä' => 'prototype',
\r
222 'É' => 'last_selection',
\r
233 $last_comp = array();
\r
237 foreach( $last_comp as $key => $val )
\r
238 $js_replace .= ".replace(/". $key ."/g,'". str_replace( array("\n", "\r"), array('\n','\r'), $val ) ."')";
\r
240 $this->datas.= sprintf("editAreaLoader.iframe_script= \"<script type='text/javascript'>%s</script>\"%s;\n",
\r
241 str_replace( array_values($last_comp), array_keys($last_comp), $sub_scripts ),
\r
244 if($this->load_all_plugins)
\r
245 $this->datas.="editAreaLoader.all_plugins_loaded=true;\n";
\r
248 // load the template
\r
249 $this->datas.= sprintf("editAreaLoader.template= \"%s\";\n", $this->get_html_content("template.html"));
\r
251 $this->datas.= sprintf("editAreaLoader.iframe_css= \"<style>%s</style>\";\n", $this->get_css_content("edit_area.css"));
\r
253 // $this->datas= "function editArea(){};editArea.prototype.loader= function(){alert('bouhbouh');} var a= new editArea();a.loader();";
\r
257 function send_datas()
\r
259 if($this->param['debug']){
\r
260 $header=sprintf("/* USE PHP COMPRESSION\n");
\r
261 $header.=sprintf("javascript size: based files: %s => PHP COMPRESSION => %s ", $this->file_loaded_size, strlen($this->datas));
\r
262 if($this->use_gzip){
\r
263 $gzip_datas= gzencode($this->datas, 9, FORCE_GZIP);
\r
264 $header.=sprintf("=> GZIP COMPRESSION => %s", strlen($gzip_datas));
\r
265 $ratio = round(100 - strlen($gzip_datas) / $this->file_loaded_size * 100.0);
\r
267 $ratio = round(100 - strlen($this->datas) / $this->file_loaded_size * 100.0);
\r
269 $header.=sprintf(", reduced by %s%%\n", $ratio);
\r
270 $header.=sprintf("compression time: %s\n", $this->get_microtime()-$this->start_time);
\r
271 $header.=sprintf("%s\n", implode("\n", $this->infos));
\r
272 $header.=sprintf("*/\n");
\r
273 $this->datas= $header.$this->datas;
\r
275 $mtime= time(); // ensure that the 2 disk files will have the same update time
\r
276 // generate gzip file and cahce it if using disk cache
\r
277 if($this->use_gzip){
\r
278 $this->gzip_datas= gzencode($this->datas, 9, FORCE_GZIP);
\r
279 if($this->param['use_disk_cache'])
\r
280 $this->file_put_contents($this->gzip_cache_file, $this->gzip_datas, $mtime);
\r
283 // generate full js file and cache it if using disk cache
\r
284 if($this->param['use_disk_cache'])
\r
285 $this->file_put_contents($this->full_cache_file, $this->datas, $mtime);
\r
288 if($this->use_gzip)
\r
289 echo $this->gzip_datas;
\r
297 function get_content($end_uri)
\r
299 $end_uri=preg_replace("/\.\./", "", $end_uri); // Remove any .. (security)
\r
300 $file= $this->path.$end_uri;
\r
301 if(file_exists($file)){
\r
302 $this->infos[]=sprintf("'%s' loaded", $end_uri);
\r
303 /*$fd = fopen($file, 'rb');
\r
304 $content = fread($fd, filesize($file));
\r
307 return $this->file_get_contents($file);
\r
309 $this->infos[]=sprintf("'%s' not loaded", $end_uri);
\r
314 function get_javascript_content($end_uri)
\r
316 $val=$this->get_content($end_uri);
\r
318 $this->compress_javascript($val);
\r
319 $this->prepare_string_for_quotes($val);
\r
323 function compress_javascript(&$code)
\r
325 if($this->param['compress'])
\r
327 // remove all comments
\r
328 // (\"(?:[^\"\\]*(?:\\\\)*(?:\\\"?)?)*(?:\"|$))|(\'(?:[^\'\\]*(?:\\\\)*(?:\\'?)?)*(?:\'|$))|(?:\/\/(?:.|\r|\t)*?(\n|$))|(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))
\r
329 $code= preg_replace("/(\"(?:[^\"\\\\]*(?:\\\\\\\\)*(?:\\\\\"?)?)*(?:\"|$))|(\'(?:[^\'\\\\]*(?:\\\\\\\\)*(?:\\\\\'?)?)*(?:\'|$))|(?:\/\/(?:.|\r|\t)*?(\n|$))|(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))/s", "$1$2$3", $code);
\r
330 // remove line return, empty line and tabulation
\r
331 $code= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', " ", $code);
\r
332 // add line break before "else" otherwise navigators can't manage to parse the file
\r
333 $code= preg_replace('/(\b(else)\b)/', "\n$1", $code);
\r
334 // remove unnecessary spaces
\r
335 $code= preg_replace('/( |\t|\r)*(;|\{|\}|=|==|\-|\+|,|\(|\)|\|\||&\&|\:)( |\t|\r)*/', "$2", $code);
\r
339 function get_css_content($end_uri){
\r
340 $code=$this->get_content($end_uri);
\r
342 $code= preg_replace("/(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))/s", "", $code);
\r
344 $code= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', "", $code);
\r
346 $code= preg_replace('/( |\t|\r)?(\:|,|\{|\})( |\t|\r)+/', "$2", $code);
\r
348 $this->prepare_string_for_quotes($code);
\r
352 function get_html_content($end_uri){
\r
353 $code=$this->get_content($end_uri);
\r
354 //$code= preg_replace('/(\"(?:\\\"|[^\"])*(?:\"|$))|' . "(\'(?:\\\'|[^\'])*(?:\'|$))|(?:\/\/(?:.|\r|\t)*?(\n|$))|(?:\/\*(?:.|\n|\r|\t)*?(?:\*\/|$))/s", "$1$2$3", $code);
\r
355 $code= preg_replace('/(( |\t|\r)*\n( |\t)*)+/s', " ", $code);
\r
356 $this->prepare_string_for_quotes($code);
\r
360 function prepare_string_for_quotes(&$str){
\r
361 // prepare the code to be putted into quotes
\r
362 /*$pattern= array("/(\\\\)?\"/", '/\\\n/' , '/\\\r/' , "/(\r?\n)/");
\r
363 $replace= array('$1$1\\"', '\\\\\\n', '\\\\\\r' , '\\\n"$1+"');*/
\r
364 $pattern= array("/(\\\\)?\"/", '/\\\n/' , '/\\\r/' , "/(\r?\n)/");
\r
365 if($this->param['compress'])
\r
366 $replace= array('$1$1\\"', '\\\\\\n', '\\\\\\r' , '\n');
\r
368 $replace= array('$1$1\\"', '\\\\\\n', '\\\\\\r' , "\\n\"\n+\"");
\r
369 $str= preg_replace($pattern, $replace, $str);
\r
372 function replace_scripts($var, $param1, $param2)
\r
374 $this->$var=stripslashes($param2);
\r
375 return $param1."[];";
\r
378 /* for php version that have not thoses functions */
\r
379 function file_get_contents($file)
\r
381 $fd = fopen($file, 'rb');
\r
382 $content = fread($fd, filesize($file));
\r
384 $this->file_loaded_size+= strlen($content);
\r
388 function file_put_contents($file, &$content, $mtime=-1)
\r
392 $fp = @fopen($file, "wb");
\r
394 fwrite($fp, $content);
\r
396 touch($file, $mtime);
\r
402 function get_microtime()
\r
404 list($usec, $sec) = explode(" ", microtime());
\r
405 return ((float)$usec + (float)$sec);
\r