diff --git a/smartoptimizer/.htaccess b/smartoptimizer/.htaccess index e25e2f9..e2a356f 100644 --- a/smartoptimizer/.htaccess +++ b/smartoptimizer/.htaccess @@ -1,3 +1,3 @@ php_flag zlib.output_compression Off php_flag output_buffering Off -php_value output_handler NULL +php_value output_handler None diff --git a/smartoptimizer/config.php b/smartoptimizer/config.php index dff4f69..92b3b98 100644 --- a/smartoptimizer/config.php +++ b/smartoptimizer/config.php @@ -1,7 +1,7 @@ array(MY_JS_DIR.'test.js', MY_JS_DIR.'test2.js'), -// 'test.css' => array(MY_JS_DIR.'style.css', MY_JS_DIR.'print.css') -//); \ No newline at end of file +/* +$settings['groups'] = array( + 'alljs.js' => array(MY_JS_DIR.'js/getDataService.js', + MY_JS_DIR.'js/recettes_directive.js', + MY_JS_DIR.'js/search_directive.js', + MY_JS_DIR.'js/cloud_directive.js', + MY_JS_DIR.'js/paginate_directive3.js' + ), + 'test.css' => array(MY_JS_DIR.'style.css', MY_JS_DIR.'print.css') +);*/ + +$settings['myrootpath']=implode('/',array_slice(explode('/',$_SERVER["SCRIPT_FILENAME"]),0, -2)); + +$smartoptimizerPath=$settings['myrootpath'].'/smartoptimizer'; + +$settings['js_group_name'] = 'group.alljs.js'; +$settings['alljs'] = $smartoptimizerPath."/".$settings['cacheDir']."alljs.txt"; + +$settings['css_group_name'] = 'group.allcss.css'; +$settings['allcss'] = $smartoptimizerPath."/".$settings['cacheDir']."allcss.txt"; diff --git a/smartoptimizer/index.php b/smartoptimizer/index.php index 43f1399..d67ab76 100644 --- a/smartoptimizer/index.php +++ b/smartoptimizer/index.php @@ -1,5 +1,5 @@ "application/x-shockwave-flash", "ico" => "image/x-icon", ); -/**Processs a time in a string - * - * @param datetime $time - * @return string Date formatted - */ - -function gmdatestr($time = null) { - if (is_null($time)){ $time = time();} - return gmdate("D, d M Y H:i:s", $time) . " GMT"; -} -/**Adds a status header and then exits - * - * @param string $status - */ function headerExit($status) { header("HTTP/1.0 $status"); exit(); } -/** - * Adds Headers to a returning HTTP response that tells a server not to cache the file ever - **/ -function headerNoCache() - { + +function headerNoCache() { // already expired header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); @@ -97,124 +74,113 @@ function headerNoCache() //generate a unique Etag each time header('Etag: '.microtime()); } -/** - * Adds header to the response informing that the file never expires cache it forever. - */ -function headerNeverExpire() { - $exptime = time() + 31557600; - $curtime = time(); - header("Expires: " . gmdatestr($exptime)); - header("Cache-Control: public, max-age=31557600"); - header("Last-Modified: " . gmdatestr($curtime)); - header("Vary: Accept-Encoding"); +function headerNeverExpire(){ + header("Expires: " . gmdatestr(time() + 315360000)); + header("Cache-Control: max-age=315360000"); } -/** - * Adds Headers to a file pls echo a Error that smartoptimizer couldnt process the file - * If Debug is set true this will 404 the file else it will return the file with a Script header. - * - * @global array $settings - * @param string $msg The message to ruturn if Debug is false - */ -function debugExit($msg) { - global $settings; - if (!$settings['debug']) { - headerExit('404 Not Found'); - } - headerNoCache(); - header('Content-Type: text/html; charset=' . $settings['charSet']); - header("Content-Encoding: none"); - echo "//\n"; - exit(); +function debugExit($msg){ + global $settings; + if (!$settings['debug']) { + headerExit('404 Not Found'); + } + headerNoCache(); + header('Content-Type: text/html; charset='.$settings['charSet']); + header("Content-Encoding: none"); + echo "//\n"; + exit(); } -/**Gets the input files modification time and compares it to the modification time of /smartoptimizer/index.php and config.php - * and returns the larges of the 3 (ie the most recent time) - * - * @global array $files - * @global string $fileType - * @staticvar datetime $filesmtime - * @return datetime - **/ +function gmdatestr($time = null) { + if (is_null($time)) $time = time(); + return gmdate("D, d M Y H:i:s", $time) . " GMT"; +} function filesmtime() { global $files, $fileMinifier; static $filesmtime; - if ($filesmtime){return $filesmtime;} - $filesmtime = max(filemtime("minifiers/$fileMinifier.php"), filemtime('index.php'), filemtime('config.php')); + if ($filesmtime) return $filesmtime; + $filesmtime = max(@filemtime("minifiers/$fileMinifier.php"), filemtime('index.php'), filemtime('config.php')); foreach ($files as $file) { - if (!file_exists($file)) {debugExit("File not found ($file).");} + if (!file_exists($file)) debugExit("File not found ($file)."); $filesmtime = max(filemtime($file), $filesmtime); } return $filesmtime; } +function getFileList($listFile) +{ + global $settings; + + $fileNames=array(); + if(file_exists($listFile)) { + + $fp=fopen($listFile,'r'); + + while (($buffer = fgets($fp, 4096)) !== false) { + $fileNames[]=str_replace("\n","",$settings['myrootpath'].'/'.$buffer); + } + if (!feof($fp)) { + echo "Erreur: fgets() a échoué\n"; + } + fclose($fp); + } + + return $fileNames; +} + require 'config.php'; list($query) = explode('?', urldecode($_SERVER['QUERY_STRING'])); -//if(isset($settings['groups']) && substr($query, 0, 6) == 'group.'){ -// //we have a pre-defined group to include -// $group_name = str_replace('group.','',$query); -// if(isset($settings['groups'][$group_name])){ -// $fileNames = $settings['groups'][$group_name]; -// $fileDir = ''; -// } -// else {debugExit("Group ($group_name) not set. Please edit config.");} -//} else { -if (preg_match('/^\/?(.+\/)?(.+)$/', $query, $matchResult)) { - $fileNames = $matchResult[2]; - $fileDir = $settings['baseDir'] . $matchResult[1]; -} else { - debugExit("Invalid file name ($query)"); -} -//} -//check the file exists and is relative to the base directory. -if (strpos(realpath($fileDir), realpath($settings['baseDir'])) !== 0) {debugExit("File is out of base directory.");} - -if ($settings['concatenate']) { - if(!is_array($fileNames)){ - $files = explode($settings['separator'], $fileNames); - } else { - $files = $fileNames; - } - $settings['concatenate'] = count($files) > 1; -} else {$files = array($fileNames);} +if ($settings['concatenate'] && $query == $settings['js_group_name'] ) { + $fileDir = ''; + $files=getFileList($settings['alljs']); + // $files=array_merge($files,getFileList($settings['alljs'])); + +} elseif ($settings['concatenate'] && $query == $settings['css_group_name'] ) { + $fileDir = ''; + $files=getFileList($settings['allcss']); +}else { + if (preg_match('/^\/?(.+\/)?(.+)$/', $query, $matchResult)) { + $fileNames = $matchResult[2]; + $fileDir = $settings['baseDir'].$matchResult[1]; + } else debugExit("Invalid file name ($query)"); + $files = array($fileNames); +} foreach ($files as $key => $file) { if (preg_match('/^[^\x00]+\.([a-z0-9]+)$/i', $file, $matchResult)) { $fileTypes[] = strtolower($matchResult[1]); - } else {debugExit("Unsupported file ($file)");} + } else debugExit("Unsupported file ($file)"); $files[$key] = $fileDir.$file; } if ($settings['concatenate']) { - if (count(array_unique($fileTypes)) > 1) {debugExit("Files must be of the same type.");} + if (count(array_unique($fileTypes)) > 1) debugExit("Files must be of the same type."); } -//check we have a minifier + $fileType = $fileTypes[0]; $fileMinifier = $settings[$fileType.'Minifier']; -if(!file_exists('minifiers/'.$fileMinifier.'.php')) {debugExit($fileType.'Minifier not found. Please create "minifiers/'.$fileMinifier.'.php" or change minifier in config.php');} -//check the file meets our filetypes list or -if (!isset($mimeTypes[$fileType])){ debugExit("Unsupported file type ($fileType)");} +if(!file_exists('minifiers/'.$fileMinifier.'.php')) debugExit($fileType.'Minifier not found. Please create "minifiers/'.$fileMinifier.'.php" or change minifier in config.php'); + +if (!isset($mimeTypes[$fileType])) debugExit("Unsupported file type ($fileType)"); header("Content-Type: {$mimeTypes[$fileType]}; charset=".$settings['charSet']); -// lets check and see if the client accepts compression and change our variable if it doesnt also check the filetype to ensure we want to compress. + $settings['gzip'] = ($settings['gzip'] && !in_array($fileType, $settings['gzipExceptions']) && - in_array('gzip', array_map('trim', explode(',' , htmlspecialchars($_SERVER['HTTP_ACCEPT_ENCODING'], ENT_QUOTES)))) && + in_array('gzip', array_map('trim', explode(',' , @$_SERVER['HTTP_ACCEPT_ENCODING']))) && function_exists('gzencode')); -// ok client likes compression - lets tell it we are sending a compressed file. -if ($settings['gzip']){ header("Content-Encoding: gzip");} -//check we have a minifier + +if ($settings['gzip']) header("Content-Encoding: gzip"); + $settings['minify'] = $settings['minify'] && file_exists('minifiers/'.$fileMinifier.'.php'); -//if its css and we have an opera browser embed the css -$settings['embed'] = $settings['embed'] && $fileType == 'css' && (!preg_match('/msie/i', htmlspecialchars($_SERVER['HTTP_USER_AGENT'])) || preg_match('/msie 8|opera/i', htmlspecialchars($_SERVER['HTTP_USER_AGENT']))); -//check if we want to call the cache. +$settings['embed'] = $settings['embed'] && $fileType == 'css' && (!preg_match('/msie/i', $_SERVER['HTTP_USER_AGENT']) || preg_match('/msie 8|opera/i', $_SERVER['HTTP_USER_AGENT'])); $settings['serverCache'] = $settings['serverCache'] && ($settings['minify'] || $settings['gzip'] || $settings['concatenate'] || $settings['embed']); if ($settings['serverCache']) { @@ -225,9 +191,9 @@ function_exists('gzencode')); ($settings['serverCache'] && (!file_exists($cachedFile) || ($settings['serverCacheCheck'] && filesmtime() > filemtime($cachedFile))))); if ($settings['clientCache'] && $settings['clientCacheCheck']) { - if ($settings['serverCache'] && !$generateContent) {$mtime = filemtime($cachedFile);} -elseif ($settings['serverCache']) {$mtime = time();} - else {$mtime = filesmtime();} + if ($settings['serverCache'] && !$generateContent) $mtime = filemtime($cachedFile); + elseif ($settings['serverCache']) $mtime = time(); + else $mtime = filesmtime(); $mtimestr = gmdatestr($mtime); } @@ -237,7 +203,7 @@ function_exists('gzencode')); header("Cache-Control: must-revalidate"); } elseif ($settings['clientCache']) { headerNeverExpire(); - } else { headerNoCache();} + } else headerNoCache(); if ($generateContent) { if ($settings['minify']){ @@ -245,10 +211,10 @@ function_exists('gzencode')); } $content = array(); - foreach ($files as $file){(($content[] = file_get_contents($file)) !== false) || debugExit("File not found ($file).");} + foreach ($files as $file) (($content[] = @file_get_contents($file)) !== false) || debugExit("File not found ($file)."); $content = implode("\n", $content); - if ($settings['minify']) {$content = call_user_func('minify_' . $fileType, $content);} - if ($settings['gzip']){ $content = gzencode($content, $settings['compressionLevel']);} + if ($settings['minify']) $content = call_user_func('minify_' . $fileType, $content); + if ($settings['gzip']) $content = gzencode($content, $settings['compressionLevel']); if ($settings['serverCache']) { $handle = @fopen($cachedFile, 'w') or debugExit("Could not create cache file($cachedFile)."); fwrite($handle, $content); @@ -260,4 +226,5 @@ function_exists('gzencode')); header('Content-Length: ' . filesize($cachedFile)); readfile($cachedFile); } -} else {headerExit('304 Not Modified');} \ No newline at end of file +} else headerExit('304 Not Modified'); +?> diff --git a/smartoptimizer/minifiers/css.php b/smartoptimizer/minifiers/css.php index 533dc98..e8cf072 100644 --- a/smartoptimizer/minifiers/css.php +++ b/smartoptimizer/minifiers/css.php @@ -14,12 +14,9 @@ function convertUrl($url, $count) if (preg_match('@^[^/]+:@', $url)) return $url; $fileType = substr(strrchr($url, '.'), 1); - if (isset($mimeTypes[$fileType])) - $mimeType = $mimeTypes[$fileType]; - elseif (function_exists('mime_content_type') && file_exists($fileDir.$url)) - $mimeType = mime_content_type($fileDir.$url); - else - $mimeType = null; + if (isset($mimeTypes[$fileType])) $mimeType = $mimeTypes[$fileType]; + elseif (function_exists('mime_content_type')) $mimeType = mime_content_type($url); + else $mimeType = null; if (!$settings['embed'] || !file_exists($fileDir.$url) || @@ -37,7 +34,7 @@ function convertUrl($url, $count) $contents = file_get_contents($fileDir.$url); - if ($fileType == 'css') { + if ($fileType === 'css') { $oldFileDir = $fileDir; $fileDir = rtrim(dirname($fileDir.$url), '\/').'/'; $oldBaseUrl = $baseUrl; @@ -52,60 +49,61 @@ function convertUrl($url, $count) } function minify_css($str) { + $strsize=strlen($str); $res = ''; $i=0; $inside_block = false; $current_char = ''; - while ($i+1+\n\r\t ]{2}$/'; if (strlen($res) && preg_match($pattern, $res[strlen($res)-1].$str[$i+1])) $res .= $current_char; @@ -113,7 +111,7 @@ function minify_css($str) { $i++; } - if ($i diff --git a/smartoptimizer/minifiers/html.php b/smartoptimizer/minifiers/html.php new file mode 100644 index 0000000..3b1099b --- /dev/null +++ b/smartoptimizer/minifiers/html.php @@ -0,0 +1,49 @@ +|', $buffer, $out); + $jsScripts=$out[1]; + + $fp=fopen($settings['alljs'],"w"); + foreach ($jsScripts as $value) fwrite($fp, $value."\n"); + fclose($fp); + + $html_modified=preg_replace('||', '',$buffer ); + $buffer=preg_replace('||', '',$html_modified ); + + /* here we take automatically all css files found in HTML and we concatenate them. */ + preg_match_all('||', $buffer, $out); + $cssScripts=$out[1]; + + $fp=fopen($settings['allcss'],"w"); + foreach ($cssScripts as $value) fwrite($fp, $value."\n"); + fclose($fp); + + $html_modified=preg_replace('||', '',$buffer ); + $buffer=preg_replace('||', '',$html_modified ); + + } + + $search = array( + '/\>[^\S ]+/s', // strip whitespaces after tags, except space + '/[^\S ]+\', + '<', + '\\1' + ); + + $buffer = preg_replace($search, $replace, $buffer); + + return $buffer; +} + +?> diff --git a/smartoptimizer/minifiers/js.php b/smartoptimizer/minifiers/js.php index 6a7fe66..1902e04 100644 --- a/smartoptimizer/minifiers/js.php +++ b/smartoptimizer/minifiers/js.php @@ -3,47 +3,48 @@ * SmartOptimizer JavaScript Minifier */ function minify_js($str) { + $strsize=strlen($str); $res = ''; $maybe_regex = true; $i=0; $current_char = ''; - while ($i+11 && preg_match('/^(\+\+)|(--)$/', $res[strlen($res)-2].$res[strlen($res)-1])) || - (strlen($str)>$i+2 && preg_match('/^(\+\+)|(--)$/', $str[$i+1].$str[$i+2])) || + ($strsize>$i+2 && preg_match('/^(\+\+)|(--)$/', $str[$i+1].$str[$i+2])) || preg_match('/^(\+\+)|(--)$/', $res[strlen($res)-1].$str[$i+1])// || // for example i+ ++j; )) $res .= $current_char; } else $res .= $current_char; @@ -91,7 +92,7 @@ function minify_js($str) { $i++; } - if ($i \ No newline at end of file +?> diff --git a/smartoptimizer/minifiers/packer.php b/smartoptimizer/minifiers/packer.php index d31628e..7db139d 100644 --- a/smartoptimizer/minifiers/packer.php +++ b/smartoptimizer/minifiers/packer.php @@ -1,40 +1,51 @@ + * + * changelog: + * version 1.3 (jan 19, 2015) + * - improved variable renaming + * + * version 1.2 (aug 13, 2014): + * - fixed a bug, '\n' packed then unpacked becomes '\'. + * - improved variable renaming (any -> a, any -> b, any ->c) instead of (pack -> p, packer -> p ??) + */ /* 9 April 2008. version 1.1 - * + * * This is the php version of the Dean Edwards JavaScript's Packer, * Based on : - * + * * ParseMaster, version 1.0.2 (2005-08-19) Copyright 2005, Dean Edwards * a multi-pattern parser. * KNOWN BUG: erroneous behavior when using escapeChar with a replacement * value that is a function - * + * * packer, version 2.0.2 (2005-08-19) Copyright 2004-2005, Dean Edwards - * + * * License: http://creativecommons.org/licenses/LGPL/2.1/ - * + * * Ported to PHP by Nicolas Martin. - * + * * ---------------------------------------------------------------------- * changelog: * 1.1 : correct a bug, '\0' packed then unpacked becomes '\'. * ---------------------------------------------------------------------- - * + * * examples of usage : * $myPacker = new JavaScriptPacker($script, 62, true, false); * $packed = $myPacker->pack(); - * + * * or - * + * * $myPacker = new JavaScriptPacker($script, 'Normal', true, false); * $packed = $myPacker->pack(); - * + * * or (default values) - * + * * $myPacker = new JavaScriptPacker($script); * $packed = $myPacker->pack(); - * - * + * + * * params of the constructor : * $script: the JavaScript to pack, string. * $encoding: level of encoding, int or string : @@ -45,14 +56,14 @@ * $specialChars: if you are flagged your private and local variables * in the script, boolean. * default: false. - * + * * The pack() method return the compressed JavasScript, as a string. - * + * * see http://dean.edwards.name/packer/usage/ for more information. - * + * * Notes : * # need PHP 5 . Tested with PHP 5.1.2, 5.1.3, 5.1.4, 5.2.3 - * + * * # The packed result may be different than with the Dean Edwards * version, but with the same length. The reason is that the PHP * function usort to sort array don't necessarily preserve the @@ -60,50 +71,42 @@ * in fact preserve this order (but that's not require by the * ECMAScript standard). So the encoded keywords order can be * different in the two results. - * + * * # Be careful with the 'High ASCII' Level encoding if you use - * UTF-8 in your files... + * UTF-8 in your files... */ - - class JavaScriptPacker { // constants const IGNORE = '$1'; - // validate parameters private $_script = ''; private $_encoding = 62; private $_fastDecode = true; private $_specialChars = false; - private $LITERAL_ENCODING = array( 'None' => 0, 'Numeric' => 10, 'Normal' => 62, 'High ASCII' => 95 ); - public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false) { $this->_script = $_script . "\n"; if (array_key_exists($_encoding, $this->LITERAL_ENCODING)) $_encoding = $this->LITERAL_ENCODING[$_encoding]; $this->_encoding = min((int)$_encoding, 95); - $this->_fastDecode = $_fastDecode; + $this->_fastDecode = $_fastDecode; $this->_specialChars = $_specialChars; } - public function pack() { $this->_addParser('_basicCompression'); if ($this->_specialChars) $this->_addParser('_encodeSpecialChars'); if ($this->_encoding) $this->_addParser('_encodeKeywords'); - // go! return $this->_pack($this->_script); } - // apply all parsing routines private function _pack($script) { for ($i = 0; isset($this->_parsers[$i]); $i++) { @@ -111,13 +114,11 @@ private function _pack($script) { } return $script; } - // keep a list of parsing functions, they'll be executed all at once private $_parsers = array(); private function _addParser($parser) { $this->_parsers[] = $parser; } - // zero encoding - just removal of white space and comments private function _basicCompression($script) { $parser = new ParseMaster(); @@ -139,7 +140,6 @@ private function _basicCompression($script) { $parser->add('/;+\\s*([};])/', '$2'); // apply the above $script = $parser->exec($script); - // remove white-space $parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3'); $parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3'); @@ -147,20 +147,25 @@ private function _basicCompression($script) { // done return $parser->exec($script); } - private function _encodeSpecialChars($script) { $parser = new ParseMaster(); // replace: $name -> n, $$name -> na + // $parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/', + // array('fn' => '_replace_name') + // ); + // ptcong edited + // to avoid conflict vars: buttons1, buttons2 + // and use smart named $parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/', array('fn' => '_replace_name') ); + // end edited // replace: _name -> _0, double-underscore (__name) is ignored $regexp = '/\\b_[A-Za-z\\d]\\w*/'; // build the word list $keywords = $this->_analyze($script, $regexp, '_encodePrivate'); // quick ref $encoded = $keywords['encoded']; - $parser->add($regexp, array( 'fn' => '_replace_encoded', @@ -169,7 +174,6 @@ private function _encodeSpecialChars($script) { ); return $parser->exec($script); } - private function _encodeKeywords($script) { // escape high-ascii values already in the script (i.e. in strings) if ($this->_encoding > 62) @@ -182,7 +186,6 @@ private function _encodeKeywords($script) { // build the word list $keywords = $this->_analyze($script, $regexp, $encode); $encoded = $keywords['encoded']; - // encode $parser->add($regexp, array( @@ -192,13 +195,12 @@ private function _encodeKeywords($script) { ); if (empty($script)) return $script; else { - //$res = $parser->exec($script); + // $res = $parser->exec($script); //$res = $this->_bootStrap($res, $keywords); //return $res; return $this->_bootStrap($parser->exec($script), $keywords); } } - private function _analyze($script, $regexp, $encode) { // analyse // retreive all words in the script @@ -245,7 +247,6 @@ private function _analyze($script, $regexp, $encode) { $this->_count[$word] = 0; } } while ($i); - // sort the words by frequency // Note: the javascript and php version of sort can be different : // in php manual, usort : @@ -255,7 +256,7 @@ private function _analyze($script, $regexp, $encode) { // but equivalent. // the ECMAscript standard does not guarantee this behaviour, // and thus not all browsers (e.g. Mozilla versions dating back to at - // least 2003) respect this. + // least 2003) respect this. usort($unsorted, array(&$this, '_sortWords')); $j = 0; // because there are "protected" words in the list @@ -271,26 +272,20 @@ private function _analyze($script, $regexp, $encode) { 'encoded' => $_encoded, 'protected' => $_protected); } - private $_count = array(); private function _sortWords($match1, $match2) { return $this->_count[$match2] - $this->_count[$match1]; } - // build the boot function used for loading and decoding private function _bootStrap($packed, $keywords) { $ENCODE = $this->_safeRegExp('$encode\\($count\\)'); - // $packed: the packed script $packed = "'" . $this->_escape($packed) . "'"; - // $ascii: base for encoding $ascii = min(count($keywords['sorted']), $this->_encoding); if ($ascii == 0) $ascii = 1; - // $count: number of words contained in the script $count = count($keywords['sorted']); - // $keywords: list of words contained in the script foreach ($keywords['protected'] as $i=>$value) { $keywords['sorted'][$i] = ''; @@ -298,13 +293,11 @@ private function _bootStrap($packed, $keywords) { // convert from a string to an array ksort($keywords['sorted']); $keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')"; - $encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii); $encode = $this->_getJSFunction($encode); $encode = preg_replace('/_encoding/','$ascii', $encode); $encode = preg_replace('/arguments\\.callee/','$encode', $encode); $inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : ''); - // $decode: code snippet to speed up decoding if ($this->_fastDecode) { // create the decoder @@ -319,7 +312,6 @@ private function _bootStrap($packed, $keywords) { if ($count == 0) $decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1); } - // boot function $unpack = $this->_getJSFunction('_unpack'); if ($this->_fastDecode) { @@ -343,7 +335,6 @@ private function _bootStrap($packed, $keywords) { // pack the boot function too $unpackPacker = new JavaScriptPacker($unpack, 0, false, true); $unpack = $unpackPacker->pack(); - // arguments $params = array($packed, $ascii, $count, $keywords); if ($this->_fastDecode) { @@ -351,11 +342,9 @@ private function _bootStrap($packed, $keywords) { $params[] = '{}'; } $params = implode(',', $params); - // the whole thing return 'eval(' . $unpack . '(' . $params . "))\n"; } - private $buffer; private function _insertFastDecode($match) { return '{' . $this->buffer . ';'; @@ -363,25 +352,21 @@ private function _insertFastDecode($match) { private function _insertFastEncode($match) { return '{$encode=' . $this->buffer . ';'; } - // mmm.. ..which one do i need ?? private function _getEncoder($ascii) { return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ? '_encode95' : '_encode62' : '_encode36' : '_encode10'; } - // zero encoding // characters: 0123456789 private function _encode10($charCode) { return $charCode; } - // inherent base36 support // characters: 0123456789abcdefghijklmnopqrstuvwxyz private function _encode36($charCode) { return base_convert($charCode, 10, 36); } - // hitch a ride on base36 and add the upper case alpha characters // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ private function _encode62($charCode) { @@ -390,36 +375,29 @@ private function _encode62($charCode) { $res = $this->_encode62((int)($charCode / $this->_encoding)); } $charCode = $charCode % $this->_encoding; - if ($charCode > 35) return $res . chr($charCode + 29); else return $res . base_convert($charCode, 10, 36); } - // use high-ascii values // characters: ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ private function _encode95($charCode) { $res = ''; if ($charCode >= $this->_encoding) $res = $this->_encode95($charCode / $this->_encoding); - return $res . chr(($charCode % $this->_encoding) + 161); } - private function _safeRegExp($string) { return '/'.preg_replace('/\$/', '\\\$', $string).'/'; } - private function _encodePrivate($charCode) { return "_" . $charCode; } - // protect characters used by the parser private function _escape($script) { return preg_replace('/([\\\\\'])/', '\\\$1', $script); } - // protect high-ascii characters already in the script private function _escape95($script) { return preg_replace_callback( @@ -431,15 +409,12 @@ private function _escape95($script) { private function _escape95Bis($match) { return '\x'.((string)dechex(ord($match))); } - - private function _getJSFunction($aName) { if (defined('self::JSFUNCTION'.$aName)) return constant('self::JSFUNCTION'.$aName); - else + else return ''; } - // JavaScript Functions used. // Note : In Dean's version, these functions are converted // with 'String(aFunctionName);'. @@ -447,13 +422,11 @@ private function _getJSFunction($aName) { // 'while (aBool) anAction();' is converted to // 'while (aBool) { anAction(); }'. // The JavaScript functions below are corrected. - // unpacking function - this is the boot strap function // data extracted from this packing routine is passed to // this function when decoded in the target // NOTE ! : without the ';' final. const JSFUNCTION_unpack = - 'function($packed, $ascii, $count, $keywords, $encode, $decode) { while ($count--) { if ($keywords[$count]) { @@ -470,13 +443,11 @@ private function _getJSFunction($aName) { return $packed; }'; */ - // code-snippet inserted into the unpacker to speed up decoding const JSFUNCTION_decodeBody = //_decode = function() { // does the browser support String.replace where the // replacement value is a function? - ' if (!\'\'.replace(/^/, String)) { // decode all the values we need while ($count--) { @@ -503,21 +474,18 @@ private function _getJSFunction($aName) { $count = 1; }'; */ - // zero encoding // characters: 0123456789 const JSFUNCTION_encode10 = 'function($charCode) { return $charCode; }';//;'; - // inherent base36 support // characters: 0123456789abcdefghijklmnopqrstuvwxyz const JSFUNCTION_encode36 = 'function($charCode) { return $charCode.toString(36); }';//;'; - // hitch a ride on base36 and add the upper case alpha characters // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ const JSFUNCTION_encode62 = @@ -525,27 +493,21 @@ private function _getJSFunction($aName) { return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) + (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36)); }'; - // use high-ascii values // characters: ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ const JSFUNCTION_encode95 = 'function($charCode) { return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) + String.fromCharCode($charCode % _encoding + 161); -}'; - +}'; } - - class ParseMaster { public $ignoreCase = false; public $escapeChar = ''; - // constants const EXPRESSION = 0; const REPLACEMENT = 1; const LENGTH = 2; - // used to determine nesting levels private $GROUPS = '/\\(/';//g private $SUB_REPLACE = '/\\$\\d/'; @@ -554,12 +516,10 @@ class ParseMaster { private $ESCAPE = '/\\\./';//g private $QUOTE = '/\'/'; private $DELETED = '/\\x01[^\\x01]*\\x01/';//g - public function add($expression, $replacement = '') { // count the number of sub-expressions // - add one because each pattern is itself a sub-expression $length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string)$expression), $out); - // treat only strings $replacement if (is_string($replacement)) { // does the pattern deal with sub-expressions? @@ -587,11 +547,9 @@ public function add($expression, $replacement = '') { if (!empty($expression)) $this->_add($expression, $replacement, $length); else $this->_add('/^$/', $replacement, $length); } - public function exec($string) { // execute the global replacement $this->_escaped = array(); - // simulate the _patterns.toSTring of Dean $regexp = '/'; foreach ($this->_patterns as $reg) { @@ -599,7 +557,6 @@ public function exec($string) { } $regexp = substr($regexp, 0, -1) . '/'; $regexp .= ($this->ignoreCase) ? 'i' : ''; - $string = $this->_escape($string, $this->escapeChar); $string = preg_replace_callback( $regexp, @@ -610,29 +567,23 @@ public function exec($string) { $string ); $string = $this->_unescape($string, $this->escapeChar); - return preg_replace($this->DELETED, '', $string); } - public function reset() { // clear the patterns collection so that this object may be re-used $this->_patterns = array(); } - // private private $_escaped = array(); // escaped characters private $_patterns = array(); // patterns stored by index - // create and add a new pattern to the patterns collection private function _add() { $arguments = func_get_args(); $this->_patterns[] = $arguments; } - // this is the global replace function (it's quite complicated) private function _replacement($arguments) { if (empty($arguments)) return ''; - $i = 1; $j = 0; // loop through the patterns while (isset($this->_patterns[$j])) { @@ -640,28 +591,22 @@ private function _replacement($arguments) { // do we have a result? if (isset($arguments[$i]) && ($arguments[$i] != '')) { $replacement = $pattern[self::REPLACEMENT]; - if (is_array($replacement) && isset($replacement['fn'])) { - if (isset($replacement['data'])) $this->buffer = $replacement['data']; return call_user_func(array(&$this, $replacement['fn']), $arguments, $i); - } elseif (is_int($replacement)) { return $arguments[$replacement + $i]; - } $delete = ($this->escapeChar == '' || strpos($arguments[$i], $this->escapeChar) === false) ? '' : "\x01" . $arguments[$i] . "\x01"; return $delete . $replacement; - // skip over references to sub-expressions } else { $i += $pattern[self::LENGTH]; } } } - private function _backReferences($match, $offset) { $replacement = $this->buffer['replacement']; $quote = $this->buffer['quote']; @@ -671,53 +616,86 @@ private function _backReferences($match, $offset) { } return $replacement; } - + // ptcong edited + private $_names = array(); + private $_names_count = 0; + private function _get_name() { + $num = $this->_names_count; + $base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; + $b = strlen($base); + $r = $num % $b; + $res = $base[$r]; + $q = floor($num/$b); + while ($q) { + $r = $q % $b; + if ($q == 1 && $r == 1) $r = 0; + $q = floor($q/$b); + $res = $base[$r].$res; + } + return $res; + } private function _replace_name($match, $offset){ - $length = strlen($match[$offset + 2]); - $start = $length - max($length - strlen($match[$offset + 3]), 0); - return substr($match[$offset + 1], $start, $length) . $match[$offset + 4]; + static $keywords = array( + 'abstract','as','boolean','break','byte','case','catch','char','class', + 'const','continue','debugger','default','delete','do','double','else', + 'enum','export','extends','false','final','finally','float','for', + 'function','goto','if','implements','import','in','instanceof','int', + 'long','native','new','null','package','private','protected','public', + 'return','short','static','super','switch','synchronized','this', + 'throw','throws','transient','true','try','typeof','var','void', + 'while','with','yield','let','interface', + // avoid conflict with some variables + 'index', 'e', 'err', + ); + $varname = $match[0]; + if (empty($this->_names[$varname])) { + $named = $this->_get_name(); + $this->_names_count++; + if (in_array($named, $keywords)) { + return $this->_replace_name($match, $offset); + } + $this->_names[$varname] = $named; + } + return $this->_names[$varname]; } - + // end edited private function _replace_encoded($match, $offset) { return $this->buffer[$match[$offset]]; } - - // php : we cannot pass additional data to preg_replace_callback, // and we cannot use &$this in create_function, so let's go to lower level private $buffer; - // encode escaped characters private function _escape($string, $escapeChar) { if ($escapeChar) { $this->buffer = $escapeChar; + // ptcong90 edited to ensure that unescape job will work well + // original: '/\\' . $escapeChar . '(.)' .'/', return preg_replace_callback( - '/\\' . $escapeChar . '(.)' .'/', + '/\\' . $escapeChar . '(.)?' .'/', array(&$this, '_escapeBis'), $string ); - } else { return $string; } } private function _escapeBis($match) { - $this->_escaped[] = $match[1]; + // ptcong90 edited to ensure that unescape job will work well + // original: $this->_escaped[] = $match[1]; + $this->_escaped[] = isset($match[1]) ? $match[1] : ''; return $this->buffer; } - // decode escaped characters private function _unescape($string, $escapeChar) { if ($escapeChar) { - $regexp = '/'.'\\'.$escapeChar.'/'; + $regexp = '/\\'.$escapeChar.'/'; $this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0); - return preg_replace_callback - ( + return preg_replace_callback( $regexp, array(&$this, '_unescapeBis'), $string ); - } else { return $string; } @@ -733,7 +711,6 @@ private function _unescapeBis() { $this->buffer['i']++; return $this->buffer['escapeChar'] . $temp; } - private function _internalEscape($string) { return preg_replace($this->ESCAPE, '', $string); } @@ -741,6 +718,6 @@ private function _internalEscape($string) { function minify_js($js){ $packer = new JavaScriptPacker($js, 'Normal', true, false); + return $packer->pack(); } -?>