From 60f001c42526eac5010f873e085754c28a0f9129 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sun, 8 Jun 2008 13:24:23 +0000 Subject: [PATCH] Rewrote Minify::serve() to use built-in cache functions and send cached output with readfile() = huge perf increase. Also only returns array iff quiet is set. Removed a few options. Encoder.php : added option to leave out compress Fixed unit tests for new Minify behavior and to show verbose when called directly on Windows. --- lib/HTTP/Encoder.php | 48 ++-- lib/Minify.php | 331 ++++++++++++++------------ web/ab_tests/ideal_php/before.php | 1 + web/ab_tests/minify/before.js.php | 2 +- web/ab_tests/results_ideal_php.txt | 28 +-- web/test/test_CSS.php | 2 +- web/test/test_HTML.php | 2 +- web/test/test_HTTP_ConditionalGet.php | 2 +- web/test/test_HTTP_Encoder.php | 4 +- web/test/test_Javascript.php | 4 +- web/test/test_Minify.php | 23 +- 11 files changed, 245 insertions(+), 202 deletions(-) diff --git a/lib/HTTP/Encoder.php b/lib/HTTP/Encoder.php index 06ed3af..93c7a80 100644 --- a/lib/HTTP/Encoder.php +++ b/lib/HTTP/Encoder.php @@ -24,7 +24,12 @@ * * * For more control over headers, use getHeaders() and getData() and send your - * own output. + * own output. + * + * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate, + * and gzcompress functions for gzip, deflate, and compress-encoding + * respectively. + * * @package Minify * @subpackage HTTP * @author Stephen Clay @@ -56,7 +61,8 @@ class HTTP_Encoder { * * @return null */ - public function __construct($spec) { + public function __construct($spec) + { $this->_content = $spec['content']; $this->_headers['Content-Length'] = (string)strlen($this->_content); if (isset($spec['type'])) { @@ -78,7 +84,8 @@ class HTTP_Encoder { * * return string */ - public function getContent() { + public function getContent() + { return $this->_content; } @@ -96,7 +103,8 @@ class HTTP_Encoder { * * @return array */ - public function getHeaders() { + public function getHeaders() + { return $this->_headers; } @@ -111,7 +119,8 @@ class HTTP_Encoder { * * @return null */ - public function sendHeaders() { + public function sendHeaders() + { foreach ($this->_headers as $name => $val) { header($name . ': ' . $val); } @@ -128,7 +137,8 @@ class HTTP_Encoder { * * @return null */ - public function sendAll() { + public function sendAll() + { $this->sendHeaders(); echo $this->_content; } @@ -143,12 +153,15 @@ class HTTP_Encoder { * A syntax-aware scan is done of the Accept-Encoding, so the method must * be non 0. The methods are favored in order of deflate, gzip, then * compress. Yes, deflate is always smaller and faster! + * + * @param bool $allowCompress allow the older compress encoding * * @return array two values, 1st is the actual encoding method, 2nd is the * alias of that method to use in the Content-Encoding header (some browsers * call gzip "x-gzip" etc.) */ - public static function getAcceptedEncoding() { + public static function getAcceptedEncoding($allowCompress = true) + { // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) @@ -174,7 +187,7 @@ class HTTP_Encoder { ,$m)) { return array('gzip', $m[1]); } - if (preg_match( + if ($allowCompress && preg_match( '@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' ,$ae ,$m)) { @@ -193,14 +206,15 @@ class HTTP_Encoder { * this fails, false is returned. * * If successful, the Content-Length header is updated, and Content-Encoding - * and Vary headers are added. - * + * and Vary headers are added. + * * @param int $compressionLevel given to zlib functions. If not given, the * class default will be used. * * @return bool success true if the content was actually compressed */ - public function encode($compressionLevel = null) { + public function encode($compressionLevel = null) + { if (null === $compressionLevel) { $compressionLevel = self::$compressionLevel; } @@ -209,11 +223,11 @@ class HTTP_Encoder { || !extension_loaded('zlib')) { return false; - } - if ($this->_encodeMethod[0] === 'gzip') { + } + if ($this->_encodeMethod[0] === 'deflate') { + $encoded = gzdeflate($this->_content, $compressionLevel); + } elseif ($this->_encodeMethod[0] === 'gzip') { $encoded = gzencode($this->_content, $compressionLevel); - } elseif ($this->_encodeMethod[0] === 'deflate') { - $encoded = gzdeflate($this->_content, $compressionLevel); } else { $encoded = gzcompress($this->_content, $compressionLevel); } @@ -225,8 +239,8 @@ class HTTP_Encoder { $this->_headers['Vary'] = 'Accept-Encoding'; $this->_content = $encoded; return true; - } - + } + protected $_content = ''; protected $_headers = array(); protected $_encodeMethod = array('', ''); diff --git a/lib/Minify.php b/lib/Minify.php index 7092043..8061575 100644 --- a/lib/Minify.php +++ b/lib/Minify.php @@ -34,14 +34,6 @@ class Minify { // Apache default and what Yahoo! uses.. const TYPE_JS = 'application/x-javascript'; - /** - * @var bool Should the un-encoded version be cached? - * - * True results in more cache files, but lower PHP load if different - * encodings are commonly requested. - */ - public static $cacheUnencodedVersion = true; - /** * Specify a writeable directory for cache files. If not called, Minify * will not use a disk cache and, for each 200 response, will need to @@ -65,34 +57,37 @@ class Minify { /** * Serve a request for a minified file. * - * @param mixed instance of subclass of Minify_Controller_Base or string name of controller. E.g. 'Files' + * @param mixed instance of subclass of Minify_Controller_Base or string name of + * controller. E.g. 'Files' * * @param array $options controller/serve options * - * @return array success, statusCode, content, and headers generated + * @return mixed null, or, if the 'quiet' option is set to true, an array + * with keys "success" (bool), "statusCode" (int), "content" (string), and + * "headers" (array). * * Here are the available options and defaults in the base controller: * * 'isPublic' : send "public" instead of "private" in Cache-Control * headers, allowing shared caches to cache the output. (default true) * - * 'quiet' : set to true to have no content/headers sent (default false) + * 'quiet' : set to true to have serve() return an array rather than sending + * any headers/output (default false) * * 'encodeOutput' : to disable content encoding, set this to false (default true) * * 'encodeMethod' : generally you should let this be determined by * HTTP_Encoder (leave null), but you can force a particular encoding - * to be returned, by setting this to 'gzip', 'deflate', 'compress', or '' - * (no encoding) + * to be returned, by setting this to 'gzip', 'deflate', or '' (no encoding) * * 'encodeLevel' : level of encoding compression (0 to 9, default 9) * * 'contentTypeCharset' : if given, this will be appended to the Content-Type * header sent (needed mainly for HTML docs) * - * 'setExpires' : set this to a timestamp or GMT date to have Minify send - * an HTTP Expires header instead of checking for conditional GET (default null). - * E.g. (time() + 86400 * 365) for 1yr + * 'setExpires' : set this to a timestamp to have Minify send an HTTP Expires + * header instead of checking for conditional GET (default null). + * E.g. ($_SERVER['REQUEST_TIME'] + 86400 * 365) for 1yr * Note this has nothing to do with server-side caching. * * 'perType' : this is an array of options to send to a particular minifier @@ -128,45 +123,57 @@ class Minify { if (! self::$_options['quiet']) { header(self::$_options['badRequestHeader']); echo self::$_options['badRequestHeader']; + return; + } else { + list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']); + return array( + 'success' => false + ,'statusCode' => (int)$statusCode + ,'content' => '' + ,'headers' => array() + ); } - list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']); - return array( - 'success' => false - ,'statusCode' => (int)$statusCode - ,'content' => '' - ,'headers' => array() - ); } self::$_controller = $controller; - - $cgOptions = array( - 'lastModifiedTime' => self::$_options['lastModifiedTime'] - ,'isPublic' => self::$_options['isPublic'] - ); - if (null !== self::$_options['setExpires']) { - $cgOptions['setExpires'] = self::$_options['setExpires']; - } - - // check client cache - require_once 'HTTP/ConditionalGet.php'; - $cg = new HTTP_ConditionalGet($cgOptions); - if ($cg->cacheIsValid) { - // client's cache is valid - if (! self::$_options['quiet']) { - $cg->sendHeaders(); + + if (null === self::$_options['setExpires']) { + // conditional GET + require_once 'HTTP/ConditionalGet.php'; + $cg = new HTTP_ConditionalGet(array( + 'lastModifiedTime' => self::$_options['lastModifiedTime'] + ,'isPublic' => self::$_options['isPublic'] + )); + if ($cg->cacheIsValid) { + // client's cache is valid + if (! self::$_options['quiet']) { + $cg->sendHeaders(); + return; + } else { + return array( + 'success' => true + ,'statusCode' => 304 + ,'content' => '' + ,'headers' => array() + ); + } + } else { + // client will need output + $headers = $cg->getHeaders(); + unset($cg); } - return array( - 'success' => true - ,'statusCode' => 304 - ,'content' => '' - ,'headers' => array() + } else { + // don't need conditional GET + $privacy = self::$_options['isPublic'] + ? 'public' + : 'private'; + $headers = array( + 'Cache-Control' => $privacy . ', max-age=' + . (self::$_options['setExpires'] - $_SERVER['REQUEST_TIME']) + ,'Expires' => gmdate('D, d M Y H:i:s \G\M\T', self::$_options['setExpires']) ); } - // client will need output - $headers = $cg->getHeaders(); - unset($cg); - + // determine encoding if (self::$_options['encodeOutput']) { if (self::$_options['encodeMethod'] !== null) { @@ -176,26 +183,52 @@ class Minify { // sniff request header require_once 'HTTP/Encoder.php'; // depending on what the client accepts, $contentEncoding may be - // 'x-gzip' while our internal encodeMethod is 'gzip' - list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(); + // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling + // getAcceptedEncoding() with false leaves out compress as an option. + list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false); } } else { self::$_options['encodeMethod'] = ''; // identity (no encoding) } if (null !== self::$_cachePath) { - self::_setupCache(); - // fetch content from cache file(s). - $content = self::_fetchContent(self::$_options['encodeMethod']); - self::$_cache = null; + // using cache + // the goal is to use only the cache methods to sniff the length and + // output the content, as they do not require ever loading the file into + // memory. + $cacheId = self::_getCacheId(); + $encodingExtension = self::$_options['encodeMethod'] + ? ('deflate' === self::$_options['encodeMethod'] + ? '.zd' + : '.zg') + : ''; + $fullCacheId = $cacheId . $encodingExtension; + // check cache for valid entry + $cacheContentLength = self::getCacheSize($fullCacheId, self::$_options['lastModifiedTime']); + $cacheIsReady = (false !== $cacheContentLength); + if (! $cacheIsReady) { + // generate & cache content + $content = self::_combineMinify(); + self::writeCache($cacheId, $content); + self::writeCache($cacheId . '.zd', gzdeflate($content, self::$_options['encodeLevel'])); + self::writeCache($cacheId . '.zg', gzencode($content, self::$_options['encodeLevel'])); + } } else { - // no cache, just combine, minify, encode + // no cache + $cacheIsReady = false; $content = self::_combineMinify(); - $content = self::_encode($content); } - - // add headers to those from ConditionalGet - //$headers['Content-Length'] = strlen($content); + if (! $cacheIsReady && self::$_options['encodeMethod']) { + // still need to encode + $content = ('deflate' === self::$_options['encodeMethod']) + ? gzdeflate($content, self::$_options['encodeLevel']) + : gzencode($content, self::$_options['encodeLevel']); + } + + // add headers + $headers['Content-Length'] = $cacheIsReady + ? $cacheContentLength + : strlen($content); $headers['Content-Type'] = (null !== self::$_options['contentTypeCharset']) ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset'] : self::$_options['contentType']; @@ -209,20 +242,27 @@ class Minify { foreach ($headers as $name => $val) { header($name . ': ' . $val); } - echo $content; + if ($cacheIsReady) { + self::outputCache($fullCacheId); + } else { + echo $content; + } + } else { + return array( + 'success' => true + ,'statusCode' => 200 + ,'content' => $cacheIsReady + ? self::getCache($fullCacheId) + : $content + ,'headers' => $headers + ); } - return array( - 'success' => true - ,'statusCode' => 200 - ,'content' => $content - ,'headers' => $headers - ); } /** * @var mixed null if disk cache is not to be used */ - protected static $_cachePath = null; + private static $_cachePath = null; /** * @var Minify_Controller active controller for current request @@ -234,70 +274,6 @@ class Minify { */ protected static $_options = null; - /** - * @var Cache_Lite_File cache obj for current request - */ - protected static $_cache = null; - - - - /** - * Fetch encoded content from cache (or generate and store it). - * - * If self::$cacheUnencodedVersion is true and encoded content must be - * generated, this function will call itself recursively to fetch (or - * generate) the minified content. Otherwise, it will always recombine - * and reminify files to generate different encodings. - * - * @param string $encodeMethod - * - * @return string minified, encoded content - */ - protected static function _fetchContent($encodeMethod) - { - $cacheId = self::_getCacheId(self::$_controller->sources, self::$_options) - . $encodeMethod; - $content = self::$_cache->get($cacheId, 'Minify'); - if (false === $content) { - // must generate - if ($encodeMethod === '') { - // generate identity cache to store - $content = self::_combineMinify(); - } else { - // fetch identity cache & encode it to store - if (self::$cacheUnencodedVersion) { - // double layer cache - $content = self::_fetchContent(''); - } else { - // recombine - $content = self::_combineMinify(); - } - $content = self::_encode($content); - } - self::$_cache->save($content, $cacheId, 'Minify'); - } - return $content; - } - - /** - * Set self::$_cache to a new instance of Cache_Lite_File (patched 2007-10-03) - * - * @return null - */ - protected static function _setupCache() { - // until the patch is rolled into PEAR, we'll provide the - // class in our package - require_once dirname(__FILE__) . '/Cache/Lite/File.php'; - - self::$_cache = new Cache_Lite_File(array( - 'cacheDir' => self::$_cachePath . '/' - ,'fileNameProtection' => false - - // currently only available in patched Cache_Lite_File - ,'masterTime' => self::$_options['lastModifiedTime'] - )); - } - /** * Combines sources and minifies the result. * @@ -365,29 +341,6 @@ class Minify { return $content; } - /** - * Applies HTTP encoding - * - * @param string $content - * - * @return string - */ - protected static function _encode($content) - { - if (self::$_options['encodeMethod'] === '' - || ! self::$_options['encodeOutput']) { - // "identity" encoding - return $content; - } - require_once 'HTTP/Encoder.php'; - $encoder = new HTTP_Encoder(array( - 'content' => $content - ,'method' => self::$_options['encodeMethod'] - )); - $encoder->encode(self::$_options['encodeLevel']); - return $encoder->getContent(); - } - /** * Make a unique cache id for for this request. * @@ -402,4 +355,74 @@ class Minify { ,self::$_options['perType'] ))); } + + /** + * Write data to file and verify its contents + * + * @param string $file full file path + * + * @param string $data + * + * @return bool success + */ + protected static function _verifiedWrite($file, $data) + { + return (file_put_contents($file, $data, LOCK_EX) + && (md5($data) === md5_file($file)) + ); + } + + /** + * Write data to cache. + * + * @param string $id cache id (e.g. a filename) + * + * @param string $data + * + * @return bool success + */ + protected static function writeCache($id, $data) + { + return self::_verifiedWrite(self::$_cachePath . '/' . $id, $data); + } + + /** + * Get the size of a valid cache entry (if exists) + * + * @param string $id cache id (e.g. a filename) + * + * @param int $srcMtime mtime of the original source file(s) + * + * @return mixed number of bytes on success, false if file doesn't + * exist or is stale + */ + protected static function getCacheSize($id, $srcMtime) + { + $file = self::$_cachePath . '/' . $id; + return (file_exists($file) && (filemtime($file) >= $srcMtime)) + ? filesize($file) + : false; + } + + /** + * Send the cached content to output + * + * @param string $id cache id (e.g. a filename) + */ + protected static function outputCache($id) + { + readfile(self::$_cachePath . '/' . $id); + } + + /** + * Fetch the cached content + * + * @param string $id cache id (e.g. a filename) + * + * @return string + */ + protected static function getCache($id) + { + return file_get_contents(self::$_cachePath . '/' . $id); + } } diff --git a/web/ab_tests/ideal_php/before.php b/web/ab_tests/ideal_php/before.php index 63f9860..ef85501 100644 --- a/web/ab_tests/ideal_php/before.php +++ b/web/ab_tests/ideal_php/before.php @@ -20,5 +20,6 @@ header('Expires: '. gmdate('D, d M Y H:i:s \G\M\T', $_SERVER['REQUEST_TIME'] + ( header('Content-Type: application/x-javascript; charset=utf-8'); header('Content-Encoding: deflate'); header('Content-Length: ' . filesize($cached)); +header('Vary: Accept-Encoding'); readfile($cached); \ No newline at end of file diff --git a/web/ab_tests/minify/before.js.php b/web/ab_tests/minify/before.js.php index d62800a..a91c45c 100644 --- a/web/ab_tests/minify/before.js.php +++ b/web/ab_tests/minify/before.js.php @@ -14,5 +14,5 @@ Minify::serve('Files', array( 'files' => array( dirname(__FILE__) . '/before.js' ) - ,'setExpires' => 31536000 // 1 yr + ,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr )); diff --git a/web/ab_tests/results_ideal_php.txt b/web/ab_tests/results_ideal_php.txt index 7e5a962..8f8feee 100644 --- a/web/ab_tests/results_ideal_php.txt +++ b/web/ab_tests/results_ideal_php.txt @@ -13,31 +13,31 @@ Document Path: /_3rd_party/minify/web/ab_tests/ideal_php/before.php Document Length: 15993 bytes Concurrency Level: 100 -Time taken for tests: 4.531250 seconds +Time taken for tests: 4.437500 seconds Complete requests: 2000 Failed requests: 0 Write errors: 0 -Total transferred: 32802000 bytes +Total transferred: 32848000 bytes HTML transferred: 31986000 bytes -Requests per second: 441.38 [#/sec] (mean) -Time per request: 226.563 [ms] (mean) -Time per request: 2.266 [ms] (mean, across all concurrent requests) -Transfer rate: 7069.35 [Kbytes/sec] received +Requests per second: 450.70 [#/sec] (mean) +Time per request: 221.875 [ms] (mean) +Time per request: 2.219 [ms] (mean, across all concurrent requests) +Transfer rate: 7228.85 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max -Connect: 0 0 2.7 0 31 -Processing: 62 217 50.0 218 671 -Waiting: 31 216 50.7 218 671 -Total: 62 218 49.9 218 671 +Connect: 0 0 2.6 0 15 +Processing: 31 214 37.5 218 359 +Waiting: 31 209 32.7 218 281 +Total: 31 215 37.5 218 359 Percentage of the requests served within a certain time (ms) 50% 218 66% 218 - 75% 218 + 75% 234 80% 234 90% 234 - 95% 265 + 95% 250 98% 296 - 99% 500 - 100% 671 (longest request) + 99% 343 + 100% 359 (longest request) diff --git a/web/test/test_CSS.php b/web/test/test_CSS.php index 98eb654..ff55d29 100644 --- a/web/test/test_CSS.php +++ b/web/test/test_CSS.php @@ -29,7 +29,7 @@ function test_CSS() $minOutput = Minify_CSS::minify($src, $options); $passed = assertTrue($minExpected === $minOutput, 'Minify_CSS : ' . $item); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n"; if (!$passed) { echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n"; diff --git a/web/test/test_HTML.php b/web/test/test_HTML.php index 801ad70..1551c9f 100644 --- a/web/test/test_HTML.php +++ b/web/test/test_HTML.php @@ -19,7 +19,7 @@ function test_HTML() $passed = assertTrue($minExpected === $minOutput, 'Minify_HTML'); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n"; echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n"; echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; diff --git a/web/test/test_HTTP_ConditionalGet.php b/web/test/test_HTTP_ConditionalGet.php index 9cafd87..162eb58 100644 --- a/web/test/test_HTTP_ConditionalGet.php +++ b/web/test/test_HTTP_ConditionalGet.php @@ -97,7 +97,7 @@ function test_HTTP_ConditionalGet() $passed = assertTrue($exp == $ret, 'HTTP_ConditionalGet : ' . $test['desc']); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n--- INM = {$test['inm']} / IMS = {$test['ims']}\n"; echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n"; echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n"; diff --git a/web/test/test_HTTP_Encoder.php b/web/test/test_HTTP_Encoder.php index 4fe6a87..b4c84ed 100644 --- a/web/test/test_HTTP_Encoder.php +++ b/web/test/test_HTTP_Encoder.php @@ -59,7 +59,7 @@ function test_HTTP_Encoder() $ret = HTTP_Encoder::getAcceptedEncoding(); $passed = assertTrue($exp == $ret, 'HTTP_Encoder : ' . $test['desc']); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n--- AE | UA = {$test['ae']} | {$test['ua']}\n"; echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n"; echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n"; @@ -90,7 +90,7 @@ function test_HTTP_Encoder() $passed = assertTrue($ret == $test['exp'], $desc); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n--- {$test['method']}: expected bytes: " , "{$test['exp']}. Returned: {$ret}\n\n"; } diff --git a/web/test/test_Javascript.php b/web/test/test_Javascript.php index d046c78..7e74759 100644 --- a/web/test/test_Javascript.php +++ b/web/test/test_Javascript.php @@ -13,7 +13,7 @@ function test_Javascript() $passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript'); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n"; echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n"; echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; @@ -27,7 +27,7 @@ function test_Javascript() $passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript'); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n"; echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n"; echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; diff --git a/web/test/test_Minify.php b/web/test/test_Minify.php index d33e9e0..d65771f 100644 --- a/web/test/test_Minify.php +++ b/web/test/test_Minify.php @@ -1,5 +1,7 @@ false )); $passed = assertTrue($expected === $output, 'Minify : 304 response'); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\nOutput: " .var_export($output, 1). "\n\n"; if (! $passed) { echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n"; @@ -40,22 +42,24 @@ function test_Minify() } assertTrue( - ! class_exists('Cache_Lite_File', false) - && ! class_exists('HTTP_Encoder', false) + //! class_exists('Cache_Lite_File', false) + ! class_exists('HTTP_Encoder', false) && ! class_exists('Minify_CSS', false) - ,'File.php, Encoder.php, CSS.php not loaded' + ,'Encoder.php, CSS.php not loaded' ); // Test minifying JS and serving with Expires header + $content = preg_replace('/\\r\\n?/', "\n", file_get_contents($minifyTestPath . '/minified.js')); $expected = array( 'success' => true ,'statusCode' => 200 // Minify_Javascript always converts to \n line endings - ,'content' => preg_replace('/\\r\\n?/', "\n", file_get_contents($minifyTestPath . '/minified.js')) + ,'content' => $content ,'headers' => array ( - 'Cache-Control' => 'public', + 'Cache-Control' => 'public, max-age=86400', 'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $tomorrow), + 'Content-Length' => strlen($content), 'Content-Type' => 'application/x-javascript', ) ); @@ -70,7 +74,7 @@ function test_Minify() )); $passed = assertTrue($expected === $output, 'Minify : JS and Expires'); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\nOutput: " .var_export($output, 1). "\n\n"; if (! $passed) { echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n"; @@ -93,8 +97,9 @@ function test_Minify() 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), 'ETag' => "\"{$lastModified}pub\"", 'Cache-Control' => 'max-age=0, public, must-revalidate', + 'Content-Length' => filesize($minifyTestPath . '/minified.css'), 'Content-Type' => 'text/css', - ) + ) ); $output = Minify::serve('Files', array( 'files' => array( @@ -107,7 +112,7 @@ function test_Minify() )); $passed = assertTrue($expected === $output, 'Minify : CSS and Etag/Last-Modified'); - if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) { + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { echo "\nOutput: " .var_export($output, 1). "\n\n"; if (! $passed) { echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";