From 79a4e17fb6dc4827b17527fcd363311004991659 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Fri, 29 Aug 2008 22:56:34 +0000 Subject: [PATCH] All PHP/doc files have Unix newlines w/ no BOM --- HISTORY | 50 +- LICENSE | 50 +- README | 92 +-- min/config.php | 74 +- min/groupsConfig.php | 20 +- min/index.php | 122 +-- min/lib/HTTP/ConditionalGet.php | 524 ++++++------- min/lib/JSMin.php | 580 +++++++------- min/lib/Minify.php | 826 +++++++++---------- min/lib/Minify/Build.php | 202 ++--- min/lib/Minify/CSS.php | 2 +- min/lib/Minify/Cache/File.php | 206 ++--- min/lib/Minify/Controller/Base.php | 368 ++++----- min/lib/Minify/Controller/Files.php | 142 ++-- min/lib/Minify/Controller/Groups.php | 146 ++-- min/lib/Minify/Controller/Page.php | 160 ++-- min/lib/Minify/Controller/Version1.php | 220 +++--- min/lib/Minify/HTML.php | 320 ++++---- min/lib/Minify/Javascript.php | 148 ++-- min/lib/Minify/Lines.php | 248 +++--- min/lib/Minify/Source.php | 344 ++++---- min/min_group_uri.php | 62 +- web/ab_tests/ideal_php/before.php | 48 +- web/ab_tests/minify/test_Files.php | 30 +- web/ab_tests/minify/test_Groups.php | 30 +- web/ab_tests/minify/test_Version1.php | 24 +- web/ab_tests/v1.0/minify.php | 1000 ++++++++++++------------ web/config.php | 34 +- web/examples/1/_groupsSources.php | 20 +- web/examples/1/m.php | 28 +- web/examples/2/_groupsSources.php | 20 +- web/examples/2/m.php | 28 +- web/examples/3/m.php | 86 +- web/test/HTTP_ConditionalGet/2.php | 88 +-- web/test/HTTP_ConditionalGet/5.php | 54 +- web/test/_inc.php | 58 +- web/test/test_CSS.php | 84 +- web/test/test_HTML.php | 118 +-- web/test/test_HTTP_ConditionalGet.php | 232 +++--- web/test/test_HTTP_Encoder.php | 446 +++++------ web/test/test_Javascript.php | 74 +- web/test/test_Lines.php | 64 +- web/test/test_Minify.php | 262 +++---- web/test/test_Minify_Build.php | 68 +- web/test/test_all.php | 20 +- web/tools/encodeFile.php | 62 +- web/tools/minifyFile.php | 101 +-- web/version1/minify.php | 12 +- 48 files changed, 3999 insertions(+), 3998 deletions(-) diff --git a/HISTORY b/HISTORY index 86f941f..6e91a48 100644 --- a/HISTORY +++ b/HISTORY @@ -1,26 +1,26 @@ -Minify Release History - -Version 2.0.2 beta (2008-06-24) - * Fast new cache system. Cached files served almost 3x as fast. - * Dropped support of compress encoding (though HTTP_Encoder still supports it) - -Version 2.0.1 (2008-05-31) - * E_STRICT compliance (Cache_Lite_File). - -Version 2.0.0 (2008-05-22) - * Complete code overhaul. Minify is now a PEAR-style class and toolkit - for building customized minifying file servers. - * Content-Encoding: deflate/gzip/compress, based on request headers - * Expanded CSS and HTML minifiers with test cases - * Easily plug-in 3rd-party minifiers (like Packer) - * Plug-able front end controller allows changing the way files are chosen - * Compression & encoding modules lazy-loaded as needed (304 responses use minimal code) - * Separate utility classes for HTTP encoding and cache control - -Version 1.0.1 (2007-05-05) - * Fixed various problems resolving pathnames when hosted on an NFS mount. - * Fixed 'undefined constant' notice. - * Replaced old JSMin library with a much faster custom implementation. - -Version 1.0.0 (2007-05-02) +Minify Release History + +Version 2.0.2 beta (2008-06-24) + * Fast new cache system. Cached files served almost 3x as fast. + * Dropped support of compress encoding (though HTTP_Encoder still supports it) + +Version 2.0.1 (2008-05-31) + * E_STRICT compliance (Cache_Lite_File). + +Version 2.0.0 (2008-05-22) + * Complete code overhaul. Minify is now a PEAR-style class and toolkit + for building customized minifying file servers. + * Content-Encoding: deflate/gzip/compress, based on request headers + * Expanded CSS and HTML minifiers with test cases + * Easily plug-in 3rd-party minifiers (like Packer) + * Plug-able front end controller allows changing the way files are chosen + * Compression & encoding modules lazy-loaded as needed (304 responses use minimal code) + * Separate utility classes for HTTP encoding and cache control + +Version 1.0.1 (2007-05-05) + * Fixed various problems resolving pathnames when hosted on an NFS mount. + * Fixed 'undefined constant' notice. + * Replaced old JSMin library with a much faster custom implementation. + +Version 1.0.0 (2007-05-02) * First release. \ No newline at end of file diff --git a/LICENSE b/LICENSE index ba1ba1c..329cd87 100644 --- a/LICENSE +++ b/LICENSE @@ -1,25 +1,25 @@ -Copyright (c) 2007 Ryan Grove -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of this project nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2007 Ryan Grove +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of this project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README b/README index 278656c..5a798c4 100644 --- a/README +++ b/README @@ -1,46 +1,46 @@ -WELCOME TO MINIFY 2.0! - -Minify is an HTTP content server. It compresses sources of content -(usually files), combines the result and serves it with appropriate -HTTP headers. These headers can allow clients to perform conditional -GETs (serving content only when clients do not have a valid cache) -or tell clients to cache the file until a given date/time. -More info: http://code.google.com/p/minify/ - -INSTALLATION AND USAGE: http://code.google.com/p/minify/wiki/UserGuide - -UNIT TESTING & EXAMPLES: http://code.google.com/p/minify/wiki/TestingMinify - -MINIFY OVERVIEW - -Minify works with Minify_Source objects. These usually represent a file -on disk, but a source can also be a string in memory or from a database. -Sources with known "last modified" timestamps allow Minify to implement -server-side caching and conditional GETs. - -You configure Minify via a Minify_Controller object. The controller -supplies the sources and default options to serve a request, -determining how it's to be responded to. Several controllers are -supplied with Minify, but it's also fairly easy to write your own. See -the files in /lib/Minify/Controller for examples. - -To use an existing controller, you call Minify::serve(), passing it: -1. the name of your controller of choice (without the "Minify_Controller" - prefix) or a custom controller object subclassed from Minify_Controller_Base. -2. a combined array of controller and Minify options. Eg.: - -// serve a minified javascript file and tell clients to cache it for a -// year -Minify::serve('Files', array( - 'files' => array('/path/to/file1.js', '/path/to/file2.js') - ,'setExpires' => (time() + 86400 * 365) -)); - -The above creates an instance of Minify_Controller_Files, which creates -source objects from the 'files' option, and supplies other default options. - -FILE ENCODINGS - -Minify *should* work fine with files encoded in UTF-8 or other 8-bit -encodings like ISO 8859/Windows-1252. Leading UTF-8 BOMs are stripped from -all sources to prevent duplication in output files. +WELCOME TO MINIFY 2.0! + +Minify is an HTTP content server. It compresses sources of content +(usually files), combines the result and serves it with appropriate +HTTP headers. These headers can allow clients to perform conditional +GETs (serving content only when clients do not have a valid cache) +or tell clients to cache the file until a given date/time. +More info: http://code.google.com/p/minify/ + +INSTALLATION AND USAGE: http://code.google.com/p/minify/wiki/UserGuide + +UNIT TESTING & EXAMPLES: http://code.google.com/p/minify/wiki/TestingMinify + +MINIFY OVERVIEW + +Minify works with Minify_Source objects. These usually represent a file +on disk, but a source can also be a string in memory or from a database. +Sources with known "last modified" timestamps allow Minify to implement +server-side caching and conditional GETs. + +You configure Minify via a Minify_Controller object. The controller +supplies the sources and default options to serve a request, +determining how it's to be responded to. Several controllers are +supplied with Minify, but it's also fairly easy to write your own. See +the files in /lib/Minify/Controller for examples. + +To use an existing controller, you call Minify::serve(), passing it: +1. the name of your controller of choice (without the "Minify_Controller" + prefix) or a custom controller object subclassed from Minify_Controller_Base. +2. a combined array of controller and Minify options. Eg.: + +// serve a minified javascript file and tell clients to cache it for a +// year +Minify::serve('Files', array( + 'files' => array('/path/to/file1.js', '/path/to/file2.js') + ,'setExpires' => (time() + 86400 * 365) +)); + +The above creates an instance of Minify_Controller_Files, which creates +source objects from the 'files' option, and supplies other default options. + +FILE ENCODINGS + +Minify *should* work fine with files encoded in UTF-8 or other 8-bit +encodings like ISO 8859/Windows-1252. Leading UTF-8 BOMs are stripped from +all sources to prevent duplication in output files. diff --git a/min/config.php b/min/config.php index 7014db4..7922290 100644 --- a/min/config.php +++ b/min/config.php @@ -1,16 +1,16 @@ - array('//js/file1.js', '//js/file2.js'), - // 'css' => array('//css/file1.css', '//css/file2.css'), - 'js' => array('//js/Class.js', '//js/email.js') + array('//js/file1.js', '//js/file2.js'), + // 'css' => array('//css/file1.css', '//css/file2.css'), + 'js' => array('//js/Class.js', '//js/email.js') ); \ No newline at end of file diff --git a/min/index.php b/min/index.php index 9da41de..327ed2f 100644 --- a/min/index.php +++ b/min/index.php @@ -1,55 +1,55 @@ - - * list($updateTime, $content) = getDbUpdateAndContent(); - * $cg = new HTTP_ConditionalGet(array( - * 'lastModifiedTime' => $updateTime - * )); - * $cg->sendHeaders(); - * if ($cg->cacheIsValid) { - * exit(); - * } - * echo $content; - * - * - * E.g. Content from DB with no update time: - * - * $content = getContentFromDB(); - * $cg = new HTTP_ConditionalGet(array( - * 'contentHash' => md5($content) - * )); - * $cg->sendHeaders(); - * if ($cg->cacheIsValid) { - * exit(); - * } - * echo $content; - * - * - * E.g. Static content with some static includes: - * - * // before content - * $cg = new HTTP_ConditionalGet(array( - * 'lastUpdateTime' => max( - * filemtime(__FILE__) - * ,filemtime('/path/to/header.inc') - * ,filemtime('/path/to/footer.inc') - * ) - * )); - * $cg->sendHeaders(); - * if ($cg->cacheIsValid) { - * exit(); - * } - * - * @package Minify - * @subpackage HTTP - * @author Stephen Clay - */ -class HTTP_ConditionalGet { - - /** - * Does the client have a valid copy of the requested resource? - * - * You'll want to check this after instantiating the object. If true, do - * not send content, just call sendHeaders() if you haven't already. - * - * @var bool - */ - public $cacheIsValid = null; - - /** - * @param array $spec options - * - * 'isPublic': (bool) if true, the Cache-Control header will contain - * "public", allowing proxy caches to cache the content. Otherwise - * "private" will be sent, allowing only browsers to cache. (default false) - * - * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers - * will be sent with content. This is recommended. - * - * 'contentHash': (string) if given, only the ETag header can be sent with - * content (only HTTP1.1 clients can conditionally GET). The given string - * should be short with no quote characters and always change when the - * resource changes (recommend md5()). This is not needed/used if - * lastModifiedTime is given. - * - * 'invalidate': (bool) if true, the client cache will be considered invalid - * without testing. Effectively this disables conditional GET. - * (default false) - * - * 'maxAge': (int) if given, this will set the Cache-Control max-age in - * seconds, and also set the Expires header to the equivalent GMT date. - * After the max-age period has passed, the browser will again send a - * conditional GET to revalidate its cache. - * - * @return null - */ - public function __construct($spec) { - $scope = (isset($spec['isPublic']) && $spec['isPublic']) - ? 'public' - : 'private'; - $maxAge = 0; - // backwards compatibility (can be removed later) - if (isset($spec['setExpires']) - && is_numeric($spec['setExpires']) - && ! isset($spec['maxAge'])) { - $spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME']; - } - if (isset($spec['maxAge'])) { - $maxAge = $spec['maxAge']; - $this->_headers['Expires'] = self::gmtDate( - $_SERVER['REQUEST_TIME'] + $spec['maxAge'] - ); - } - if (isset($spec['lastModifiedTime'])) { - // base both headers on time - $this->_setLastModified($spec['lastModifiedTime']); - $this->_setEtag($spec['lastModifiedTime'], $scope); - } else { - // hope to use ETag - if (isset($spec['contentHash'])) { - $this->_setEtag($spec['contentHash'], $scope); - } - } - $this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}, must-revalidate"; - // invalidate cache if disabled, otherwise check - $this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate']) - ? false - : $this->_isCacheValid(); - } - - /** - * Get array of output headers to be sent - * - * In the case of 304 responses, this array will only contain the response - * code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified') - * - * Otherwise something like: - * - * array( - * 'Cache-Control' => 'max-age=0, public, must-revalidate' - * ,'ETag' => '"foobar"' - * ) - * - * - * @return array - */ - public function getHeaders() { - return $this->_headers; - } - - /** - * Set the Content-Length header in bytes - * - * With most PHP configs, as long as you don't flush() output, this method - * is not needed and PHP will buffer all output and set Content-Length for - * you. Otherwise you'll want to call this to let the client know up front. - * - * @param int $bytes - * - * @return int copy of input $bytes - */ - public function setContentLength($bytes) { - return $this->_headers['Content-Length'] = $bytes; - } - - /** - * Send headers - * - * @see getHeaders() - * - * Note this doesn't "clear" the headers. Calling sendHeaders() will - * call header() again (but probably have not effect) and getHeaders() will - * still return the headers. - * - * @return null - */ - public function sendHeaders() { - $headers = $this->_headers; - if (array_key_exists('_responseCode', $headers)) { - header($headers['_responseCode']); - unset($headers['_responseCode']); - } - foreach ($headers as $name => $val) { - header($name . ': ' . $val); - } - } - - /** - * Get a GMT formatted date for use in HTTP headers - * - * - * header('Expires: ' . HTTP_ConditionalGet::gmtdate($time)); - * - * - * @param int $time unix timestamp - * - * @return string - */ - public static function gmtDate($time) { - return gmdate('D, d M Y H:i:s \G\M\T', $time); - } - - protected $_headers = array(); - protected $_lmTime = null; - protected $_etag = null; - - protected function _setEtag($hash, $scope) { - $this->_etag = '"' . $hash - . substr($scope, 0, 3) - . '"'; - $this->_headers['ETag'] = $this->_etag; - } - - protected function _setLastModified($time) { - $this->_lmTime = (int)$time; - $this->_headers['Last-Modified'] = self::gmtDate($time); - } - - /** - * Determine validity of client cache and queue 304 header if valid - */ - protected function _isCacheValid() - { - if (null === $this->_etag) { - // lmTime is copied to ETag, so this condition implies that the - // client received neither ETag nor Last-Modified, so can't - // possibly has a valid cache. - return false; - } - $isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified()); - if ($isValid) { - $this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified'; - } - return $isValid; - } - - protected function resourceMatchedEtag() { - if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) { - return false; - } - $cachedEtagList = get_magic_quotes_gpc() - ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) - : $_SERVER['HTTP_IF_NONE_MATCH']; - $cachedEtags = split(',', $cachedEtagList); - foreach ($cachedEtags as $cachedEtag) { - if (trim($cachedEtag) == $this->_etag) { - return true; - } - } - return false; - } - - protected function resourceNotModified() { - if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { - return false; - } - $ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; - if (false !== ($semicolon = strrpos($ifModifiedSince, ';'))) { - // IE has tacked on extra data to this header, strip it - $ifModifiedSince = substr($ifModifiedSince, 0, $semicolon); - } - return ($ifModifiedSince == self::gmtDate($this->_lmTime)); - } -} + + * list($updateTime, $content) = getDbUpdateAndContent(); + * $cg = new HTTP_ConditionalGet(array( + * 'lastModifiedTime' => $updateTime + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * echo $content; + * + * + * E.g. Content from DB with no update time: + * + * $content = getContentFromDB(); + * $cg = new HTTP_ConditionalGet(array( + * 'contentHash' => md5($content) + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * echo $content; + * + * + * E.g. Static content with some static includes: + * + * // before content + * $cg = new HTTP_ConditionalGet(array( + * 'lastUpdateTime' => max( + * filemtime(__FILE__) + * ,filemtime('/path/to/header.inc') + * ,filemtime('/path/to/footer.inc') + * ) + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * + * @package Minify + * @subpackage HTTP + * @author Stephen Clay + */ +class HTTP_ConditionalGet { + + /** + * Does the client have a valid copy of the requested resource? + * + * You'll want to check this after instantiating the object. If true, do + * not send content, just call sendHeaders() if you haven't already. + * + * @var bool + */ + public $cacheIsValid = null; + + /** + * @param array $spec options + * + * 'isPublic': (bool) if true, the Cache-Control header will contain + * "public", allowing proxy caches to cache the content. Otherwise + * "private" will be sent, allowing only browsers to cache. (default false) + * + * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers + * will be sent with content. This is recommended. + * + * 'contentHash': (string) if given, only the ETag header can be sent with + * content (only HTTP1.1 clients can conditionally GET). The given string + * should be short with no quote characters and always change when the + * resource changes (recommend md5()). This is not needed/used if + * lastModifiedTime is given. + * + * 'invalidate': (bool) if true, the client cache will be considered invalid + * without testing. Effectively this disables conditional GET. + * (default false) + * + * 'maxAge': (int) if given, this will set the Cache-Control max-age in + * seconds, and also set the Expires header to the equivalent GMT date. + * After the max-age period has passed, the browser will again send a + * conditional GET to revalidate its cache. + * + * @return null + */ + public function __construct($spec) { + $scope = (isset($spec['isPublic']) && $spec['isPublic']) + ? 'public' + : 'private'; + $maxAge = 0; + // backwards compatibility (can be removed later) + if (isset($spec['setExpires']) + && is_numeric($spec['setExpires']) + && ! isset($spec['maxAge'])) { + $spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME']; + } + if (isset($spec['maxAge'])) { + $maxAge = $spec['maxAge']; + $this->_headers['Expires'] = self::gmtDate( + $_SERVER['REQUEST_TIME'] + $spec['maxAge'] + ); + } + if (isset($spec['lastModifiedTime'])) { + // base both headers on time + $this->_setLastModified($spec['lastModifiedTime']); + $this->_setEtag($spec['lastModifiedTime'], $scope); + } else { + // hope to use ETag + if (isset($spec['contentHash'])) { + $this->_setEtag($spec['contentHash'], $scope); + } + } + $this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}, must-revalidate"; + // invalidate cache if disabled, otherwise check + $this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate']) + ? false + : $this->_isCacheValid(); + } + + /** + * Get array of output headers to be sent + * + * In the case of 304 responses, this array will only contain the response + * code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified') + * + * Otherwise something like: + * + * array( + * 'Cache-Control' => 'max-age=0, public, must-revalidate' + * ,'ETag' => '"foobar"' + * ) + * + * + * @return array + */ + public function getHeaders() { + return $this->_headers; + } + + /** + * Set the Content-Length header in bytes + * + * With most PHP configs, as long as you don't flush() output, this method + * is not needed and PHP will buffer all output and set Content-Length for + * you. Otherwise you'll want to call this to let the client know up front. + * + * @param int $bytes + * + * @return int copy of input $bytes + */ + public function setContentLength($bytes) { + return $this->_headers['Content-Length'] = $bytes; + } + + /** + * Send headers + * + * @see getHeaders() + * + * Note this doesn't "clear" the headers. Calling sendHeaders() will + * call header() again (but probably have not effect) and getHeaders() will + * still return the headers. + * + * @return null + */ + public function sendHeaders() { + $headers = $this->_headers; + if (array_key_exists('_responseCode', $headers)) { + header($headers['_responseCode']); + unset($headers['_responseCode']); + } + foreach ($headers as $name => $val) { + header($name . ': ' . $val); + } + } + + /** + * Get a GMT formatted date for use in HTTP headers + * + * + * header('Expires: ' . HTTP_ConditionalGet::gmtdate($time)); + * + * + * @param int $time unix timestamp + * + * @return string + */ + public static function gmtDate($time) { + return gmdate('D, d M Y H:i:s \G\M\T', $time); + } + + protected $_headers = array(); + protected $_lmTime = null; + protected $_etag = null; + + protected function _setEtag($hash, $scope) { + $this->_etag = '"' . $hash + . substr($scope, 0, 3) + . '"'; + $this->_headers['ETag'] = $this->_etag; + } + + protected function _setLastModified($time) { + $this->_lmTime = (int)$time; + $this->_headers['Last-Modified'] = self::gmtDate($time); + } + + /** + * Determine validity of client cache and queue 304 header if valid + */ + protected function _isCacheValid() + { + if (null === $this->_etag) { + // lmTime is copied to ETag, so this condition implies that the + // client received neither ETag nor Last-Modified, so can't + // possibly has a valid cache. + return false; + } + $isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified()); + if ($isValid) { + $this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified'; + } + return $isValid; + } + + protected function resourceMatchedEtag() { + if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + return false; + } + $cachedEtagList = get_magic_quotes_gpc() + ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) + : $_SERVER['HTTP_IF_NONE_MATCH']; + $cachedEtags = split(',', $cachedEtagList); + foreach ($cachedEtags as $cachedEtag) { + if (trim($cachedEtag) == $this->_etag) { + return true; + } + } + return false; + } + + protected function resourceNotModified() { + if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + return false; + } + $ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + if (false !== ($semicolon = strrpos($ifModifiedSince, ';'))) { + // IE has tacked on extra data to this header, strip it + $ifModifiedSince = substr($ifModifiedSince, 0, $semicolon); + } + return ($ifModifiedSince == self::gmtDate($this->_lmTime)); + } +} diff --git a/min/lib/JSMin.php b/min/lib/JSMin.php index 2f3bcc7..e06129c 100644 --- a/min/lib/JSMin.php +++ b/min/lib/JSMin.php @@ -1,291 +1,291 @@ - - * @copyright 2002 Douglas Crockford (jsmin.c) - * @copyright 2008 Ryan Grove (PHP port) - * @license http://opensource.org/licenses/mit-license.php MIT License - * @version 1.1.1 (2008-03-02) - * @link http://code.google.com/p/jsmin-php/ - */ - -class JSMin { - const ORD_LF = 10; - const ORD_SPACE = 32; - - protected $a = ''; - protected $b = ''; - protected $input = ''; - protected $inputIndex = 0; - protected $inputLength = 0; - protected $lookAhead = null; - protected $output = ''; - - // -- Public Static Methods -------------------------------------------------- - - public static function minify($js) { - $jsmin = new JSMin($js); - return $jsmin->min(); - } - - // -- Public Instance Methods ------------------------------------------------ - - public function __construct($input) { - $this->input = str_replace("\r\n", "\n", $input); - $this->inputLength = strlen($this->input); - } - - // -- Protected Instance Methods --------------------------------------------- - - protected function action($d) { - switch($d) { - case 1: - $this->output .= $this->a; - - case 2: - $this->a = $this->b; - - if ($this->a === "'" || $this->a === '"') { - for (;;) { - $this->output .= $this->a; - $this->a = $this->get(); - - if ($this->a === $this->b) { - break; - } - - if (ord($this->a) <= self::ORD_LF) { - throw new JSMinException('Unterminated string literal.'); - } - - if ($this->a === '\\') { - $this->output .= $this->a; - $this->a = $this->get(); - } - } - } - - case 3: - $this->b = $this->next(); - - if ($this->b === '/' && ( - $this->a === '(' || $this->a === ',' || $this->a === '=' || - $this->a === ':' || $this->a === '[' || $this->a === '!' || - $this->a === '&' || $this->a === '|' || $this->a === '?')) { - - $this->output .= $this->a . $this->b; - - for (;;) { - $this->a = $this->get(); - - if ($this->a === '/') { - break; - } elseif ($this->a === '\\') { - $this->output .= $this->a; - $this->a = $this->get(); - } elseif (ord($this->a) <= self::ORD_LF) { - throw new JSMinException('Unterminated regular expression '. - 'literal.'); - } - - $this->output .= $this->a; - } - - $this->b = $this->next(); - } - } - } - - protected function get() { - $c = $this->lookAhead; - $this->lookAhead = null; - - if ($c === null) { - if ($this->inputIndex < $this->inputLength) { - $c = $this->input[$this->inputIndex]; - $this->inputIndex += 1; - } else { - $c = null; - } - } - - if ($c === "\r") { - return "\n"; - } - - if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { - return $c; - } - - return ' '; - } - - protected function isAlphaNum($c) { - return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; - } - - protected function min() { - $this->a = "\n"; - $this->action(3); - - while ($this->a !== null) { - switch ($this->a) { - case ' ': - if ($this->isAlphaNum($this->b)) { - $this->action(1); - } else { - $this->action(2); - } - break; - - case "\n": - switch ($this->b) { - case '{': - case '[': - case '(': - case '+': - case '-': - $this->action(1); - break; - - case ' ': - $this->action(3); - break; - - default: - if ($this->isAlphaNum($this->b)) { - $this->action(1); - } - else { - $this->action(2); - } - } - break; - - default: - switch ($this->b) { - case ' ': - if ($this->isAlphaNum($this->a)) { - $this->action(1); - break; - } - - $this->action(3); - break; - - case "\n": - switch ($this->a) { - case '}': - case ']': - case ')': - case '+': - case '-': - case '"': - case "'": - $this->action(1); - break; - - default: - if ($this->isAlphaNum($this->a)) { - $this->action(1); - } - else { - $this->action(3); - } - } - break; - - default: - $this->action(1); - break; - } - } - } - - return $this->output; - } - - protected function next() { - $c = $this->get(); - - if ($c === '/') { - switch($this->peek()) { - case '/': - for (;;) { - $c = $this->get(); - - if (ord($c) <= self::ORD_LF) { - return $c; - } - } - - case '*': - $this->get(); - - for (;;) { - switch($this->get()) { - case '*': - if ($this->peek() === '/') { - $this->get(); - return ' '; - } - break; - - case null: - throw new JSMinException('Unterminated comment.'); - } - } - - default: - return $c; - } - } - - return $c; - } - - protected function peek() { - $this->lookAhead = $this->get(); - return $this->lookAhead; - } -} - -// -- Exceptions --------------------------------------------------------------- -class JSMinException extends Exception {} + + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (PHP port) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 1.1.1 (2008-03-02) + * @link http://code.google.com/p/jsmin-php/ + */ + +class JSMin { + const ORD_LF = 10; + const ORD_SPACE = 32; + + protected $a = ''; + protected $b = ''; + protected $input = ''; + protected $inputIndex = 0; + protected $inputLength = 0; + protected $lookAhead = null; + protected $output = ''; + + // -- Public Static Methods -------------------------------------------------- + + public static function minify($js) { + $jsmin = new JSMin($js); + return $jsmin->min(); + } + + // -- Public Instance Methods ------------------------------------------------ + + public function __construct($input) { + $this->input = str_replace("\r\n", "\n", $input); + $this->inputLength = strlen($this->input); + } + + // -- Protected Instance Methods --------------------------------------------- + + protected function action($d) { + switch($d) { + case 1: + $this->output .= $this->a; + + case 2: + $this->a = $this->b; + + if ($this->a === "'" || $this->a === '"') { + for (;;) { + $this->output .= $this->a; + $this->a = $this->get(); + + if ($this->a === $this->b) { + break; + } + + if (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated string literal.'); + } + + if ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } + } + } + + case 3: + $this->b = $this->next(); + + if ($this->b === '/' && ( + $this->a === '(' || $this->a === ',' || $this->a === '=' || + $this->a === ':' || $this->a === '[' || $this->a === '!' || + $this->a === '&' || $this->a === '|' || $this->a === '?')) { + + $this->output .= $this->a . $this->b; + + for (;;) { + $this->a = $this->get(); + + if ($this->a === '/') { + break; + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated regular expression '. + 'literal.'); + } + + $this->output .= $this->a; + } + + $this->b = $this->next(); + } + } + } + + protected function get() { + $c = $this->lookAhead; + $this->lookAhead = null; + + if ($c === null) { + if ($this->inputIndex < $this->inputLength) { + $c = $this->input[$this->inputIndex]; + $this->inputIndex += 1; + } else { + $c = null; + } + } + + if ($c === "\r") { + return "\n"; + } + + if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { + return $c; + } + + return ' '; + } + + protected function isAlphaNum($c) { + return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; + } + + protected function min() { + $this->a = "\n"; + $this->action(3); + + while ($this->a !== null) { + switch ($this->a) { + case ' ': + if ($this->isAlphaNum($this->b)) { + $this->action(1); + } else { + $this->action(2); + } + break; + + case "\n": + switch ($this->b) { + case '{': + case '[': + case '(': + case '+': + case '-': + $this->action(1); + break; + + case ' ': + $this->action(3); + break; + + default: + if ($this->isAlphaNum($this->b)) { + $this->action(1); + } + else { + $this->action(2); + } + } + break; + + default: + switch ($this->b) { + case ' ': + if ($this->isAlphaNum($this->a)) { + $this->action(1); + break; + } + + $this->action(3); + break; + + case "\n": + switch ($this->a) { + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case "'": + $this->action(1); + break; + + default: + if ($this->isAlphaNum($this->a)) { + $this->action(1); + } + else { + $this->action(3); + } + } + break; + + default: + $this->action(1); + break; + } + } + } + + return $this->output; + } + + protected function next() { + $c = $this->get(); + + if ($c === '/') { + switch($this->peek()) { + case '/': + for (;;) { + $c = $this->get(); + + if (ord($c) <= self::ORD_LF) { + return $c; + } + } + + case '*': + $this->get(); + + for (;;) { + switch($this->get()) { + case '*': + if ($this->peek() === '/') { + $this->get(); + return ' '; + } + break; + + case null: + throw new JSMinException('Unterminated comment.'); + } + } + + default: + return $c; + } + } + + return $c; + } + + protected function peek() { + $this->lookAhead = $this->get(); + return $this->lookAhead; + } +} + +// -- Exceptions --------------------------------------------------------------- +class JSMinException extends Exception {} ?> \ No newline at end of file diff --git a/min/lib/Minify.php b/min/lib/Minify.php index e1c8124..3ba5d9d 100644 --- a/min/lib/Minify.php +++ b/min/lib/Minify.php @@ -1,243 +1,243 @@ - - * @author Stephen Clay - * @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved. - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @link http://code.google.com/p/minify/ - */ -class Minify { - - const TYPE_CSS = 'text/css'; - const TYPE_HTML = 'text/html'; - // there is some debate over the ideal JS Content-Type, but this is the - // Apache default and what Yahoo! uses.. - const TYPE_JS = 'application/x-javascript'; - - /** - * How many hours behind are the file modification times of uploaded files? - * + + * @author Stephen Clay + * @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved. + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://code.google.com/p/minify/ + */ +class Minify { + + const TYPE_CSS = 'text/css'; + const TYPE_HTML = 'text/html'; + // there is some debate over the ideal JS Content-Type, but this is the + // Apache default and what Yahoo! uses.. + const TYPE_JS = 'application/x-javascript'; + + /** + * How many hours behind are the file modification times of uploaded files? + * * If you upload files from Windows to a non-Windows server, Windows may report * incorrect mtimes for the files. Immediately after modifying and uploading a * file, use the touch command to update the mtime on the server. If the mtime * jumps ahead by a number of hours, set this variable to that number. If the mtime - * moves back, this should not be needed. - * - * @var int $uploaderHoursBehind - */ - public static $uploaderHoursBehind = 0; - - /** - * @see setCache() - * @param mixed $cache object with identical interface as Minify_Cache_File or - * a directory path. (default = '') - * @return null - * @deprecated - */ - public static function useServerCache($path = '') - { - self::setCache($path); - } - - /** - * Specify a cache object (with identical interface as Minify_Cache_File) or - * a path to use with Minify_Cache_File. - * - * If not called, Minify will not use a cache and, for each 200 response, will - * need to recombine files, minify and encode the output. - * - * @param mixed $cache object with identical interface as Minify_Cache_File or - * a directory path. (default = '') - * - * @return null - */ - public static function setCache($cache = '') - { - if (is_string($cache)) { - require_once 'Minify/Cache/File.php'; - self::$_cache = new Minify_Cache_File($cache); - } else { - self::$_cache = $cache; - } - } - - private static $_cache = null; - - /** - * Serve a request for a minified file. - * - * 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 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', or '' (no encoding) - * - * 'encodeLevel' : level of encoding compression (0 to 9, default 9) - * - * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey - * value to remove. (default 'UTF-8') - * - * 'maxAge' : set this to the number of seconds the client should use its cache - * before revalidating with the server. This sets Cache-Control: max-age and the - * Expires header. Unlike the old 'setExpires' setting, this setting will NOT - * prevent conditional GETs. Note this has nothing to do with server-side caching. + * moves back, this should not be needed. + * + * @var int $uploaderHoursBehind + */ + public static $uploaderHoursBehind = 0; + + /** + * @see setCache() + * @param mixed $cache object with identical interface as Minify_Cache_File or + * a directory path. (default = '') + * @return null + * @deprecated + */ + public static function useServerCache($path = '') + { + self::setCache($path); + } + + /** + * Specify a cache object (with identical interface as Minify_Cache_File) or + * a path to use with Minify_Cache_File. + * + * If not called, Minify will not use a cache and, for each 200 response, will + * need to recombine files, minify and encode the output. + * + * @param mixed $cache object with identical interface as Minify_Cache_File or + * a directory path. (default = '') + * + * @return null + */ + public static function setCache($cache = '') + { + if (is_string($cache)) { + require_once 'Minify/Cache/File.php'; + self::$_cache = new Minify_Cache_File($cache); + } else { + self::$_cache = $cache; + } + } + + private static $_cache = null; + + /** + * Serve a request for a minified file. + * + * 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 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', or '' (no encoding) + * + * 'encodeLevel' : level of encoding compression (0 to 9, default 9) + * + * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey + * value to remove. (default 'UTF-8') + * + * 'maxAge' : set this to the number of seconds the client should use its cache + * before revalidating with the server. This sets Cache-Control: max-age and the + * Expires header. Unlike the old 'setExpires' setting, this setting will NOT + * prevent conditional GETs. Note this has nothing to do with server-side caching. * * 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir' * minifier option to enable URI rewriting in CSS files (default true) - * - * 'debug' : set to true to minify all sources with the 'Lines' controller, which - * eases the debugging of combined files. This also prevents 304 responses. - * @see Minify_Lines::minify() - * - * 'minifiers' : to override Minify's default choice of minifier function for - * a particular content-type, specify your callback under the key of the - * content-type: - * - * // call customCssMinifier($css) for all CSS minification - * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier'; - * - * // don't minify Javascript at all - * $options['minifiers'][Minify::TYPE_JS] = ''; - * - * - * 'minifierOptions' : to send options to the minifier function, specify your options - * under the key of the content-type. E.g. To send the CSS minifier an option: - * - * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument - * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue'; - * - * - * 'contentType' : (optional) this is only needed if your file extension is not - * js/css/html. The given content-type will be sent regardless of source file - * extension, so this should not be used in a Groups config with other - * Javascript/CSS files. - * - * Any controller options are documented in that controller's setupSources() method. - * - * @param mixed instance of subclass of Minify_Controller_Base or string name of - * controller. E.g. 'Files' - * - * @param array $options controller/serve options - * - * @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). - */ - public static function serve($controller, $options = array()) { - if (is_string($controller)) { - // make $controller into object - $class = 'Minify_Controller_' . $controller; - if (! class_exists($class, false)) { - require_once "Minify/Controller/" - . str_replace('_', '/', $controller) . ".php"; - } - $controller = new $class(); - } - - // set up controller sources and mix remaining options with - // controller defaults - $options = $controller->setupSources($options); - $options = $controller->analyzeSources($options); - self::$_options = $controller->mixInDefaultOptions($options); - - // check request validity - if (! $controller->sources) { - // invalid request! - 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() - ); - } - } - - self::$_controller = $controller; - - if (self::$_options['debug']) { - self::_setupDebug($controller->sources); - self::$_options['maxAge'] = 0; - } - - // check client cache - require_once 'HTTP/ConditionalGet.php'; - $cgOptions = array( - 'lastModifiedTime' => self::$_options['lastModifiedTime'] - ,'isPublic' => self::$_options['isPublic'] - ); - if (self::$_options['maxAge'] > 0) { - $cgOptions['maxAge'] = self::$_options['maxAge']; - } - $cg = new HTTP_ConditionalGet($cgOptions); - if ($cg->cacheIsValid) { - // client's cache is valid - if (! self::$_options['quiet']) { - $cg->sendHeaders(); - return; - } else { - return array( - 'success' => true - ,'statusCode' => 304 - ,'content' => '' - ,'headers' => $cg->getHeaders() - ); - } - } else { - // client will need output - $headers = $cg->getHeaders(); - unset($cg); - } - - // determine encoding - if (self::$_options['encodeOutput']) { - if (self::$_options['encodeMethod'] !== null) { - // controller specifically requested this - $contentEncoding = self::$_options['encodeMethod']; - } else { - // 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'. 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) + * + * 'debug' : set to true to minify all sources with the 'Lines' controller, which + * eases the debugging of combined files. This also prevents 304 responses. + * @see Minify_Lines::minify() + * + * 'minifiers' : to override Minify's default choice of minifier function for + * a particular content-type, specify your callback under the key of the + * content-type: + * + * // call customCssMinifier($css) for all CSS minification + * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier'; + * + * // don't minify Javascript at all + * $options['minifiers'][Minify::TYPE_JS] = ''; + * + * + * 'minifierOptions' : to send options to the minifier function, specify your options + * under the key of the content-type. E.g. To send the CSS minifier an option: + * + * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument + * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue'; + * + * + * 'contentType' : (optional) this is only needed if your file extension is not + * js/css/html. The given content-type will be sent regardless of source file + * extension, so this should not be used in a Groups config with other + * Javascript/CSS files. + * + * Any controller options are documented in that controller's setupSources() method. + * + * @param mixed instance of subclass of Minify_Controller_Base or string name of + * controller. E.g. 'Files' + * + * @param array $options controller/serve options + * + * @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). + */ + public static function serve($controller, $options = array()) { + if (is_string($controller)) { + // make $controller into object + $class = 'Minify_Controller_' . $controller; + if (! class_exists($class, false)) { + require_once "Minify/Controller/" + . str_replace('_', '/', $controller) . ".php"; + } + $controller = new $class(); + } + + // set up controller sources and mix remaining options with + // controller defaults + $options = $controller->setupSources($options); + $options = $controller->analyzeSources($options); + self::$_options = $controller->mixInDefaultOptions($options); + + // check request validity + if (! $controller->sources) { + // invalid request! + 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() + ); + } + } + + self::$_controller = $controller; + + if (self::$_options['debug']) { + self::_setupDebug($controller->sources); + self::$_options['maxAge'] = 0; + } + + // check client cache + require_once 'HTTP/ConditionalGet.php'; + $cgOptions = array( + 'lastModifiedTime' => self::$_options['lastModifiedTime'] + ,'isPublic' => self::$_options['isPublic'] + ); + if (self::$_options['maxAge'] > 0) { + $cgOptions['maxAge'] = self::$_options['maxAge']; + } + $cg = new HTTP_ConditionalGet($cgOptions); + if ($cg->cacheIsValid) { + // client's cache is valid + if (! self::$_options['quiet']) { + $cg->sendHeaders(); + return; + } else { + return array( + 'success' => true + ,'statusCode' => 304 + ,'content' => '' + ,'headers' => $cg->getHeaders() + ); + } + } else { + // client will need output + $headers = $cg->getHeaders(); + unset($cg); + } + + // determine encoding + if (self::$_options['encodeOutput']) { + if (self::$_options['encodeMethod'] !== null) { + // controller specifically requested this + $contentEncoding = self::$_options['encodeMethod']; + } else { + // 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'. 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 (self::$_options['contentType'] === self::TYPE_CSS @@ -252,184 +252,184 @@ class Minify { } } } - - // check server cache - if (null !== self::$_cache) { - // 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 = 'minify_' . self::_getCacheId(); - $encodingExtension = self::$_options['encodeMethod'] - ? ('deflate' === self::$_options['encodeMethod'] - ? '.zd' - : '.zg') - : ''; - $fullCacheId = $cacheId . $encodingExtension; - // check cache for valid entry - $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']); - if ($cacheIsReady) { - $cacheContentLength = self::$_cache->getSize($fullCacheId); - } else { - // generate & cache content - $content = self::_combineMinify(); - self::$_cache->store($cacheId, $content); - self::$_cache->store($cacheId . '.zd', gzdeflate($content, self::$_options['encodeLevel'])); - self::$_cache->store($cacheId . '.zg', gzencode($content, self::$_options['encodeLevel'])); - } - } else { - // no cache - $cacheIsReady = false; - $content = self::_combineMinify(); - } - 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'] = self::$_options['contentTypeCharset'] - ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset'] - : self::$_options['contentType']; - if (self::$_options['encodeMethod'] !== '') { - $headers['Content-Encoding'] = $contentEncoding; - $headers['Vary'] = 'Accept-Encoding'; - } - - if (! self::$_options['quiet']) { - // output headers & content - foreach ($headers as $name => $val) { - header($name . ': ' . $val); - } - if ($cacheIsReady) { - self::$_cache->display($fullCacheId); - } else { - echo $content; - } - } else { - return array( - 'success' => true - ,'statusCode' => 200 - ,'content' => $cacheIsReady - ? self::$_cache->fetch($fullCacheId) - : $content - ,'headers' => $headers - ); - } - } - - /** - * @var Minify_Controller active controller for current request - */ - protected static $_controller = null; - - /** - * @var array options for current request - */ - protected static $_options = null; - - /** - * Set up sources to use Minify_Lines - * - * @param array $sources Minify_Source instances - * - * @return null - */ - protected static function _setupDebug($sources) - { - foreach ($sources as $source) { - $source->minifier = array('Minify_Lines', 'minify'); - $id = $source->getId(); - $source->minifyOptions = array( - 'id' => (is_file($id) ? basename($id) : $id) - ); - } - } - - /** - * Combines sources and minifies the result. - * - * @return string - */ - protected static function _combineMinify() { - $type = self::$_options['contentType']; // ease readability - - // when combining scripts, make sure all statements separated - $implodeSeparator = ($type === self::TYPE_JS) - ? ';' - : ''; - // allow the user to pass a particular array of options to each - // minifier (designated by type). source objects may still override - // these - $defaultOptions = isset(self::$_options['minifierOptions'][$type]) - ? self::$_options['minifierOptions'][$type] - : array(); - // if minifier not set, default is no minification. source objects - // may still override this - $defaultMinifier = isset(self::$_options['minifiers'][$type]) - ? self::$_options['minifiers'][$type] - : false; - - if (Minify_Source::haveNoMinifyPrefs(self::$_controller->sources)) { - // all source have same options/minifier, better performance - // to combine, then minify once - foreach (self::$_controller->sources as $source) { - $pieces[] = $source->getContent(); - } - $content = implode($implodeSeparator, $pieces); - if ($defaultMinifier) { - self::$_controller->loadMinifier($defaultMinifier); - $content = call_user_func($defaultMinifier, $content, $defaultOptions); - } - } else { - // minify each source with its own options and minifier, then combine - foreach (self::$_controller->sources as $source) { - // allow the source to override our minifier and options - $minifier = (null !== $source->minifier) - ? $source->minifier - : $defaultMinifier; - $options = (null !== $source->minifyOptions) - ? array_merge($defaultOptions, $source->minifyOptions) - : $defaultOptions; - if ($defaultMinifier) { - self::$_controller->loadMinifier($minifier); - // get source content and minify it - $pieces[] = call_user_func($minifier, $source->getContent(), $options); - } else { - $pieces[] = $source->getContent(); - } - } - $content = implode($implodeSeparator, $pieces); - } - - // do any post-processing (esp. for editing build URIs) - if (self::$_options['postprocessorRequire']) { - require_once self::$_options['postprocessorRequire']; - } - if (self::$_options['postprocessor']) { - $content = call_user_func(self::$_options['postprocessor'], $content, $type); - } - return $content; - } - - /** - * Make a unique cache id for for this request. - * - * Any settings that could affect output are taken into consideration - * - * @return string - */ - protected static function _getCacheId() { - return md5(serialize(array( - Minify_Source::getDigest(self::$_controller->sources) - ,self::$_options['minifiers'] + + // check server cache + if (null !== self::$_cache) { + // 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 = 'minify_' . self::_getCacheId(); + $encodingExtension = self::$_options['encodeMethod'] + ? ('deflate' === self::$_options['encodeMethod'] + ? '.zd' + : '.zg') + : ''; + $fullCacheId = $cacheId . $encodingExtension; + // check cache for valid entry + $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']); + if ($cacheIsReady) { + $cacheContentLength = self::$_cache->getSize($fullCacheId); + } else { + // generate & cache content + $content = self::_combineMinify(); + self::$_cache->store($cacheId, $content); + self::$_cache->store($cacheId . '.zd', gzdeflate($content, self::$_options['encodeLevel'])); + self::$_cache->store($cacheId . '.zg', gzencode($content, self::$_options['encodeLevel'])); + } + } else { + // no cache + $cacheIsReady = false; + $content = self::_combineMinify(); + } + 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'] = self::$_options['contentTypeCharset'] + ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset'] + : self::$_options['contentType']; + if (self::$_options['encodeMethod'] !== '') { + $headers['Content-Encoding'] = $contentEncoding; + $headers['Vary'] = 'Accept-Encoding'; + } + + if (! self::$_options['quiet']) { + // output headers & content + foreach ($headers as $name => $val) { + header($name . ': ' . $val); + } + if ($cacheIsReady) { + self::$_cache->display($fullCacheId); + } else { + echo $content; + } + } else { + return array( + 'success' => true + ,'statusCode' => 200 + ,'content' => $cacheIsReady + ? self::$_cache->fetch($fullCacheId) + : $content + ,'headers' => $headers + ); + } + } + + /** + * @var Minify_Controller active controller for current request + */ + protected static $_controller = null; + + /** + * @var array options for current request + */ + protected static $_options = null; + + /** + * Set up sources to use Minify_Lines + * + * @param array $sources Minify_Source instances + * + * @return null + */ + protected static function _setupDebug($sources) + { + foreach ($sources as $source) { + $source->minifier = array('Minify_Lines', 'minify'); + $id = $source->getId(); + $source->minifyOptions = array( + 'id' => (is_file($id) ? basename($id) : $id) + ); + } + } + + /** + * Combines sources and minifies the result. + * + * @return string + */ + protected static function _combineMinify() { + $type = self::$_options['contentType']; // ease readability + + // when combining scripts, make sure all statements separated + $implodeSeparator = ($type === self::TYPE_JS) + ? ';' + : ''; + // allow the user to pass a particular array of options to each + // minifier (designated by type). source objects may still override + // these + $defaultOptions = isset(self::$_options['minifierOptions'][$type]) + ? self::$_options['minifierOptions'][$type] + : array(); + // if minifier not set, default is no minification. source objects + // may still override this + $defaultMinifier = isset(self::$_options['minifiers'][$type]) + ? self::$_options['minifiers'][$type] + : false; + + if (Minify_Source::haveNoMinifyPrefs(self::$_controller->sources)) { + // all source have same options/minifier, better performance + // to combine, then minify once + foreach (self::$_controller->sources as $source) { + $pieces[] = $source->getContent(); + } + $content = implode($implodeSeparator, $pieces); + if ($defaultMinifier) { + self::$_controller->loadMinifier($defaultMinifier); + $content = call_user_func($defaultMinifier, $content, $defaultOptions); + } + } else { + // minify each source with its own options and minifier, then combine + foreach (self::$_controller->sources as $source) { + // allow the source to override our minifier and options + $minifier = (null !== $source->minifier) + ? $source->minifier + : $defaultMinifier; + $options = (null !== $source->minifyOptions) + ? array_merge($defaultOptions, $source->minifyOptions) + : $defaultOptions; + if ($defaultMinifier) { + self::$_controller->loadMinifier($minifier); + // get source content and minify it + $pieces[] = call_user_func($minifier, $source->getContent(), $options); + } else { + $pieces[] = $source->getContent(); + } + } + $content = implode($implodeSeparator, $pieces); + } + + // do any post-processing (esp. for editing build URIs) + if (self::$_options['postprocessorRequire']) { + require_once self::$_options['postprocessorRequire']; + } + if (self::$_options['postprocessor']) { + $content = call_user_func(self::$_options['postprocessor'], $content, $type); + } + return $content; + } + + /** + * Make a unique cache id for for this request. + * + * Any settings that could affect output are taken into consideration + * + * @return string + */ + protected static function _getCacheId() { + return md5(serialize(array( + Minify_Source::getDigest(self::$_controller->sources) + ,self::$_options['minifiers'] ,self::$_options['minifierOptions'] - ,self::$_options['postprocessor'] - ))); - } -} + ,self::$_options['postprocessor'] + ))); + } +} diff --git a/min/lib/Minify/Build.php b/min/lib/Minify/Build.php index 15070f6..a6deec7 100644 --- a/min/lib/Minify/Build.php +++ b/min/lib/Minify/Build.php @@ -1,101 +1,101 @@ - - * // in config file - * $groupSources = array( - * 'js' => array('file1.js', 'file2.js') - * ,'css' => array('file1.css', 'file2.css', 'file3.css') - * ) - * - * // during HTML generation - * $jsBuild = new Minify_Build($groupSources['js']); - * $cssBuild = new Minify_Build($groupSources['css']); - * - * $script = ""; - * $link = ""; - * - * // in min.php - * Minify::serve('Groups', array( - * 'groups' => $groupSources - * ,'setExpires' => (time() + 86400 * 365) - * )); - * - * - * @package Minify - * @author Stephen Clay - */ -class Minify_Build { - - /** - * Last modification time of all files in the build - * - * @var int - */ - public $lastModified = 0; - - /** - * String to use as ampersand in uri(). Set this to '&' if - * you are not HTML-escaping URIs. - * - * @var string - */ - public static $ampersand = '&'; - - /** - * Get a time-stamped URI - * - * - * echo $b->uri('/site.js'); - * // outputs "/site.js?1678242" - * - * echo $b->uri('/scriptaculous.js?load=effects'); - * // outputs "/scriptaculous.js?load=effects&1678242" - * - * - * @param string $uri - * @return string - */ - public function uri($uri) { - $sep = strpos($uri, '?') === false - ? '?' - : self::$ampersand; - return "{$uri}{$sep}{$this->lastModified}"; - } - - /** - * Create a build object - * - * @param array $sources array of Minify_Source objects and/or file paths - * - * @return null - */ - public function __construct($sources) - { - $max = 0; - foreach ((array)$sources as $source) { - if ($source instanceof Minify_Source) { - $max = max($max, $source->lastModified); - } elseif (is_string($source)) { - if (0 === strpos($source, '//')) { - $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); - } - if (is_file($source)) { - $max = max($max, filemtime($source)); - } - } - } - $this->lastModified = $max; - } -} + + * // in config file + * $groupSources = array( + * 'js' => array('file1.js', 'file2.js') + * ,'css' => array('file1.css', 'file2.css', 'file3.css') + * ) + * + * // during HTML generation + * $jsBuild = new Minify_Build($groupSources['js']); + * $cssBuild = new Minify_Build($groupSources['css']); + * + * $script = ""; + * $link = ""; + * + * // in min.php + * Minify::serve('Groups', array( + * 'groups' => $groupSources + * ,'setExpires' => (time() + 86400 * 365) + * )); + * + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Build { + + /** + * Last modification time of all files in the build + * + * @var int + */ + public $lastModified = 0; + + /** + * String to use as ampersand in uri(). Set this to '&' if + * you are not HTML-escaping URIs. + * + * @var string + */ + public static $ampersand = '&'; + + /** + * Get a time-stamped URI + * + * + * echo $b->uri('/site.js'); + * // outputs "/site.js?1678242" + * + * echo $b->uri('/scriptaculous.js?load=effects'); + * // outputs "/scriptaculous.js?load=effects&1678242" + * + * + * @param string $uri + * @return string + */ + public function uri($uri) { + $sep = strpos($uri, '?') === false + ? '?' + : self::$ampersand; + return "{$uri}{$sep}{$this->lastModified}"; + } + + /** + * Create a build object + * + * @param array $sources array of Minify_Source objects and/or file paths + * + * @return null + */ + public function __construct($sources) + { + $max = 0; + foreach ((array)$sources as $source) { + if ($source instanceof Minify_Source) { + $max = max($max, $source->lastModified); + } elseif (is_string($source)) { + if (0 === strpos($source, '//')) { + $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); + } + if (is_file($source)) { + $max = max($max, filemtime($source)); + } + } + } + $this->lastModified = $max; + } +} diff --git a/min/lib/Minify/CSS.php b/min/lib/Minify/CSS.php index 75e97a3..65b9e67 100644 --- a/min/lib/Minify/CSS.php +++ b/min/lib/Minify/CSS.php @@ -1,4 +1,4 @@ -_path = $path; - } - - /** - * Write data to cache. - * - * @param string $id cache id (e.g. a filename) - * - * @param string $data - * - * @return bool success - */ - public function store($id, $data) - { - return self::_verifiedWrite($this->_path . '/' . $id, $data); - } - - /** - * Get the size of a cache entry - * - * @param string $id cache id (e.g. a filename) - * - * @return int size in bytes - */ - public function getSize($id) - { - return filesize($this->_path . '/' . $id); - } - - /** - * Does a valid cache entry exist? - * - * @param string $id cache id (e.g. a filename) - * - * @param int $srcMtime mtime of the original source file(s) - * - * @return bool exists - */ - public function isValid($id, $srcMtime) - { - $file = $this->_path . '/' . $id; - return (file_exists($file) && (filemtime($file) >= $srcMtime)); - } - - /** - * Send the cached content to output - * - * @param string $id cache id (e.g. a filename) - */ - public function display($id) - { - readfile($this->_path . '/' . $id); - } - - /** - * Fetch the cached content - * - * @param string $id cache id (e.g. a filename) - * - * @return string - */ - public function fetch($id) - { - return file_get_contents($this->_path . '/' . $id); - } - - private $_path = null; - - /** - * Write data to file and verify its contents - * - * @param string $file path - * - * @param string $data - * - * @return bool success - */ - private static function _verifiedWrite($file, $data) - { - if (! @file_put_contents($file, $data)) { - return false; - } - if (md5($data) !== md5_file($file)) { - @unlink($file); - return false; - } - return true; - } -} +_path = $path; + } + + /** + * Write data to cache. + * + * @param string $id cache id (e.g. a filename) + * + * @param string $data + * + * @return bool success + */ + public function store($id, $data) + { + return self::_verifiedWrite($this->_path . '/' . $id, $data); + } + + /** + * Get the size of a cache entry + * + * @param string $id cache id (e.g. a filename) + * + * @return int size in bytes + */ + public function getSize($id) + { + return filesize($this->_path . '/' . $id); + } + + /** + * Does a valid cache entry exist? + * + * @param string $id cache id (e.g. a filename) + * + * @param int $srcMtime mtime of the original source file(s) + * + * @return bool exists + */ + public function isValid($id, $srcMtime) + { + $file = $this->_path . '/' . $id; + return (file_exists($file) && (filemtime($file) >= $srcMtime)); + } + + /** + * Send the cached content to output + * + * @param string $id cache id (e.g. a filename) + */ + public function display($id) + { + readfile($this->_path . '/' . $id); + } + + /** + * Fetch the cached content + * + * @param string $id cache id (e.g. a filename) + * + * @return string + */ + public function fetch($id) + { + return file_get_contents($this->_path . '/' . $id); + } + + private $_path = null; + + /** + * Write data to file and verify its contents + * + * @param string $file path + * + * @param string $data + * + * @return bool success + */ + private static function _verifiedWrite($file, $data) + { + if (! @file_put_contents($file, $data)) { + return false; + } + if (md5($data) !== md5_file($file)) { + @unlink($file); + return false; + } + return true; + } +} diff --git a/min/lib/Minify/Controller/Base.php b/min/lib/Minify/Controller/Base.php index 99019ee..d3f5bb3 100644 --- a/min/lib/Minify/Controller/Base.php +++ b/min/lib/Minify/Controller/Base.php @@ -1,123 +1,123 @@ - - * - * @todo add static function to ease setting currentPath for CSS files - * (see line 83 of Version1.php) - */ -abstract class Minify_Controller_Base { - - /** - * Setup controller sources - * - * You must override this method in your subclass controller to set - * $this->sources. If the request is NOT valid, make sure $this->sources - * is left an empty array. Then strip any controller-specific options from - * $options and return it. - * - * @param array $options controller and Minify options - * - * @param array $options Minify options - */ - abstract public function setupSources($options); - - /** - * Get default Minify options for this controller. - * - * Override in subclass to change defaults - * - * @return array options for Minify - */ - public function getDefaultMinifyOptions() { - return array( - 'isPublic' => true - ,'encodeOutput' => true - ,'encodeMethod' => null // determine later - ,'encodeLevel' => 9 - ,'minifierOptions' => array() // no minifier options - ,'contentTypeCharset' => 'UTF-8' + + * + * @todo add static function to ease setting currentPath for CSS files + * (see line 83 of Version1.php) + */ +abstract class Minify_Controller_Base { + + /** + * Setup controller sources + * + * You must override this method in your subclass controller to set + * $this->sources. If the request is NOT valid, make sure $this->sources + * is left an empty array. Then strip any controller-specific options from + * $options and return it. + * + * @param array $options controller and Minify options + * + * @param array $options Minify options + */ + abstract public function setupSources($options); + + /** + * Get default Minify options for this controller. + * + * Override in subclass to change defaults + * + * @return array options for Minify + */ + public function getDefaultMinifyOptions() { + return array( + 'isPublic' => true + ,'encodeOutput' => true + ,'encodeMethod' => null // determine later + ,'encodeLevel' => 9 + ,'minifierOptions' => array() // no minifier options + ,'contentTypeCharset' => 'UTF-8' ,'maxAge' => 1800 // 30 minutes - ,'rewriteCssUris' => true - ,'quiet' => false // serve() will send headers and output - ,'debug' => false - - // if you override this, the response code MUST be directly after - // the first space. - ,'badRequestHeader' => 'HTTP/1.0 400 Bad Request' - - // callback function to see/modify content of all sources - ,'postprocessor' => null - // file to require to load preprocessor - ,'postprocessorRequire' => null - ); - } - - /** - * Get default minifiers for this controller. - * - * Override in subclass to change defaults - * - * @return array minifier callbacks for common types - */ - public function getDefaultMinifers() { - $ret[Minify::TYPE_JS] = array('Minify_Javascript', 'minify'); - $ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify'); - $ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify'); - return $ret; - } - - /** - * Load any code necessary to execute the given minifier callback. - * - * The controller is responsible for loading minification code on demand - * via this method. This built-in function will only load classes for - * static method callbacks where the class isn't already defined. It uses - * the PEAR convention, so, given array('Jimmy_Minifier', 'minCss'), this - * function will include 'Jimmy/Minifier.php' - * - * If you need code loaded on demand and this doesn't suit you, you'll need - * to override this function in your subclass. - * @see Minify_Controller_Page::loadMinifier() - * - * @param callback $minifierCallback callback of minifier function - * - * @return null - */ - public function loadMinifier($minifierCallback) - { - if (is_array($minifierCallback) - && is_string($minifierCallback[0]) - && !class_exists($minifierCallback[0], false)) { - - require str_replace('_', '/', $minifierCallback[0]) . '.php'; - } - } - - /** - * Is a user-given file within an allowable directory, existing, - * and having an extension js/css/html/txt - * - * This is a convenience function for controllers that have to accept - * user-given paths - * - * @param string $file full file path (already processed by realpath()) - * @param array $safeDirs directories where files are safe to serve - * @return bool file is safe - */ - public static function _fileIsSafe($file, $safeDirs) - { + ,'rewriteCssUris' => true + ,'quiet' => false // serve() will send headers and output + ,'debug' => false + + // if you override this, the response code MUST be directly after + // the first space. + ,'badRequestHeader' => 'HTTP/1.0 400 Bad Request' + + // callback function to see/modify content of all sources + ,'postprocessor' => null + // file to require to load preprocessor + ,'postprocessorRequire' => null + ); + } + + /** + * Get default minifiers for this controller. + * + * Override in subclass to change defaults + * + * @return array minifier callbacks for common types + */ + public function getDefaultMinifers() { + $ret[Minify::TYPE_JS] = array('Minify_Javascript', 'minify'); + $ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify'); + $ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify'); + return $ret; + } + + /** + * Load any code necessary to execute the given minifier callback. + * + * The controller is responsible for loading minification code on demand + * via this method. This built-in function will only load classes for + * static method callbacks where the class isn't already defined. It uses + * the PEAR convention, so, given array('Jimmy_Minifier', 'minCss'), this + * function will include 'Jimmy/Minifier.php' + * + * If you need code loaded on demand and this doesn't suit you, you'll need + * to override this function in your subclass. + * @see Minify_Controller_Page::loadMinifier() + * + * @param callback $minifierCallback callback of minifier function + * + * @return null + */ + public function loadMinifier($minifierCallback) + { + if (is_array($minifierCallback) + && is_string($minifierCallback[0]) + && !class_exists($minifierCallback[0], false)) { + + require str_replace('_', '/', $minifierCallback[0]) . '.php'; + } + } + + /** + * Is a user-given file within an allowable directory, existing, + * and having an extension js/css/html/txt + * + * This is a convenience function for controllers that have to accept + * user-given paths + * + * @param string $file full file path (already processed by realpath()) + * @param array $safeDirs directories where files are safe to serve + * @return bool file is safe + */ + public static function _fileIsSafe($file, $safeDirs) + { $pathOk = false; foreach ((array)$safeDirs as $safeDir) { if (strpos($file, $safeDir) === 0 && file_exists($file)) { @@ -128,68 +128,68 @@ abstract class Minify_Controller_Base { if (! $pathOk) { return false; } - $base = basename($file); - if ($base[0] === '.') { - return false; - } - list($revExt) = explode('.', strrev($base)); - return in_array(strrev($revExt), array('js', 'css', 'html', 'txt')); - } - - /*public static function _haveSameExt - - /** - * @var array instances of Minify_Source, which provide content and - * any individual minification needs. - * - * @see Minify_Source - */ - public $sources = array(); - - /** - * Mix in default controller options with user-given options - * - * @param array $options user options - * - * @return array mixed options - */ - public final function mixInDefaultOptions($options) - { - $ret = array_merge( - $this->getDefaultMinifyOptions(), $options - ); - if (! isset($options['minifiers'])) { - $options['minifiers'] = array(); - } - $ret['minifiers'] = array_merge( - $this->getDefaultMinifers(), $options['minifiers'] - ); - return $ret; - } - - /** - * Analyze sources (if there are any) and set $options 'contentType' - * and 'lastModifiedTime' if they already aren't. - * - * @param array $options options for Minify - * - * @return array options for Minify - */ - public final function analyzeSources($options = array()) - { - if ($this->sources) { - if (! isset($options['contentType'])) { - $options['contentType'] = Minify_Source::getContentType($this->sources); - } - // last modified is needed for caching, even if setExpires is set - if (! isset($options['lastModifiedTime'])) { - $max = 0; - foreach ($this->sources as $source) { - $max = max($source->lastModified, $max); - } - $options['lastModifiedTime'] = $max; - } - } - return $options; - } -} + $base = basename($file); + if ($base[0] === '.') { + return false; + } + list($revExt) = explode('.', strrev($base)); + return in_array(strrev($revExt), array('js', 'css', 'html', 'txt')); + } + + /*public static function _haveSameExt + + /** + * @var array instances of Minify_Source, which provide content and + * any individual minification needs. + * + * @see Minify_Source + */ + public $sources = array(); + + /** + * Mix in default controller options with user-given options + * + * @param array $options user options + * + * @return array mixed options + */ + public final function mixInDefaultOptions($options) + { + $ret = array_merge( + $this->getDefaultMinifyOptions(), $options + ); + if (! isset($options['minifiers'])) { + $options['minifiers'] = array(); + } + $ret['minifiers'] = array_merge( + $this->getDefaultMinifers(), $options['minifiers'] + ); + return $ret; + } + + /** + * Analyze sources (if there are any) and set $options 'contentType' + * and 'lastModifiedTime' if they already aren't. + * + * @param array $options options for Minify + * + * @return array options for Minify + */ + public final function analyzeSources($options = array()) + { + if ($this->sources) { + if (! isset($options['contentType'])) { + $options['contentType'] = Minify_Source::getContentType($this->sources); + } + // last modified is needed for caching, even if setExpires is set + if (! isset($options['lastModifiedTime'])) { + $max = 0; + foreach ($this->sources as $source) { + $max = max($source->lastModified, $max); + } + $options['lastModifiedTime'] = $max; + } + } + return $options; + } +} diff --git a/min/lib/Minify/Controller/Files.php b/min/lib/Minify/Controller/Files.php index 2be6668..25cf933 100644 --- a/min/lib/Minify/Controller/Files.php +++ b/min/lib/Minify/Controller/Files.php @@ -1,71 +1,71 @@ - - * Minify::serve('Files', array( - * 'files' => array( - * '//js/jquery.js' - * ,'//js/plugins.js' - * ,'/home/username/file.js' - * ) - * )); - * - * - * As a shortcut, the controller will replace "//" at the beginning - * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. - * - * @package Minify - * @author Stephen Clay - */ -class Minify_Controller_Files extends Minify_Controller_Base { - - /** - * Set up file sources - * - * @param array $options controller and Minify options - * @return array Minify options - * - * Controller options: - * - * 'files': (required) array of complete file paths, or a single path - */ - public function setupSources($options) { - // strip controller options - $files = (array)$options['files']; - unset($options['files']); - - $sources = array(); - foreach ($files as $file) { - if ($file instanceof Minify_Source) { - $sources[] = $file; - continue; - } - if (0 === strpos($file, '//')) { - $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); - } - $file = realpath($file); - if (file_exists($file)) { - $sources[] = new Minify_Source(array( - 'filepath' => $file - )); - } else { - // file not found - return $options; - } - } - if ($sources) { - $this->sources = $sources; - } - return $options; - } -} - + + * Minify::serve('Files', array( + * 'files' => array( + * '//js/jquery.js' + * ,'//js/plugins.js' + * ,'/home/username/file.js' + * ) + * )); + * + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Controller_Files extends Minify_Controller_Base { + + /** + * Set up file sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + * Controller options: + * + * 'files': (required) array of complete file paths, or a single path + */ + public function setupSources($options) { + // strip controller options + $files = (array)$options['files']; + unset($options['files']); + + $sources = array(); + foreach ($files as $file) { + if ($file instanceof Minify_Source) { + $sources[] = $file; + continue; + } + if (0 === strpos($file, '//')) { + $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); + } + $file = realpath($file); + if (file_exists($file)) { + $sources[] = new Minify_Source(array( + 'filepath' => $file + )); + } else { + // file not found + return $options; + } + } + if ($sources) { + $this->sources = $sources; + } + return $options; + } +} + diff --git a/min/lib/Minify/Controller/Groups.php b/min/lib/Minify/Controller/Groups.php index 3d77bd5..eb828e0 100644 --- a/min/lib/Minify/Controller/Groups.php +++ b/min/lib/Minify/Controller/Groups.php @@ -1,50 +1,50 @@ - - * Minify::serve('Groups', array( - * 'groups' => array( - * 'css' => array('//css/type.css', '//css/layout.css') - * ,'js' => array('//js/jquery.js', '//js/site.js') - * ) - * )); - * - * - * If the above code were placed in /serve.php, it would enable the URLs - * /serve.php/js and /serve.php/css - * - * As a shortcut, the controller will replace "//" at the beginning - * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. - * - * @package Minify - * @author Stephen Clay - */ -class Minify_Controller_Groups extends Minify_Controller_Base { - - /** - * Set up groups of files as sources - * - * @param array $options controller and Minify options - * @return array Minify options - * - * Controller options: - * - * 'groups': (required) array mapping PATH_INFO strings to arrays - * of complete file paths. @see Minify_Controller_Groups - */ - public function setupSources($options) { - // strip controller options - $groups = $options['groups']; - unset($options['groups']); + + * Minify::serve('Groups', array( + * 'groups' => array( + * 'css' => array('//css/type.css', '//css/layout.css') + * ,'js' => array('//js/jquery.js', '//js/site.js') + * ) + * )); + * + * + * If the above code were placed in /serve.php, it would enable the URLs + * /serve.php/js and /serve.php/css + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Controller_Groups extends Minify_Controller_Base { + + /** + * Set up groups of files as sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + * Controller options: + * + * 'groups': (required) array mapping PATH_INFO strings to arrays + * of complete file paths. @see Minify_Controller_Groups + */ + public function setupSources($options) { + // strip controller options + $groups = $options['groups']; + unset($options['groups']); // mod_fcgid places PATH_INFO in ORIG_PATH_INFO $pi = isset($_SERVER['ORIG_PATH_INFO']) @@ -57,29 +57,29 @@ class Minify_Controller_Groups extends Minify_Controller_Base { // no PATH_INFO or not a valid group return $options; } - $sources = array(); - foreach ((array)$groups[$pi] as $file) { - if ($file instanceof Minify_Source) { - $sources[] = $file; - continue; - } - if (0 === strpos($file, '//')) { - $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); - } - $file = realpath($file); - if (file_exists($file)) { - $sources[] = new Minify_Source(array( - 'filepath' => $file - )); - } else { - // file doesn't exist - return $options; - } - } - if ($sources) { - $this->sources = $sources; - } - return $options; - } -} - + $sources = array(); + foreach ((array)$groups[$pi] as $file) { + if ($file instanceof Minify_Source) { + $sources[] = $file; + continue; + } + if (0 === strpos($file, '//')) { + $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); + } + $file = realpath($file); + if (file_exists($file)) { + $sources[] = new Minify_Source(array( + 'filepath' => $file + )); + } else { + // file doesn't exist + return $options; + } + } + if ($sources) { + $this->sources = $sources; + } + return $options; + } +} + diff --git a/min/lib/Minify/Controller/Page.php b/min/lib/Minify/Controller/Page.php index aa4ab91..b8c090b 100644 --- a/min/lib/Minify/Controller/Page.php +++ b/min/lib/Minify/Controller/Page.php @@ -1,80 +1,80 @@ - - */ -class Minify_Controller_Page extends Minify_Controller_Base { - - /** - * Set up source of HTML content - * - * @param array $options controller and Minify options - * @return array Minify options - * - * Controller options: - * - * 'content': (required) HTML markup - * - * 'id': (required) id of page (string for use in server-side caching) - * - * 'lastModifiedTime': timestamp of when this content changed. This - * is recommended to allow both server and client-side caching. - * - * 'minifyAll': should all CSS and Javascript blocks be individually - * minified? (default false) - * - * @todo Add 'file' option to read HTML file. - */ - public function setupSources($options) { - // strip controller options - $sourceSpec = array( - 'content' => $options['content'] - ,'id' => $options['id'] - ); - unset($options['content'], $options['id']); - - if (isset($options['minifyAll'])) { - // this will be the 2nd argument passed to Minify_HTML::minify() - $sourceSpec['minifyOptions'] = array( - 'cssMinifier' => array('Minify_CSS', 'minify') - ,'jsMinifier' => array('Minify_Javascript', 'minify') - ); - $this->_loadCssJsMinifiers = true; - unset($options['minifyAll']); - } - $this->sources[] = new Minify_Source($sourceSpec); - - // may not be needed - //$options['minifier'] = array('Minify_HTML', 'minify'); - - $options['contentType'] = Minify::TYPE_HTML; - return $options; - } - - protected $_loadCssJsMinifiers = false; - - /** - * @see Minify_Controller_Base::loadMinifier() - */ - public function loadMinifier($minifierCallback) - { - if ($this->_loadCssJsMinifiers) { - // Minify will not call for these so we must manually load - // them when Minify/HTML.php is called for. - require 'Minify/CSS.php'; - require 'Minify/Javascript.php'; - } - parent::loadMinifier($minifierCallback); // load Minify/HTML.php - } -} - + + */ +class Minify_Controller_Page extends Minify_Controller_Base { + + /** + * Set up source of HTML content + * + * @param array $options controller and Minify options + * @return array Minify options + * + * Controller options: + * + * 'content': (required) HTML markup + * + * 'id': (required) id of page (string for use in server-side caching) + * + * 'lastModifiedTime': timestamp of when this content changed. This + * is recommended to allow both server and client-side caching. + * + * 'minifyAll': should all CSS and Javascript blocks be individually + * minified? (default false) + * + * @todo Add 'file' option to read HTML file. + */ + public function setupSources($options) { + // strip controller options + $sourceSpec = array( + 'content' => $options['content'] + ,'id' => $options['id'] + ); + unset($options['content'], $options['id']); + + if (isset($options['minifyAll'])) { + // this will be the 2nd argument passed to Minify_HTML::minify() + $sourceSpec['minifyOptions'] = array( + 'cssMinifier' => array('Minify_CSS', 'minify') + ,'jsMinifier' => array('Minify_Javascript', 'minify') + ); + $this->_loadCssJsMinifiers = true; + unset($options['minifyAll']); + } + $this->sources[] = new Minify_Source($sourceSpec); + + // may not be needed + //$options['minifier'] = array('Minify_HTML', 'minify'); + + $options['contentType'] = Minify::TYPE_HTML; + return $options; + } + + protected $_loadCssJsMinifiers = false; + + /** + * @see Minify_Controller_Base::loadMinifier() + */ + public function loadMinifier($minifierCallback) + { + if ($this->_loadCssJsMinifiers) { + // Minify will not call for these so we must manually load + // them when Minify/HTML.php is called for. + require 'Minify/CSS.php'; + require 'Minify/Javascript.php'; + } + parent::loadMinifier($minifierCallback); // load Minify/HTML.php + } +} + diff --git a/min/lib/Minify/Controller/Version1.php b/min/lib/Minify/Controller/Version1.php index e97e1d8..1861aab 100644 --- a/min/lib/Minify/Controller/Version1.php +++ b/min/lib/Minify/Controller/Version1.php @@ -1,118 +1,118 @@ - - * Minify::serve('Version1'); - * - * - * @package Minify - * @author Stephen Clay - */ -class Minify_Controller_Version1 extends Minify_Controller_Base { - - /** - * Set up groups of files as sources - * - * @param array $options controller and Minify options - * @return array Minify options - * - */ - public function setupSources($options) { - self::_setupDefines(); - if (MINIFY_USE_CACHE) { - $cacheDir = defined('MINIFY_CACHE_DIR') - ? MINIFY_CACHE_DIR - : ''; - Minify::setCache($cacheDir); - } - $options['badRequestHeader'] = 'HTTP/1.0 404 Not Found'; - $options['contentTypeCharset'] = MINIFY_ENCODING; - - // The following restrictions are to limit the URLs that minify will - // respond to. Ideally there should be only one way to reference a file. - if (! isset($_GET['files']) - // verify at least one file, files are single comma separated, - // and are all same extension - || ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m) - // no "//" (makes URL rewriting easier) - || strpos($_GET['files'], '//') !== false - // no "\" - || strpos($_GET['files'], '\\') !== false - // no "./" - || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['files']) - ) { - return $options; - } - $extension = $m[1]; - - $files = explode(',', $_GET['files']); - if (count($files) > MINIFY_MAX_FILES) { - return $options; - } - - // strings for prepending to relative/absolute paths - $prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME']) - . DIRECTORY_SEPARATOR; - $prependAbsPaths = $_SERVER['DOCUMENT_ROOT']; - - $sources = array(); - $goodFiles = array(); + + * Minify::serve('Version1'); + * + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Controller_Version1 extends Minify_Controller_Base { + + /** + * Set up groups of files as sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + */ + public function setupSources($options) { + self::_setupDefines(); + if (MINIFY_USE_CACHE) { + $cacheDir = defined('MINIFY_CACHE_DIR') + ? MINIFY_CACHE_DIR + : ''; + Minify::setCache($cacheDir); + } + $options['badRequestHeader'] = 'HTTP/1.0 404 Not Found'; + $options['contentTypeCharset'] = MINIFY_ENCODING; + + // The following restrictions are to limit the URLs that minify will + // respond to. Ideally there should be only one way to reference a file. + if (! isset($_GET['files']) + // verify at least one file, files are single comma separated, + // and are all same extension + || ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m) + // no "//" (makes URL rewriting easier) + || strpos($_GET['files'], '//') !== false + // no "\" + || strpos($_GET['files'], '\\') !== false + // no "./" + || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['files']) + ) { + return $options; + } + $extension = $m[1]; + + $files = explode(',', $_GET['files']); + if (count($files) > MINIFY_MAX_FILES) { + return $options; + } + + // strings for prepending to relative/absolute paths + $prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME']) + . DIRECTORY_SEPARATOR; + $prependAbsPaths = $_SERVER['DOCUMENT_ROOT']; + + $sources = array(); + $goodFiles = array(); $hasBadSource = false; $allowDirs = isset($options['allowDirs']) ? $options['allowDirs'] : MINIFY_BASE_DIR; - - foreach ($files as $file) { - // prepend appropriate string for abs/rel paths - $file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file; - // make sure a real file! - $file = realpath($file); - // don't allow unsafe or duplicate files - if (parent::_fileIsSafe($file, $allowDirs) - && !in_array($file, $goodFiles)) - { - $goodFiles[] = $file; - $srcOptions = array( - 'filepath' => $file - ); - $this->sources[] = new Minify_Source($srcOptions); - } else { - $hasBadSource = true; - break; - } - } - if ($hasBadSource) { - $this->sources = array(); + + foreach ($files as $file) { + // prepend appropriate string for abs/rel paths + $file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file; + // make sure a real file! + $file = realpath($file); + // don't allow unsafe or duplicate files + if (parent::_fileIsSafe($file, $allowDirs) + && !in_array($file, $goodFiles)) + { + $goodFiles[] = $file; + $srcOptions = array( + 'filepath' => $file + ); + $this->sources[] = new Minify_Source($srcOptions); + } else { + $hasBadSource = true; + break; + } + } + if ($hasBadSource) { + $this->sources = array(); } if (! MINIFY_REWRITE_CSS_URLS) { $options['rewriteCssUris'] = false; - } - return $options; - } - - private static function _setupDefines() - { - $defaults = array( - 'MINIFY_BASE_DIR' => realpath($_SERVER['DOCUMENT_ROOT']) - ,'MINIFY_ENCODING' => 'utf-8' - ,'MINIFY_MAX_FILES' => 16 - ,'MINIFY_REWRITE_CSS_URLS' => true - ,'MINIFY_USE_CACHE' => true - ); - foreach ($defaults as $const => $val) { - if (! defined($const)) { - define($const, $val); - } - } - } -} - + } + return $options; + } + + private static function _setupDefines() + { + $defaults = array( + 'MINIFY_BASE_DIR' => realpath($_SERVER['DOCUMENT_ROOT']) + ,'MINIFY_ENCODING' => 'utf-8' + ,'MINIFY_MAX_FILES' => 16 + ,'MINIFY_REWRITE_CSS_URLS' => true + ,'MINIFY_USE_CACHE' => true + ); + foreach ($defaults as $const => $val) { + if (! defined($const)) { + define($const, $val); + } + } + } +} + diff --git a/min/lib/Minify/HTML.php b/min/lib/Minify/HTML.php index 037caa0..2581eb4 100644 --- a/min/lib/Minify/HTML.php +++ b/min/lib/Minify/HTML.php @@ -1,88 +1,88 @@ - - */ -class Minify_HTML { - - /** - * "Minify" an HTML page - * - * @param string $html - * @param array $options - * @return string - */ - public static function minify($html, $options = array()) { - - if (isset($options['cssMinifier'])) { - self::$_cssMinifier = $options['cssMinifier']; - } - if (isset($options['jsMinifier'])) { - self::$_jsMinifier = $options['jsMinifier']; - } - - $html = str_replace("\r\n", "\n", trim($html)); - - self::$_isXhtml = (false !== strpos($html, ' + */ +class Minify_HTML { + + /** + * "Minify" an HTML page + * + * @param string $html + * @param array $options + * @return string + */ + public static function minify($html, $options = array()) { + + if (isset($options['cssMinifier'])) { + self::$_cssMinifier = $options['cssMinifier']; + } + if (isset($options['jsMinifier'])) { + self::$_jsMinifier = $options['jsMinifier']; + } + + $html = str_replace("\r\n", "\n", trim($html)); + + self::$_isXhtml = (false !== strpos($html, ']*?>)([\\s\\S]*?)<\\/script>\\s*/i' - ,array('Minify_HTML', '_removeScriptCB') - ,$html); - - // replace STYLEs (and minify) with placeholders - $html = preg_replace_callback( - '/\\s*(]*?>)([\\s\\S]*?)<\\/style>\\s*/i' - ,array('Minify_HTML', '_removeStyleCB') - ,$html); - - // remove HTML comments (but not IE conditional comments). - $html = preg_replace('//', '', $html); - - // replace PREs with placeholders - $html = preg_replace_callback('/\\s*(]*?>[\\s\\S]*?<\\/pre>)\\s*/i' - ,array('Minify_HTML', '_removePreCB') - , $html); - - // replace TEXTAREAs with placeholders - $html = preg_replace_callback( - '/\\s*(]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' - ,array('Minify_HTML', '_removeTaCB') - , $html); - - // trim each line. - // @todo take into account attribute values that span multiple lines. - $html = preg_replace('/^\\s+|\\s+$/m', '', $html); - - // remove ws around block/undisplayed elements - $html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' - .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' - .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' - .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' - .'|ul)\\b[^>]*>)/i', '$1', $html); - - // remove ws outside of all elements - $html = preg_replace_callback( - '/>([^<]+)]*?>)([\\s\\S]*?)<\\/script>\\s*/i' + ,array('Minify_HTML', '_removeScriptCB') + ,$html); + + // replace STYLEs (and minify) with placeholders + $html = preg_replace_callback( + '/\\s*(]*?>)([\\s\\S]*?)<\\/style>\\s*/i' + ,array('Minify_HTML', '_removeStyleCB') + ,$html); + + // remove HTML comments (but not IE conditional comments). + $html = preg_replace('//', '', $html); + + // replace PREs with placeholders + $html = preg_replace_callback('/\\s*(]*?>[\\s\\S]*?<\\/pre>)\\s*/i' + ,array('Minify_HTML', '_removePreCB') + , $html); + + // replace TEXTAREAs with placeholders + $html = preg_replace_callback( + '/\\s*(]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' + ,array('Minify_HTML', '_removeTaCB') + , $html); + + // trim each line. + // @todo take into account attribute values that span multiple lines. + $html = preg_replace('/^\\s+|\\s+$/m', '', $html); + + // remove ws around block/undisplayed elements + $html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' + .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' + .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' + .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' + .'|ul)\\b[^>]*>)/i', '$1', $html); + + // remove ws outside of all elements + $html = preg_replace_callback( + '/>([^<]+)]+>)/i', "$1\n$2", $html); - - self::$_cssMinifier = self::$_jsMinifier = null; - return $html; + + self::$_cssMinifier = self::$_jsMinifier = null; + return $html; } protected static function _reservePlace($content) @@ -104,83 +104,83 @@ class Minify_HTML { $placeholder = '%' . self::$_replacementHash . count(self::$_placeholders) . '%'; self::$_placeholders[$placeholder] = $content; return $placeholder; - } - - protected static $_isXhtml = false; + } + + protected static $_isXhtml = false; protected static $_replacementHash = null; protected static $_placeholders = array(); - protected static $_cssMinifier = null; - protected static $_jsMinifier = null; - - protected static function _outsideTagCB($m) - { - return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<'; - } - - protected static function _removePreCB($m) - { - return self::_reservePlace($m[1]); - } - - protected static function _removeTaCB($m) - { - return self::_reservePlace($m[1]); - } - - protected static function _removeStyleCB($m) - { - $openStyle = $m[1]; - $css = $m[2]; - // remove HTML comments - $css = preg_replace('/(?:^\\s*\\s*$)/', '', $css); - - // remove CDATA section markers - $css = self::_removeCdata($css); - - // minify - $minifier = self::$_cssMinifier - ? self::$_cssMinifier - : 'trim'; - $css = call_user_func($minifier, $css); - + protected static $_cssMinifier = null; + protected static $_jsMinifier = null; + + protected static function _outsideTagCB($m) + { + return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<'; + } + + protected static function _removePreCB($m) + { + return self::_reservePlace($m[1]); + } + + protected static function _removeTaCB($m) + { + return self::_reservePlace($m[1]); + } + + protected static function _removeStyleCB($m) + { + $openStyle = $m[1]; + $css = $m[2]; + // remove HTML comments + $css = preg_replace('/(?:^\\s*\\s*$)/', '', $css); + + // remove CDATA section markers + $css = self::_removeCdata($css); + + // minify + $minifier = self::$_cssMinifier + ? self::$_cssMinifier + : 'trim'; + $css = call_user_func($minifier, $css); + return self::_reservePlace(self::_needsCdata($css) ? "{$openStyle}/**/" : "{$openStyle}{$css}" - ); - } - - protected static function _removeScriptCB($m) - { - $openScript = $m[1]; - $js = $m[2]; - - // remove HTML comments (and ending "//" if present) - $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); - - // remove CDATA section markers - $js = self::_removeCdata($js); - - // minify - $minifier = self::$_jsMinifier - ? self::$_jsMinifier - : 'trim'; - $js = call_user_func($minifier, $js); - + ); + } + + protected static function _removeScriptCB($m) + { + $openScript = $m[1]; + $js = $m[2]; + + // remove HTML comments (and ending "//" if present) + $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); + + // remove CDATA section markers + $js = self::_removeCdata($js); + + // minify + $minifier = self::$_jsMinifier + ? self::$_jsMinifier + : 'trim'; + $js = call_user_func($minifier, $js); + return self::_reservePlace(self::_needsCdata($js) ? "{$openScript}/**/" : "{$openScript}{$js}" - ); - } - - protected static function _removeCdata($str) - { - return (false !== strpos($str, ''), '', $str) - : $str; - } - - protected static function _needsCdata($str) - { - return (self::$_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); - } -} + ); + } + + protected static function _removeCdata($str) + { + return (false !== strpos($str, ''), '', $str) + : $str; + } + + protected static function _needsCdata($str) + { + return (self::$_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); + } +} diff --git a/min/lib/Minify/Javascript.php b/min/lib/Minify/Javascript.php index ed19ac9..8703cf8 100644 --- a/min/lib/Minify/Javascript.php +++ b/min/lib/Minify/Javascript.php @@ -1,74 +1,74 @@ - - */ -class Minify_Javascript { - - /** - * Minify a Javascript string - * - * @param string $js - * - * @param array $options available options: - * - * 'preserveComments': (default true) multi-line comments that begin - * with "/*!" will be preserved with newlines before and after to - * enhance readability. - * - * @return string - */ - public static function minify($js, $options = array()) - { - if (isset($options['preserveComments']) - && !$options['preserveComments']) { - return trim(JSMin::minify($js)); - } - $ret = ''; - while (1) { - list($beforeComment, $comment, $afterComment) - = self::_nextYuiComment($js); - $ret .= trim(JSMin::minify($beforeComment)); - if (false === $comment) { - break; - } - $ret .= $comment; - $js = $afterComment; - } - return $ret; - } - - /** - * Extract comments that YUI Compressor preserves. - * - * @param string $in input - * - * @return array 3 elements are returned. If a YUI comment is found, the - * 2nd element is the comment and the 1st and 2nd are the surrounding - * strings. If no comment is found, the entire string is returned as the - * 1st element and the other two are false. - */ - private static function _nextYuiComment($in) - { - return ( - (false !== ($start = strpos($in, '/*!'))) - && (false !== ($end = strpos($in, '*/', $start + 3))) - ) - ? array( - substr($in, 0, $start) - ,"\n/*" . substr($in, $start + 3, $end - $start - 1) . "\n" - ,substr($in, -(strlen($in) - $end - 2)) - ) - : array($in, false, false); - } -} - + + */ +class Minify_Javascript { + + /** + * Minify a Javascript string + * + * @param string $js + * + * @param array $options available options: + * + * 'preserveComments': (default true) multi-line comments that begin + * with "/*!" will be preserved with newlines before and after to + * enhance readability. + * + * @return string + */ + public static function minify($js, $options = array()) + { + if (isset($options['preserveComments']) + && !$options['preserveComments']) { + return trim(JSMin::minify($js)); + } + $ret = ''; + while (1) { + list($beforeComment, $comment, $afterComment) + = self::_nextYuiComment($js); + $ret .= trim(JSMin::minify($beforeComment)); + if (false === $comment) { + break; + } + $ret .= $comment; + $js = $afterComment; + } + return $ret; + } + + /** + * Extract comments that YUI Compressor preserves. + * + * @param string $in input + * + * @return array 3 elements are returned. If a YUI comment is found, the + * 2nd element is the comment and the 1st and 2nd are the surrounding + * strings. If no comment is found, the entire string is returned as the + * 1st element and the other two are false. + */ + private static function _nextYuiComment($in) + { + return ( + (false !== ($start = strpos($in, '/*!'))) + && (false !== ($end = strpos($in, '*/', $start + 3))) + ) + ? array( + substr($in, 0, $start) + ,"\n/*" . substr($in, $start + 3, $end - $start - 1) . "\n" + ,substr($in, -(strlen($in) - $end - 2)) + ) + : array($in, false, false); + } +} + diff --git a/min/lib/Minify/Lines.php b/min/lib/Minify/Lines.php index e9b76fa..8b7c96b 100644 --- a/min/lib/Minify/Lines.php +++ b/min/lib/Minify/Lines.php @@ -1,124 +1,124 @@ - - */ -class Minify_Lines { - - /** - * Add line numbers in C-style comments - * - * This uses a very basic parser easily fooled by comment tokens inside - * strings or regexes, but, otherwise, generally clean code will not be - * mangled. - * - * @param string $content - * - * @param array $options available options: - * - * 'id': (optional) string to identify file. E.g. file name/path - * - * @return string - */ - public static function minify($content, $options = array()) - { - $id = (isset($options['id']) && $options['id']) - ? $options['id'] - : ''; - if (! $eol = self::_getEol($content)) { - return $content; - } - $lines = explode($eol, $content); - $numLines = count($lines); - // determine left padding - $padTo = strlen($numLines); - $inComment = false; - $i = 0; - $newLines = array(); - while (null !== ($line = array_shift($lines))) { - if (('' !== $id) && (0 == $i % 50)) { - array_push($newLines, '', "/* {$id} */", ''); - } - ++$i; - $newLines[] = self::_addNote($line, $i, $inComment, $padTo); - $inComment = self::_eolInComment($line, $inComment); - } - return implode($eol, $newLines) . $eol; - } - - /** - * Determine EOL character sequence - * - * @param string $str file content - * - * @return string EOL char(s) or '' if no EOL could be found - */ - private static function _getEol($str) - { - $r = strpos($str, "\r"); - $n = strpos($str, "\n"); - if (false === $r && false === $n) { - return ''; - } - return ($r !== false) - ? ($n == ($r + 1) - ? "\r\n" - : "\r") - : "\n"; - } - - /** - * Is the parser within a C-style comment at the end of this line? - * - * @param string $line current line of code - * - * @param bool $inComment was the parser in a comment at the - * beginning of the line? - * - * @return bool - */ - private static function _eolInComment($line, $inComment) - { - while (strlen($line)) { - $search = $inComment - ? '*/' - : '/*'; - $pos = strpos($line, $search); - if (false === $pos) { - return $inComment; - } else { - $inComment = ! $inComment; - $line = substr($line, $pos + 2); - } - } - return $inComment; - } - - /** - * Prepend a comment (or note) to the given line - * - * @param string $line current line of code - * - * @param string $note content of note/comment - * - * @param bool $inComment was the parser in a comment at the - * beginning of the line? - * - * @param int $padTo minimum width of comment - * - * @return string - */ - private static function _addNote($line, $note, $inComment, $padTo) - { - return $inComment - ? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line - : '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; - } -} + + */ +class Minify_Lines { + + /** + * Add line numbers in C-style comments + * + * This uses a very basic parser easily fooled by comment tokens inside + * strings or regexes, but, otherwise, generally clean code will not be + * mangled. + * + * @param string $content + * + * @param array $options available options: + * + * 'id': (optional) string to identify file. E.g. file name/path + * + * @return string + */ + public static function minify($content, $options = array()) + { + $id = (isset($options['id']) && $options['id']) + ? $options['id'] + : ''; + if (! $eol = self::_getEol($content)) { + return $content; + } + $lines = explode($eol, $content); + $numLines = count($lines); + // determine left padding + $padTo = strlen($numLines); + $inComment = false; + $i = 0; + $newLines = array(); + while (null !== ($line = array_shift($lines))) { + if (('' !== $id) && (0 == $i % 50)) { + array_push($newLines, '', "/* {$id} */", ''); + } + ++$i; + $newLines[] = self::_addNote($line, $i, $inComment, $padTo); + $inComment = self::_eolInComment($line, $inComment); + } + return implode($eol, $newLines) . $eol; + } + + /** + * Determine EOL character sequence + * + * @param string $str file content + * + * @return string EOL char(s) or '' if no EOL could be found + */ + private static function _getEol($str) + { + $r = strpos($str, "\r"); + $n = strpos($str, "\n"); + if (false === $r && false === $n) { + return ''; + } + return ($r !== false) + ? ($n == ($r + 1) + ? "\r\n" + : "\r") + : "\n"; + } + + /** + * Is the parser within a C-style comment at the end of this line? + * + * @param string $line current line of code + * + * @param bool $inComment was the parser in a comment at the + * beginning of the line? + * + * @return bool + */ + private static function _eolInComment($line, $inComment) + { + while (strlen($line)) { + $search = $inComment + ? '*/' + : '/*'; + $pos = strpos($line, $search); + if (false === $pos) { + return $inComment; + } else { + $inComment = ! $inComment; + $line = substr($line, $pos + 2); + } + } + return $inComment; + } + + /** + * Prepend a comment (or note) to the given line + * + * @param string $line current line of code + * + * @param string $note content of note/comment + * + * @param bool $inComment was the parser in a comment at the + * beginning of the line? + * + * @param int $padTo minimum width of comment + * + * @return string + */ + private static function _addNote($line, $note, $inComment, $padTo) + { + return $inComment + ? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line + : '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; + } +} diff --git a/min/lib/Minify/Source.php b/min/lib/Minify/Source.php index 43ac4b7..cbffae5 100644 --- a/min/lib/Minify/Source.php +++ b/min/lib/Minify/Source.php @@ -1,177 +1,177 @@ - - */ -class Minify_Source { - - /** - * @var int time of last modification - */ - public $lastModified = null; - - /** - * @var callback minifier function specifically for this source. - */ - public $minifier = null; - - /** - * @var array minification options specific to this source. - */ - public $minifyOptions = null; + + */ +class Minify_Source { + + /** + * @var int time of last modification + */ + public $lastModified = null; + + /** + * @var callback minifier function specifically for this source. + */ + public $minifier = null; + + /** + * @var array minification options specific to this source. + */ + public $minifyOptions = null; /** * @var string full path of file */ public $filepath = null; - - /** - * Create a Minify_Source - * - * In the $spec array(), you can either provide a 'filepath' to an existing - * file (existence will not be checked!) or give 'id' (unique string for - * the content), 'content' (the string content) and 'lastModified' - * (unixtime of last update). - * - * As a shortcut, the controller will replace "//" at the beginning - * of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'. - * - * @param array $spec options - */ - public function __construct($spec) - { - if (isset($spec['filepath'])) { - if (0 === strpos($spec['filepath'], '//')) { - $spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1); - } - $this->filepath = $spec['filepath']; - $this->_id = $spec['filepath']; - $this->lastModified = filemtime($spec['filepath']) - // offset for Windows uploaders with out of sync clocks - + round(Minify::$uploaderHoursBehind * 3600); - } elseif (isset($spec['id'])) { - $this->_id = 'id::' . $spec['id']; - if (isset($spec['content'])) { - $this->_content = $spec['content']; - } else { - $this->_getContentFunc = $spec['getContentFunc']; - } - $this->lastModified = isset($spec['lastModified']) - ? $spec['lastModified'] - : time(); - } - if (isset($spec['minifier'])) { - $this->minifier = $spec['minifier']; - } - if (isset($spec['minifyOptions'])) { - $this->minifyOptions = $spec['minifyOptions']; - } - } - - /** - * Get content - * - * @return string - */ - public function getContent() - { - $content = (null !== $this->filepath) - ? file_get_contents($this->filepath) - : ((null !== $this->_content) - ? $this->_content - : call_user_func($this->_getContentFunc, $this->_id) - ); - // remove UTF-8 BOM if present - return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) - ? substr($content, 3) - : $content; - } - - /** - * Get id - * - * @return string - */ - public function getId() - { - return $this->_id; - } - - /** - * Verifies a single minification call can handle all sources - * - * @param array $sources Minify_Source instances - * - * @return bool true iff there no sources with specific minifier preferences. - */ - public static function haveNoMinifyPrefs($sources) - { - foreach ($sources as $source) { - if (null !== $source->minifier - || null !== $source->minifyOptions) { - return false; - } - } - return true; - } - - /** - * Get unique string for a set of sources - * - * @param array $sources Minify_Source instances - * - * @return string - */ - public static function getDigest($sources) - { - foreach ($sources as $source) { - $info[] = array( - $source->_id, $source->minifier, $source->minifyOptions - ); - } - return md5(serialize($info)); - } - - /** - * Guess content type from the first filename extension available - * - * This is called if the user doesn't pass in a 'contentType' options - * - * @param array $sources Minify_Source instances - * - * @return string content type. e.g. 'text/css' - */ - public static function getContentType($sources) - { - $exts = array( - 'css' => Minify::TYPE_CSS - ,'js' => Minify::TYPE_JS - ,'html' => Minify::TYPE_HTML - ); - foreach ($sources as $source) { - if (null !== $source->filepath) { - $segments = explode('.', $source->filepath); - $ext = array_pop($segments); - if (isset($exts[$ext])) { - return $exts[$ext]; - } - } - } - return 'text/plain'; - } - - protected $_content = null; - protected $_getContentFunc = null; - protected $_id = null; -} - + + /** + * Create a Minify_Source + * + * In the $spec array(), you can either provide a 'filepath' to an existing + * file (existence will not be checked!) or give 'id' (unique string for + * the content), 'content' (the string content) and 'lastModified' + * (unixtime of last update). + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @param array $spec options + */ + public function __construct($spec) + { + if (isset($spec['filepath'])) { + if (0 === strpos($spec['filepath'], '//')) { + $spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1); + } + $this->filepath = $spec['filepath']; + $this->_id = $spec['filepath']; + $this->lastModified = filemtime($spec['filepath']) + // offset for Windows uploaders with out of sync clocks + + round(Minify::$uploaderHoursBehind * 3600); + } elseif (isset($spec['id'])) { + $this->_id = 'id::' . $spec['id']; + if (isset($spec['content'])) { + $this->_content = $spec['content']; + } else { + $this->_getContentFunc = $spec['getContentFunc']; + } + $this->lastModified = isset($spec['lastModified']) + ? $spec['lastModified'] + : time(); + } + if (isset($spec['minifier'])) { + $this->minifier = $spec['minifier']; + } + if (isset($spec['minifyOptions'])) { + $this->minifyOptions = $spec['minifyOptions']; + } + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + $content = (null !== $this->filepath) + ? file_get_contents($this->filepath) + : ((null !== $this->_content) + ? $this->_content + : call_user_func($this->_getContentFunc, $this->_id) + ); + // remove UTF-8 BOM if present + return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) + ? substr($content, 3) + : $content; + } + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->_id; + } + + /** + * Verifies a single minification call can handle all sources + * + * @param array $sources Minify_Source instances + * + * @return bool true iff there no sources with specific minifier preferences. + */ + public static function haveNoMinifyPrefs($sources) + { + foreach ($sources as $source) { + if (null !== $source->minifier + || null !== $source->minifyOptions) { + return false; + } + } + return true; + } + + /** + * Get unique string for a set of sources + * + * @param array $sources Minify_Source instances + * + * @return string + */ + public static function getDigest($sources) + { + foreach ($sources as $source) { + $info[] = array( + $source->_id, $source->minifier, $source->minifyOptions + ); + } + return md5(serialize($info)); + } + + /** + * Guess content type from the first filename extension available + * + * This is called if the user doesn't pass in a 'contentType' options + * + * @param array $sources Minify_Source instances + * + * @return string content type. e.g. 'text/css' + */ + public static function getContentType($sources) + { + $exts = array( + 'css' => Minify::TYPE_CSS + ,'js' => Minify::TYPE_JS + ,'html' => Minify::TYPE_HTML + ); + foreach ($sources as $source) { + if (null !== $source->filepath) { + $segments = explode('.', $source->filepath); + $ext = array_pop($segments); + if (isset($exts[$ext])) { + return $exts[$ext]; + } + } + } + return 'text/plain'; + } + + protected $_content = null; + protected $_getContentFunc = null; + protected $_id = null; +} + diff --git a/min/min_group_uri.php b/min/min_group_uri.php index ea9070b..6513f12 100644 --- a/min/min_group_uri.php +++ b/min/min_group_uri.php @@ -1,31 +1,31 @@ - - * - * - * - * - * @param string $group a key of the array in groupsConfig.php - * @param string $ampersand '&' or '&' (default '&') - * @return string - */ -function min_group_uri($group, $ampersand = '&') -{ - static $gc = false; - if (false === $gc) { - $gc = (require dirname(__FILE__) . '/groupsConfig.php'); - } - $b = new Minify_Build($gc[$group]); - Minify_Build::$ampersand = $ampersand; - return $b->uri('/' . basename(dirname(__FILE__)) . '/?g=' . $group); -} + + * + * + * + * + * @param string $group a key of the array in groupsConfig.php + * @param string $ampersand '&' or '&' (default '&') + * @return string + */ +function min_group_uri($group, $ampersand = '&') +{ + static $gc = false; + if (false === $gc) { + $gc = (require dirname(__FILE__) . '/groupsConfig.php'); + } + $b = new Minify_Build($gc[$group]); + Minify_Build::$ampersand = $ampersand; + return $b->uri('/' . basename(dirname(__FILE__)) . '/?g=' . $group); +} diff --git a/web/ab_tests/ideal_php/before.php b/web/ab_tests/ideal_php/before.php index ef85501..fc5c77d 100644 --- a/web/ab_tests/ideal_php/before.php +++ b/web/ab_tests/ideal_php/before.php @@ -1,25 +1,25 @@ - array( - dirname(__FILE__) . '/before.js' - ) - ,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr -)); + array( + dirname(__FILE__) . '/before.js' + ) + ,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr +)); diff --git a/web/ab_tests/minify/test_Groups.php b/web/ab_tests/minify/test_Groups.php index cbee3ec..a70bc94 100644 --- a/web/ab_tests/minify/test_Groups.php +++ b/web/ab_tests/minify/test_Groups.php @@ -1,15 +1,15 @@ - array( - 'test' => array(dirname(__FILE__) . '/before.js') - ) - ,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr -)); + array( + 'test' => array(dirname(__FILE__) . '/before.js') + ) + ,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr +)); diff --git a/web/ab_tests/minify/test_Version1.php b/web/ab_tests/minify/test_Version1.php index 2df0f48..43ba632 100644 --- a/web/ab_tests/minify/test_Version1.php +++ b/web/ab_tests/minify/test_Version1.php @@ -1,12 +1,12 @@ - -* and by the article "Supercharged JavaScript" by Patrick Hunlock -* . -* -* JSMin was originally written by Douglas Crockford . -* -* Requires PHP 5.2.1+. -* -* @package Minify -* @author Ryan Grove -* @copyright 2007 Ryan Grove. All rights reserved. -* @license http://opensource.org/licenses/bsd-license.php New BSD License -* @version 1.1.0 (?) -* @link http://code.google.com/p/minify/ -*/ - -if (!defined('MINIFY_BASE_DIR')) { - /** - * Base path from which all relative file paths should be resolved. By default - * this is set to the document root. - */ - define('MINIFY_BASE_DIR', realpath($_SERVER['DOCUMENT_ROOT'])); -} - -if (!defined('MINIFY_CACHE_DIR')) { - /** Directory where compressed files will be cached. */ - define('MINIFY_CACHE_DIR', sys_get_temp_dir()); -} - -if (!defined('MINIFY_ENCODING')) { - /** Character set to use when outputting the minified files. */ - define('MINIFY_ENCODING', 'utf-8'); -} - -if (!defined('MINIFY_MAX_FILES')) { - /** Maximum number of files to combine in one request. */ - define('MINIFY_MAX_FILES', 16); -} - -if (!defined('MINIFY_REWRITE_CSS_URLS')) { - /** - * Whether or not Minify should attempt to rewrite relative URLs used in CSS - * files so that they continue to point to the correct location after the file - * is combined and minified. - * - * Minify is pretty good at getting this right, but occasionally it can make - * mistakes. If you find that URL rewriting results in problems, you should - * disable it. - */ - define('MINIFY_REWRITE_CSS_URLS', true); -} - -if (!defined('MINIFY_USE_CACHE')) { - /** - * Whether or not Minify should use a disk-based cache to increase - * performance. - */ - define('MINIFY_USE_CACHE', true); -} - -/** -* Minify is a library for combining, minifying, and caching JavaScript and CSS -* files on demand before sending them to a web browser. -* -* @package Minify -* @author Ryan Grove -* @copyright 2007 Ryan Grove. All rights reserved. -* @license http://opensource.org/licenses/bsd-license.php New BSD License -* @version 1.1.0 (?) -* @link http://code.google.com/p/minify/ -*/ -class Minify { - const TYPE_CSS = 'text/css'; - const TYPE_HTML = 'text/html'; - const TYPE_JS = 'text/javascript'; - - protected $files = array(); - protected $type = self::TYPE_JS; - - // -- Public Static Methods -------------------------------------------------- - - /** - * Combines, minifies, and outputs the requested files. - * - * Inspects the $_GET array for a 'files' entry containing a comma-separated - * list and uses this as the set of files to be combined and minified. - */ - public static function handleRequest() { - // 404 if no files were requested. - if (!isset($_GET['files'])) { - header('HTTP/1.0 404 Not Found'); - exit; - } - - $files = array_map('trim', explode(',', $_GET['files'], MINIFY_MAX_FILES)); - - // 404 if the $files array is empty for some weird reason. - if (!count($files)) { - header('HTTP/1.0 404 Not Found'); - exit; - } - - // Determine the content type based on the extension of the first file - // requested. - if (preg_match('/\.js$/iD', $files[0])) { - $type = self::TYPE_JS; - } else if (preg_match('/\.css$/iD', $files[0])) { - $type = self::TYPE_CSS; - } else { - $type = self::TYPE_HTML; - } - - // Minify and spit out the result. - try { - $minify = new Minify($type, $files); - - header("Content-Type: $type;charset=".MINIFY_ENCODING); - - $minify->browserCache(); - echo $minify->combine(); - exit; - } - catch (MinifyException $e) { - header('HTTP/1.0 404 Not Found'); - echo htmlentities($e->getMessage()); - exit; - } - } - - /** - * Minifies the specified string and returns it. - * - * @param string $string JavaScript, CSS, or HTML string to minify - * @param string $type content type of the string (Minify::TYPE_CSS, - * Minify::TYPE_HTML, or Minify::TYPE_JS) - * @return string minified string - */ - public static function min($string, $type = self::TYPE_JS) { - switch ($type) { - case self::TYPE_CSS: - return self::minifyCSS($string); - break; - - case self::TYPE_HTML: - return self::minifyHTML($string); - break; - - case self::TYPE_JS: - return self::minifyJS($string); - break; - } - - return $string; - } - - // -- Protected Static Methods ----------------------------------------------- - - /** - * Minifies the specified CSS string and returns it. - * - * @param string $css CSS string - * @return string minified string - * @see minify() - * @see minifyJS() - */ - protected static function minifyCSS($css) { - // Compress whitespace. - $css = preg_replace('/\s+/', ' ', $css); - - // Remove comments. - $css = preg_replace('/\/\*.*?\*\//', '', $css); - - return trim($css); - } - - protected static function minifyHTML($html) { - require_once dirname(__FILE__).'/lib/htmlmin.php'; - return HTMLMin::minify($html); - } - - /** - * Minifies the specified JavaScript string and returns it. - * - * @param string $js JavaScript string - * @return string minified string - * @see minify() - * @see minifyCSS() - */ - protected static function minifyJS($js) { - require_once dirname(__FILE__).'/../../../min/lib/JSMin.php'; - return JSMin::minify($js); - } - - /** - * Rewrites relative URLs in the specified CSS string to point to the correct - * location. URLs are assumed to be relative to the absolute path specified in - * the $path parameter. - * - * @param string $css CSS string - * @param string $path absolute path to which URLs are relative (should be a - * directory, not a file) - * @return string CSS string with rewritten URLs - */ - protected static function rewriteCSSUrls($css, $path) { - /* - Parentheses, commas, whitespace chars, single quotes, and double quotes are - escaped with a backslash as described in the CSS spec: - http://www.w3.org/TR/REC-CSS1#url - */ - $relativePath = preg_replace('/([\(\),\s\'"])/', '\\\$1', - str_replace(MINIFY_BASE_DIR, '', $path)); - - - return preg_replace('/url\(\s*[\'"]?\/?(.+?)[\'"]?\s*\)/i', 'url('. - $relativePath.'/$1)', $css); - } - - // -- Public Instance Methods ------------------------------------------------ - - /** - * Instantiates a new Minify object. A filename can be in the form of a - * relative path or a URL that resolves to the same site that hosts Minify. - * - * @param string $type content type of the specified files (either - * Minify::TYPE_CSS or Minify::TYPE_JS) - * @param array|string $files filename or array of filenames to be minified - */ - public function __construct($type = self::TYPE_JS, $files = array()) { - if ($type !== self::TYPE_JS && $type !== self::TYPE_CSS) { - throw new MinifyInvalidArgumentException('Invalid argument ($type): '. - $type); - } - - $this->type = $type; - - if (count((array) $files)) { - $this->addFile($files); - } - } - - /** - * Adds the specified filename or array of filenames to the list of files to - * be minified. A filename can be in the form of a relative path or a URL - * that resolves to the same site that hosts Minify. - * - * @param array|string $files filename or array of filenames - * @see getFiles() - * @see removeFile() - */ - public function addFile($files) { - $files = @array_map(array($this, 'resolveFilePath'), (array) $files); - $this->files = array_unique(array_merge($this->files, $files)); - } - - /** - * Attempts to serve the combined, minified files from the cache if possible. - * - * This method first checks the ETag value and If-Modified-Since timestamp - * sent by the browser and exits with an HTTP "304 Not Modified" response if - * the requested files haven't changed since they were last sent to the - * client. - * - * If the browser hasn't cached the content, we check to see if it's been - * cached on the server and, if so, we send the cached content and exit. - * - * If neither the client nor the server has the content in its cache, we don't - * do anything. - * - * @return bool - */ - public function browserCache() { - $hash = $this->getHash(); - $lastModified = $this->getLastModified(); - - $lastModifiedGMT = gmdate('D, d M Y H:i:s', $lastModified).' GMT'; - - // Check/set the ETag. - $etag = $hash.'_'.$lastModified; - - if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { - if (strpos($_SERVER['HTTP_IF_NONE_MATCH'], $etag) !== false) { - header("Last-Modified: $lastModifiedGMT", true, 304); - exit; - } - } - - header('ETag: "'.$etag.'"'); - - // Check If-Modified-Since. - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { - if ($lastModified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { - header("Last-Modified: $lastModifiedGMT", true, 304); - exit; - } - } - - header("Last-Modified: $lastModifiedGMT"); - - return false; - } - - /** - * Combines and returns the contents of all files that have been added with - * addFile() or via this class's constructor. - * - * If MINIFY_USE_CACHE is true, the content will be returned from the server's - * cache if the cache is up to date; otherwise the new content will be saved - * to the cache for future use. - * - * @param bool $minify minify the combined contents before returning them - * @return string combined file contents - */ - public function combine($minify = true) { - // Return contents from server cache if possible. - if (MINIFY_USE_CACHE) { - if ($cacheResult = $this->serverCache(true)) { - return $cacheResult; - } - } - - // Combine contents. - $combined = array(); - - foreach($this->files as $file) { - if ($this->type === self::TYPE_CSS && MINIFY_REWRITE_CSS_URLS) { - // Rewrite relative CSS URLs. - $combined[] = self::rewriteCSSUrls(file_get_contents($file), - dirname($file)); - } - else { - $combined[] = file_get_contents($file); - } - } - - $combined = $minify ? self::min(implode("\n", $combined), $this->type) : - implode("\n", $combined); - - // Save combined contents to the cache. - if (MINIFY_USE_CACHE) { - $cacheFile = MINIFY_CACHE_DIR.'/minify_'.$this->getHash(); - @file_put_contents($cacheFile, $combined, LOCK_EX); - } - - return $combined; - } - - /** - * Gets an array of absolute pathnames of all files that have been added with - * addFile() or via this class's constructor. - * - * @return array array of absolute pathnames - * @see addFile() - * @see removeFile() - */ - public function getFiles() { - return $this->files; - } - - /** - * Gets the MD5 hash of the concatenated filenames from the list of files to - * be minified. - */ - public function getHash() { - return hash('md5', implode('', $this->files)); - } - - /** - * Gets the timestamp of the most recently modified file. - * - * @return int timestamp - */ - public function getLastModified() { - $lastModified = 0; - - // Get the timestamp of the most recently modified file. - foreach($this->files as $file) { - $modified = filemtime($file); - - if ($modified !== false && $modified > $lastModified) { - $lastModified = $modified; - } - } - - return $lastModified; - } - - /** - * Removes the specified filename or array of filenames from the list of files - * to be minified. - * - * @param array|string $files filename or array of filenames - * @see addFile() - * @see getFiles() - */ - public function removeFile($files) { - $files = @array_map(array($this, 'resolveFilePath'), (array) $files); - $this->files = array_diff($this->files, $files); - } - - /** - * Attempts to serve the combined, minified files from the server's disk-based - * cache if possible. - * - * @param bool $return return cached content as a string instead of outputting - * it to the client - * @return bool|string - */ - public function serverCache($return = false) { - $cacheFile = MINIFY_CACHE_DIR.'/minify_'.$this->getHash(); - $lastModified = $this->getLastModified(); - - if (is_file($cacheFile) && $lastModified <= filemtime($cacheFile)) { - if ($return) { - return file_get_contents($cacheFile); - } - else { - echo file_get_contents($cacheFile); - exit; - } - } - - - return false; - } - - // -- Protected Instance Methods --------------------------------------------- - - /** - * Returns the canonicalized absolute pathname to the specified file or local - * URL. - * - * @param string $file relative file path - * @return string canonicalized absolute pathname - */ - protected function resolveFilePath($file) { - // Is this a URL? - if (preg_match('/^https?:\/\//i', $file)) { - if (!$parsedUrl = parse_url($file)) { - throw new MinifyInvalidUrlException("Invalid URL: $file"); - } - - // Does the server name match the local server name? - if (!isset($parsedUrl['host']) || - $parsedUrl['host'] != $_SERVER['SERVER_NAME']) { - throw new MinifyInvalidUrlException('Non-local URL not supported: '. - $file); - } - - // Get the file's absolute path. - $filepath = realpath(MINIFY_BASE_DIR.$parsedUrl['path']); - } - else { - // Get the file's absolute path. - $filepath = realpath(MINIFY_BASE_DIR.'/'.$file); - } - - // Ensure that the file exists, that the path is under the base directory, - // that the file's extension is either '.css' or '.js', and that the file is - // actually readable. - if (!$filepath || - !is_file($filepath) || - !is_readable($filepath) || - !preg_match('/^'.preg_quote(MINIFY_BASE_DIR, '/').'/', $filepath) || - !preg_match('/\.(?:css|js)$/iD', $filepath)) { - - // Even when the file exists, we still throw a - // MinifyFileNotFoundException in order to try to prevent an information - // disclosure vulnerability. - throw new MinifyFileNotFoundException("File not found: $file"); - } - - return $filepath; - } -} - -// -- Exception Classes -------------------------------------------------------- -class MinifyException extends Exception {} -class MinifyFileNotFoundException extends MinifyException {} -class MinifyInvalidArgumentException extends MinifyException {} -class MinifyInvalidUrlException extends MinifyException {} - -// -- Global Scope ------------------------------------------------------------- -if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) { - Minify::handleRequest(); -} + +* and by the article "Supercharged JavaScript" by Patrick Hunlock +* . +* +* JSMin was originally written by Douglas Crockford . +* +* Requires PHP 5.2.1+. +* +* @package Minify +* @author Ryan Grove +* @copyright 2007 Ryan Grove. All rights reserved. +* @license http://opensource.org/licenses/bsd-license.php New BSD License +* @version 1.1.0 (?) +* @link http://code.google.com/p/minify/ +*/ + +if (!defined('MINIFY_BASE_DIR')) { + /** + * Base path from which all relative file paths should be resolved. By default + * this is set to the document root. + */ + define('MINIFY_BASE_DIR', realpath($_SERVER['DOCUMENT_ROOT'])); +} + +if (!defined('MINIFY_CACHE_DIR')) { + /** Directory where compressed files will be cached. */ + define('MINIFY_CACHE_DIR', sys_get_temp_dir()); +} + +if (!defined('MINIFY_ENCODING')) { + /** Character set to use when outputting the minified files. */ + define('MINIFY_ENCODING', 'utf-8'); +} + +if (!defined('MINIFY_MAX_FILES')) { + /** Maximum number of files to combine in one request. */ + define('MINIFY_MAX_FILES', 16); +} + +if (!defined('MINIFY_REWRITE_CSS_URLS')) { + /** + * Whether or not Minify should attempt to rewrite relative URLs used in CSS + * files so that they continue to point to the correct location after the file + * is combined and minified. + * + * Minify is pretty good at getting this right, but occasionally it can make + * mistakes. If you find that URL rewriting results in problems, you should + * disable it. + */ + define('MINIFY_REWRITE_CSS_URLS', true); +} + +if (!defined('MINIFY_USE_CACHE')) { + /** + * Whether or not Minify should use a disk-based cache to increase + * performance. + */ + define('MINIFY_USE_CACHE', true); +} + +/** +* Minify is a library for combining, minifying, and caching JavaScript and CSS +* files on demand before sending them to a web browser. +* +* @package Minify +* @author Ryan Grove +* @copyright 2007 Ryan Grove. All rights reserved. +* @license http://opensource.org/licenses/bsd-license.php New BSD License +* @version 1.1.0 (?) +* @link http://code.google.com/p/minify/ +*/ +class Minify { + const TYPE_CSS = 'text/css'; + const TYPE_HTML = 'text/html'; + const TYPE_JS = 'text/javascript'; + + protected $files = array(); + protected $type = self::TYPE_JS; + + // -- Public Static Methods -------------------------------------------------- + + /** + * Combines, minifies, and outputs the requested files. + * + * Inspects the $_GET array for a 'files' entry containing a comma-separated + * list and uses this as the set of files to be combined and minified. + */ + public static function handleRequest() { + // 404 if no files were requested. + if (!isset($_GET['files'])) { + header('HTTP/1.0 404 Not Found'); + exit; + } + + $files = array_map('trim', explode(',', $_GET['files'], MINIFY_MAX_FILES)); + + // 404 if the $files array is empty for some weird reason. + if (!count($files)) { + header('HTTP/1.0 404 Not Found'); + exit; + } + + // Determine the content type based on the extension of the first file + // requested. + if (preg_match('/\.js$/iD', $files[0])) { + $type = self::TYPE_JS; + } else if (preg_match('/\.css$/iD', $files[0])) { + $type = self::TYPE_CSS; + } else { + $type = self::TYPE_HTML; + } + + // Minify and spit out the result. + try { + $minify = new Minify($type, $files); + + header("Content-Type: $type;charset=".MINIFY_ENCODING); + + $minify->browserCache(); + echo $minify->combine(); + exit; + } + catch (MinifyException $e) { + header('HTTP/1.0 404 Not Found'); + echo htmlentities($e->getMessage()); + exit; + } + } + + /** + * Minifies the specified string and returns it. + * + * @param string $string JavaScript, CSS, or HTML string to minify + * @param string $type content type of the string (Minify::TYPE_CSS, + * Minify::TYPE_HTML, or Minify::TYPE_JS) + * @return string minified string + */ + public static function min($string, $type = self::TYPE_JS) { + switch ($type) { + case self::TYPE_CSS: + return self::minifyCSS($string); + break; + + case self::TYPE_HTML: + return self::minifyHTML($string); + break; + + case self::TYPE_JS: + return self::minifyJS($string); + break; + } + + return $string; + } + + // -- Protected Static Methods ----------------------------------------------- + + /** + * Minifies the specified CSS string and returns it. + * + * @param string $css CSS string + * @return string minified string + * @see minify() + * @see minifyJS() + */ + protected static function minifyCSS($css) { + // Compress whitespace. + $css = preg_replace('/\s+/', ' ', $css); + + // Remove comments. + $css = preg_replace('/\/\*.*?\*\//', '', $css); + + return trim($css); + } + + protected static function minifyHTML($html) { + require_once dirname(__FILE__).'/lib/htmlmin.php'; + return HTMLMin::minify($html); + } + + /** + * Minifies the specified JavaScript string and returns it. + * + * @param string $js JavaScript string + * @return string minified string + * @see minify() + * @see minifyCSS() + */ + protected static function minifyJS($js) { + require_once dirname(__FILE__).'/../../../min/lib/JSMin.php'; + return JSMin::minify($js); + } + + /** + * Rewrites relative URLs in the specified CSS string to point to the correct + * location. URLs are assumed to be relative to the absolute path specified in + * the $path parameter. + * + * @param string $css CSS string + * @param string $path absolute path to which URLs are relative (should be a + * directory, not a file) + * @return string CSS string with rewritten URLs + */ + protected static function rewriteCSSUrls($css, $path) { + /* + Parentheses, commas, whitespace chars, single quotes, and double quotes are + escaped with a backslash as described in the CSS spec: + http://www.w3.org/TR/REC-CSS1#url + */ + $relativePath = preg_replace('/([\(\),\s\'"])/', '\\\$1', + str_replace(MINIFY_BASE_DIR, '', $path)); + + + return preg_replace('/url\(\s*[\'"]?\/?(.+?)[\'"]?\s*\)/i', 'url('. + $relativePath.'/$1)', $css); + } + + // -- Public Instance Methods ------------------------------------------------ + + /** + * Instantiates a new Minify object. A filename can be in the form of a + * relative path or a URL that resolves to the same site that hosts Minify. + * + * @param string $type content type of the specified files (either + * Minify::TYPE_CSS or Minify::TYPE_JS) + * @param array|string $files filename or array of filenames to be minified + */ + public function __construct($type = self::TYPE_JS, $files = array()) { + if ($type !== self::TYPE_JS && $type !== self::TYPE_CSS) { + throw new MinifyInvalidArgumentException('Invalid argument ($type): '. + $type); + } + + $this->type = $type; + + if (count((array) $files)) { + $this->addFile($files); + } + } + + /** + * Adds the specified filename or array of filenames to the list of files to + * be minified. A filename can be in the form of a relative path or a URL + * that resolves to the same site that hosts Minify. + * + * @param array|string $files filename or array of filenames + * @see getFiles() + * @see removeFile() + */ + public function addFile($files) { + $files = @array_map(array($this, 'resolveFilePath'), (array) $files); + $this->files = array_unique(array_merge($this->files, $files)); + } + + /** + * Attempts to serve the combined, minified files from the cache if possible. + * + * This method first checks the ETag value and If-Modified-Since timestamp + * sent by the browser and exits with an HTTP "304 Not Modified" response if + * the requested files haven't changed since they were last sent to the + * client. + * + * If the browser hasn't cached the content, we check to see if it's been + * cached on the server and, if so, we send the cached content and exit. + * + * If neither the client nor the server has the content in its cache, we don't + * do anything. + * + * @return bool + */ + public function browserCache() { + $hash = $this->getHash(); + $lastModified = $this->getLastModified(); + + $lastModifiedGMT = gmdate('D, d M Y H:i:s', $lastModified).' GMT'; + + // Check/set the ETag. + $etag = $hash.'_'.$lastModified; + + if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + if (strpos($_SERVER['HTTP_IF_NONE_MATCH'], $etag) !== false) { + header("Last-Modified: $lastModifiedGMT", true, 304); + exit; + } + } + + header('ETag: "'.$etag.'"'); + + // Check If-Modified-Since. + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + if ($lastModified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + header("Last-Modified: $lastModifiedGMT", true, 304); + exit; + } + } + + header("Last-Modified: $lastModifiedGMT"); + + return false; + } + + /** + * Combines and returns the contents of all files that have been added with + * addFile() or via this class's constructor. + * + * If MINIFY_USE_CACHE is true, the content will be returned from the server's + * cache if the cache is up to date; otherwise the new content will be saved + * to the cache for future use. + * + * @param bool $minify minify the combined contents before returning them + * @return string combined file contents + */ + public function combine($minify = true) { + // Return contents from server cache if possible. + if (MINIFY_USE_CACHE) { + if ($cacheResult = $this->serverCache(true)) { + return $cacheResult; + } + } + + // Combine contents. + $combined = array(); + + foreach($this->files as $file) { + if ($this->type === self::TYPE_CSS && MINIFY_REWRITE_CSS_URLS) { + // Rewrite relative CSS URLs. + $combined[] = self::rewriteCSSUrls(file_get_contents($file), + dirname($file)); + } + else { + $combined[] = file_get_contents($file); + } + } + + $combined = $minify ? self::min(implode("\n", $combined), $this->type) : + implode("\n", $combined); + + // Save combined contents to the cache. + if (MINIFY_USE_CACHE) { + $cacheFile = MINIFY_CACHE_DIR.'/minify_'.$this->getHash(); + @file_put_contents($cacheFile, $combined, LOCK_EX); + } + + return $combined; + } + + /** + * Gets an array of absolute pathnames of all files that have been added with + * addFile() or via this class's constructor. + * + * @return array array of absolute pathnames + * @see addFile() + * @see removeFile() + */ + public function getFiles() { + return $this->files; + } + + /** + * Gets the MD5 hash of the concatenated filenames from the list of files to + * be minified. + */ + public function getHash() { + return hash('md5', implode('', $this->files)); + } + + /** + * Gets the timestamp of the most recently modified file. + * + * @return int timestamp + */ + public function getLastModified() { + $lastModified = 0; + + // Get the timestamp of the most recently modified file. + foreach($this->files as $file) { + $modified = filemtime($file); + + if ($modified !== false && $modified > $lastModified) { + $lastModified = $modified; + } + } + + return $lastModified; + } + + /** + * Removes the specified filename or array of filenames from the list of files + * to be minified. + * + * @param array|string $files filename or array of filenames + * @see addFile() + * @see getFiles() + */ + public function removeFile($files) { + $files = @array_map(array($this, 'resolveFilePath'), (array) $files); + $this->files = array_diff($this->files, $files); + } + + /** + * Attempts to serve the combined, minified files from the server's disk-based + * cache if possible. + * + * @param bool $return return cached content as a string instead of outputting + * it to the client + * @return bool|string + */ + public function serverCache($return = false) { + $cacheFile = MINIFY_CACHE_DIR.'/minify_'.$this->getHash(); + $lastModified = $this->getLastModified(); + + if (is_file($cacheFile) && $lastModified <= filemtime($cacheFile)) { + if ($return) { + return file_get_contents($cacheFile); + } + else { + echo file_get_contents($cacheFile); + exit; + } + } + + + return false; + } + + // -- Protected Instance Methods --------------------------------------------- + + /** + * Returns the canonicalized absolute pathname to the specified file or local + * URL. + * + * @param string $file relative file path + * @return string canonicalized absolute pathname + */ + protected function resolveFilePath($file) { + // Is this a URL? + if (preg_match('/^https?:\/\//i', $file)) { + if (!$parsedUrl = parse_url($file)) { + throw new MinifyInvalidUrlException("Invalid URL: $file"); + } + + // Does the server name match the local server name? + if (!isset($parsedUrl['host']) || + $parsedUrl['host'] != $_SERVER['SERVER_NAME']) { + throw new MinifyInvalidUrlException('Non-local URL not supported: '. + $file); + } + + // Get the file's absolute path. + $filepath = realpath(MINIFY_BASE_DIR.$parsedUrl['path']); + } + else { + // Get the file's absolute path. + $filepath = realpath(MINIFY_BASE_DIR.'/'.$file); + } + + // Ensure that the file exists, that the path is under the base directory, + // that the file's extension is either '.css' or '.js', and that the file is + // actually readable. + if (!$filepath || + !is_file($filepath) || + !is_readable($filepath) || + !preg_match('/^'.preg_quote(MINIFY_BASE_DIR, '/').'/', $filepath) || + !preg_match('/\.(?:css|js)$/iD', $filepath)) { + + // Even when the file exists, we still throw a + // MinifyFileNotFoundException in order to try to prevent an information + // disclosure vulnerability. + throw new MinifyFileNotFoundException("File not found: $file"); + } + + return $filepath; + } +} + +// -- Exception Classes -------------------------------------------------------- +class MinifyException extends Exception {} +class MinifyFileNotFoundException extends MinifyException {} +class MinifyInvalidArgumentException extends MinifyException {} +class MinifyInvalidUrlException extends MinifyException {} + +// -- Global Scope ------------------------------------------------------------- +if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) { + Minify::handleRequest(); +} diff --git a/web/config.php b/web/config.php index 6aec3b1..e9ed391 100644 --- a/web/config.php +++ b/web/config.php @@ -1,17 +1,17 @@ - array( - "{$base}/jquery-1.2.3.js" - ,"{$base}/test space.js" - ) - ,'css' => array("{$base}/test.css") -); + array( + "{$base}/jquery-1.2.3.js" + ,"{$base}/test space.js" + ) + ,'css' => array("{$base}/test.css") +); unset($base); \ No newline at end of file diff --git a/web/examples/1/m.php b/web/examples/1/m.php index c9900b9..84c8277 100644 --- a/web/examples/1/m.php +++ b/web/examples/1/m.php @@ -1,14 +1,14 @@ - $groupsSources - ,'setExpires' => time() + 86400 * 365 -)); + $groupsSources + ,'setExpires' => time() + 86400 * 365 +)); diff --git a/web/examples/2/_groupsSources.php b/web/examples/2/_groupsSources.php index 7ab4c94..ae33a03 100644 --- a/web/examples/2/_groupsSources.php +++ b/web/examples/2/_groupsSources.php @@ -1,11 +1,11 @@ - array( - "{$base}/jquery-1.2.3.js" - ,"{$base}/test space.js" - ) - ,'css' => array("{$base}/test.css") -); + array( + "{$base}/jquery-1.2.3.js" + ,"{$base}/test space.js" + ) + ,'css' => array("{$base}/test.css") +); unset($base); \ No newline at end of file diff --git a/web/examples/2/m.php b/web/examples/2/m.php index c9900b9..84c8277 100644 --- a/web/examples/2/m.php +++ b/web/examples/2/m.php @@ -1,14 +1,14 @@ - $groupsSources - ,'setExpires' => time() + 86400 * 365 -)); + $groupsSources + ,'setExpires' => time() + 86400 * 365 +)); diff --git a/web/examples/3/m.php b/web/examples/3/m.php index a4c1fff..8918a4a 100644 --- a/web/examples/3/m.php +++ b/web/examples/3/m.php @@ -1,44 +1,44 @@ - dirname(__FILE__) . '/../' . $filename - )); - exit(); - } -} - -header("HTTP/1.0 404 Not Found"); + dirname(__FILE__) . '/../' . $filename + )); + exit(); + } +} + +header("HTTP/1.0 404 Not Found"); echo "HTTP/1.0 404 Not Found"; \ No newline at end of file diff --git a/web/test/HTTP_ConditionalGet/2.php b/web/test/HTTP_ConditionalGet/2.php index e258db3..02937fa 100644 --- a/web/test/HTTP_ConditionalGet/2.php +++ b/web/test/HTTP_ConditionalGet/2.php @@ -1,44 +1,44 @@ - $lastModified -)); -if ($cg->cacheIsValid) { - $cg->sendHeaders(); - // we're done - exit(); -} - -// generate content -$title = 'Last-Modified is known : add Content-Length'; -$explain = ' -

Here, like the first example, we know the Last-Modified time, -but we also want to set the Content-Length to increase cacheability and allow -HTTP persistent connections. Instead of sending headers immediately, we first -generate our content, then use setContentLength(strlen($content)) -to add the header. Then finally call sendHeaders() and send the -content.

-

Note: This is not required if your PHP config buffers all -output and your script doesn\'t do any incremental flushing of the output -buffer. PHP will generally set Content-Length for you if it can.

-

This script emulates a document that changes every ' .$every. ' seconds. -
This is version: ' . date('r', $lastModified) . '

-'; - -require '_include.php'; -$content = get_content(array( - 'title' => $title - ,'explain' => $explain -)); - -$cg->setContentLength(strlen($content)); -$cg->sendHeaders(); -send_slowly($content); - + $lastModified +)); +if ($cg->cacheIsValid) { + $cg->sendHeaders(); + // we're done + exit(); +} + +// generate content +$title = 'Last-Modified is known : add Content-Length'; +$explain = ' +

Here, like the first example, we know the Last-Modified time, +but we also want to set the Content-Length to increase cacheability and allow +HTTP persistent connections. Instead of sending headers immediately, we first +generate our content, then use setContentLength(strlen($content)) +to add the header. Then finally call sendHeaders() and send the +content.

+

Note: This is not required if your PHP config buffers all +output and your script doesn\'t do any incremental flushing of the output +buffer. PHP will generally set Content-Length for you if it can.

+

This script emulates a document that changes every ' .$every. ' seconds. +
This is version: ' . date('r', $lastModified) . '

+'; + +require '_include.php'; +$content = get_content(array( + 'title' => $title + ,'explain' => $explain +)); + +$cg->setContentLength(strlen($content)); +$cg->sendHeaders(); +send_slowly($content); + diff --git a/web/test/HTTP_ConditionalGet/5.php b/web/test/HTTP_ConditionalGet/5.php index 315e3ce..ffa82c3 100644 --- a/web/test/HTTP_ConditionalGet/5.php +++ b/web/test/HTTP_ConditionalGet/5.php @@ -1,27 +1,27 @@ - 20 - ,'lastModifiedTime' => filemtime(__FILE__) -)); -$cg->sendHeaders(); - -// generate, send content -$title = 'Last-Modified + Expires'; -$explain = ' -

Here we set a static "lastModifiedTime" and "maxAge" to 20. The browser -will consider this document fresh for 20 seconds, then revalidate its cache. After -the 304 response, the cache will be good for another 20 seconds. Unless you force -a reload, there will only be 304 responses for this page after the initial download. -'; - -require '_include.php'; -echo get_content(array( - 'title' => $title - ,'explain' => $explain -)); - + 20 + ,'lastModifiedTime' => filemtime(__FILE__) +)); +$cg->sendHeaders(); + +// generate, send content +$title = 'Last-Modified + Expires'; +$explain = ' +

Here we set a static "lastModifiedTime" and "maxAge" to 20. The browser +will consider this document fresh for 20 seconds, then revalidate its cache. After +the 304 response, the cache will be good for another 20 seconds. Unless you force +a reload, there will only be 304 responses for this page after the initial download. +'; + +require '_include.php'; +echo get_content(array( + 'title' => $title + ,'explain' => $explain +)); + diff --git a/web/test/_inc.php b/web/test/_inc.php index c6bd553..f583952 100644 --- a/web/test/_inc.php +++ b/web/test/_inc.php @@ -1,30 +1,28 @@ -0, 'fail'=>0, 'total'=>0); - - $mode = $test ? 'pass' : 'fail'; - $outMode = $test ? 'PASS' : '!FAIL'; - printf("%s: %s (%d of %d tests run so far have %sed)\n", - $outMode, $message, ++$count[$mode], ++$count['total'], $mode); - - return (bool)$test; -} - -?> \ No newline at end of file +0, 'fail'=>0, 'total'=>0); + + $mode = $test ? 'pass' : 'fail'; + $outMode = $test ? 'PASS' : '!FAIL'; + printf("%s: %s (%d of %d tests run so far have %sed)\n", + $outMode, $message, ++$count[$mode], ++$count['total'], $mode); + + return (bool)$test; +} diff --git a/web/test/test_CSS.php b/web/test/test_CSS.php index ff55d29..1b4841e 100644 --- a/web/test/test_CSS.php +++ b/web/test/test_CSS.php @@ -1,42 +1,42 @@ -read())) { - if (preg_match('/^([\w\\-]+)\.css$/', $entry, $m)) { - $list[] = $m[1]; - } - } - $d->close(); - - foreach ($list as $item) { - - $options = ($item === 'paths') - ? array('prependRelativePath' => '../') - : array(); - - $src = file_get_contents($cssPath . "/{$item}.css"); - $minExpected = file_get_contents($cssPath . "/{$item}.min.css"); - $minOutput = Minify_CSS::minify($src, $options); - $passed = assertTrue($minExpected === $minOutput, 'Minify_CSS : ' . $item); - - 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"; - echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; - } - } - } -} - -test_CSS(); +read())) { + if (preg_match('/^([\w\\-]+)\.css$/', $entry, $m)) { + $list[] = $m[1]; + } + } + $d->close(); + + foreach ($list as $item) { + + $options = ($item === 'paths') + ? array('prependRelativePath' => '../') + : array(); + + $src = file_get_contents($cssPath . "/{$item}.css"); + $minExpected = file_get_contents($cssPath . "/{$item}.min.css"); + $minOutput = Minify_CSS::minify($src, $options); + $passed = assertTrue($minExpected === $minOutput, 'Minify_CSS : ' . $item); + + 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"; + echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n"; + } + } + } +} + +test_CSS(); diff --git a/web/test/test_HTML.php b/web/test/test_HTML.php index 9cfeb9f..1159044 100644 --- a/web/test/test_HTML.php +++ b/web/test/test_HTML.php @@ -1,59 +1,59 @@ - array('Minify_CSS', 'minify') - ,'jsMinifier' => array('Minify_Javascript', 'minify') - )); - $time = microtime(true) - $time; - - $passed = assertTrue($minExpected === $minOutput, 'Minify_HTML'); - - if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { - if ($passed) { - echo "\n---Source: ", strlen($src), " bytes\n" - , "---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n\n"; - } else { - echo "\n---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n" - , "---Expected: ", strlen($minExpected), " bytes\n\n{$minExpected}\n\n" - , "---Source: ", strlen($src), " bytes\n\n{$src}\n\n\n"; - } - } - - $src = file_get_contents($thisDir . '/_test_files/html/before2.html'); - $minExpected = file_get_contents($thisDir . '/_test_files/html/before2.min.html'); - - $time = microtime(true); - $minOutput = Minify_HTML::minify($src, array( - 'cssMinifier' => array('Minify_CSS', 'minify') - ,'jsMinifier' => array('Minify_Javascript', 'minify') - )); - $time = microtime(true) - $time; - - $passed = assertTrue($minExpected === $minOutput, 'Minify_HTML'); - - if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { - if ($passed) { - echo "\n---Source: ", strlen($src), " bytes\n" - , "---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n\n"; - } else { - echo "\n---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n" - , "---Expected: ", strlen($minExpected), " bytes\n\n{$minExpected}\n\n" - , "---Source: ", strlen($src), " bytes\n\n{$src}\n\n\n"; - } - } -} - -test_HTML(); + array('Minify_CSS', 'minify') + ,'jsMinifier' => array('Minify_Javascript', 'minify') + )); + $time = microtime(true) - $time; + + $passed = assertTrue($minExpected === $minOutput, 'Minify_HTML'); + + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { + if ($passed) { + echo "\n---Source: ", strlen($src), " bytes\n" + , "---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n\n"; + } else { + echo "\n---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n" + , "---Expected: ", strlen($minExpected), " bytes\n\n{$minExpected}\n\n" + , "---Source: ", strlen($src), " bytes\n\n{$src}\n\n\n"; + } + } + + $src = file_get_contents($thisDir . '/_test_files/html/before2.html'); + $minExpected = file_get_contents($thisDir . '/_test_files/html/before2.min.html'); + + $time = microtime(true); + $minOutput = Minify_HTML::minify($src, array( + 'cssMinifier' => array('Minify_CSS', 'minify') + ,'jsMinifier' => array('Minify_Javascript', 'minify') + )); + $time = microtime(true) - $time; + + $passed = assertTrue($minExpected === $minOutput, 'Minify_HTML'); + + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { + if ($passed) { + echo "\n---Source: ", strlen($src), " bytes\n" + , "---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n\n"; + } else { + echo "\n---Output: ", strlen($minOutput), " bytes (", round($time * 1000), " ms)\n\n{$minOutput}\n\n" + , "---Expected: ", strlen($minExpected), " bytes\n\n{$minExpected}\n\n" + , "---Source: ", strlen($src), " bytes\n\n{$src}\n\n\n"; + } + } +} + +test_HTML(); diff --git a/web/test/test_HTTP_ConditionalGet.php b/web/test/test_HTTP_ConditionalGet.php index 5a73b8a..65cb521 100644 --- a/web/test/test_HTTP_ConditionalGet.php +++ b/web/test/test_HTTP_ConditionalGet.php @@ -1,117 +1,117 @@ - 'client has valid If-Modified-Since' - ,'inm' => null - ,'ims' => $gmtTime - ,'exp' => array( - 'Last-Modified' => $gmtTime - ,'ETag' => "\"{$lmTime}pri\"" - ,'Cache-Control' => 'max-age=0, private, must-revalidate' - ,'_responseCode' => 'HTTP/1.0 304 Not Modified' - ,'isValid' => true - ) - ) - ,array( - 'desc' => 'client has valid If-Modified-Since with trailing semicolon' - ,'inm' => null - ,'ims' => $gmtTime . ';' - ,'exp' => array( - 'Last-Modified' => $gmtTime - ,'ETag' => "\"{$lmTime}pri\"" - ,'Cache-Control' => 'max-age=0, private, must-revalidate' - ,'_responseCode' => 'HTTP/1.0 304 Not Modified' - ,'isValid' => true - ) - ) - ,array( - 'desc' => 'client has valid ETag' - ,'inm' => "\"badEtagFoo\", \"{$lmTime}pri\"" - ,'ims' => null - ,'exp' => array( - 'Last-Modified' => $gmtTime - ,'ETag' => "\"{$lmTime}pri\"" - ,'Cache-Control' => 'max-age=0, private, must-revalidate' - ,'_responseCode' => 'HTTP/1.0 304 Not Modified' - ,'isValid' => true - ) - ) - ,array( - 'desc' => 'no conditional get' - ,'inm' => null - ,'ims' => null - ,'exp' => array( - 'Last-Modified' => $gmtTime - ,'ETag' => "\"{$lmTime}pri\"" - ,'Cache-Control' => 'max-age=0, private, must-revalidate' - ,'isValid' => false - ) - ) - ,array( - 'desc' => 'client has invalid ETag' - ,'inm' => '"' . ($lmTime - 300) . 'pri"' - ,'ims' => null - ,'exp' => array( - 'Last-Modified' => $gmtTime - ,'ETag' => "\"{$lmTime}pri\"" - ,'Cache-Control' => 'max-age=0, private, must-revalidate' - ,'isValid' => false - ) - ) - ,array( - 'desc' => 'client has invalid If-Modified-Since' - ,'inm' => null - ,'ims' => gmdate('D, d M Y H:i:s \G\M\T', $lmTime - 300) - ,'exp' => array( - 'Last-Modified' => $gmtTime - ,'ETag' => "\"{$lmTime}pri\"" - ,'Cache-Control' => 'max-age=0, private, must-revalidate' - ,'isValid' => false - ) - ) - ); - - foreach ($tests as $test) { - // setup env - if (null === $test['inm']) { - unset($_SERVER['HTTP_IF_NONE_MATCH']); - } else { - $_SERVER['HTTP_IF_NONE_MATCH'] = get_magic_quotes_gpc() - ? addslashes($test['inm']) - : $test['inm'];; - } - if (null === $test['ims']) { - unset($_SERVER['HTTP_IF_MODIFIED_SINCE']); - } else { - $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $test['ims']; - } - $exp = $test['exp']; - - $cg = new HTTP_ConditionalGet(array( - 'lastModifiedTime' => $lmTime - )); - $ret = $cg->getHeaders(); - $ret['isValid'] = $cg->cacheIsValid; - - $passed = assertTrue($exp == $ret, 'HTTP_ConditionalGet : ' . $test['desc']); - - 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"; - } - } -} - + 'client has valid If-Modified-Since' + ,'inm' => null + ,'ims' => $gmtTime + ,'exp' => array( + 'Last-Modified' => $gmtTime + ,'ETag' => "\"{$lmTime}pri\"" + ,'Cache-Control' => 'max-age=0, private, must-revalidate' + ,'_responseCode' => 'HTTP/1.0 304 Not Modified' + ,'isValid' => true + ) + ) + ,array( + 'desc' => 'client has valid If-Modified-Since with trailing semicolon' + ,'inm' => null + ,'ims' => $gmtTime . ';' + ,'exp' => array( + 'Last-Modified' => $gmtTime + ,'ETag' => "\"{$lmTime}pri\"" + ,'Cache-Control' => 'max-age=0, private, must-revalidate' + ,'_responseCode' => 'HTTP/1.0 304 Not Modified' + ,'isValid' => true + ) + ) + ,array( + 'desc' => 'client has valid ETag' + ,'inm' => "\"badEtagFoo\", \"{$lmTime}pri\"" + ,'ims' => null + ,'exp' => array( + 'Last-Modified' => $gmtTime + ,'ETag' => "\"{$lmTime}pri\"" + ,'Cache-Control' => 'max-age=0, private, must-revalidate' + ,'_responseCode' => 'HTTP/1.0 304 Not Modified' + ,'isValid' => true + ) + ) + ,array( + 'desc' => 'no conditional get' + ,'inm' => null + ,'ims' => null + ,'exp' => array( + 'Last-Modified' => $gmtTime + ,'ETag' => "\"{$lmTime}pri\"" + ,'Cache-Control' => 'max-age=0, private, must-revalidate' + ,'isValid' => false + ) + ) + ,array( + 'desc' => 'client has invalid ETag' + ,'inm' => '"' . ($lmTime - 300) . 'pri"' + ,'ims' => null + ,'exp' => array( + 'Last-Modified' => $gmtTime + ,'ETag' => "\"{$lmTime}pri\"" + ,'Cache-Control' => 'max-age=0, private, must-revalidate' + ,'isValid' => false + ) + ) + ,array( + 'desc' => 'client has invalid If-Modified-Since' + ,'inm' => null + ,'ims' => gmdate('D, d M Y H:i:s \G\M\T', $lmTime - 300) + ,'exp' => array( + 'Last-Modified' => $gmtTime + ,'ETag' => "\"{$lmTime}pri\"" + ,'Cache-Control' => 'max-age=0, private, must-revalidate' + ,'isValid' => false + ) + ) + ); + + foreach ($tests as $test) { + // setup env + if (null === $test['inm']) { + unset($_SERVER['HTTP_IF_NONE_MATCH']); + } else { + $_SERVER['HTTP_IF_NONE_MATCH'] = get_magic_quotes_gpc() + ? addslashes($test['inm']) + : $test['inm'];; + } + if (null === $test['ims']) { + unset($_SERVER['HTTP_IF_MODIFIED_SINCE']); + } else { + $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $test['ims']; + } + $exp = $test['exp']; + + $cg = new HTTP_ConditionalGet(array( + 'lastModifiedTime' => $lmTime + )); + $ret = $cg->getHeaders(); + $ret['isValid'] = $cg->cacheIsValid; + + $passed = assertTrue($exp == $ret, 'HTTP_ConditionalGet : ' . $test['desc']); + + 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"; + } + } +} + test_HTTP_ConditionalGet(); \ No newline at end of file diff --git a/web/test/test_HTTP_Encoder.php b/web/test/test_HTTP_Encoder.php index 996fba1..3a114ad 100644 --- a/web/test/test_HTTP_Encoder.php +++ b/web/test/test_HTTP_Encoder.php @@ -1,224 +1,224 @@ - 'Any browser' - ,'ae' => 'compress, x-gzip' - ,'exp' => array('gzip', 'x-gzip') - ,'desc' => 'recognize "x-gzip" as gzip' - ) - ,array( - 'ua' => 'Any browser' - ,'ae' => 'compress, x-gzip;q=0.5' - ,'exp' => array('gzip', 'x-gzip') - ,'desc' => 'gzip w/ non-zero q' - ) - ,array( - 'ua' => 'Any browser' - ,'ae' => 'compress, x-gzip;q=0' - ,'exp' => array('compress', 'compress') - ,'desc' => 'gzip w/ zero q' - ) - ,array( - 'ua' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)' - ,'ae' => 'gzip, deflate' - ,'exp' => array('', '') - ,'desc' => 'IE6 w/o "enhanced security"' - ) - ,array( - 'ua' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)' - ,'ae' => 'gzip, deflate' - ,'exp' => array('deflate', 'deflate') - ,'desc' => 'IE6 w/ "enhanced security"' - ) - ,array( - 'ua' => 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.01)' - ,'ae' => 'gzip, deflate' - ,'exp' => array('', '') - ,'desc' => 'IE5.5' - ) - ,array( - 'ua' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.25' - ,'ae' => 'gzip,deflate' - ,'exp' => array('deflate', 'deflate') - ,'desc' => 'Opera identifying as IE6' - ) - ); - - foreach ($methodTests as $test) { - $_SERVER['HTTP_USER_AGENT'] = $test['ua']; - $_SERVER['HTTP_ACCEPT_ENCODING'] = $test['ae']; - $exp = $test['exp']; - $ret = HTTP_Encoder::getAcceptedEncoding(); - $passed = assertTrue($exp == $ret, 'HTTP_Encoder : ' . $test['desc']); - - 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"; - } - } - - $variedContent = file_get_contents($thisDir . '/_test_files/html/before.html') - . file_get_contents($thisDir . '/_test_files/css/subsilver.css') - . file_get_contents($thisDir . '/../examples/jquery-1.2.3.js'); - $variedLength = strlen($variedContent); - - $encodingTests = array( - array('method' => 'deflate', 'inv' => 'gzinflate', 'exp' => 32157) - ,array('method' => 'gzip', 'inv' => '_gzdecode', 'exp' => 32175) - ,array('method' => 'compress', 'inv' => 'gzuncompress', 'exp' => 32211) - ); - - foreach ($encodingTests as $test) { - $e = new HTTP_Encoder(array( - 'content' => $variedContent - ,'method' => $test['method'] - )); - $e->encode(9); - $ret = strlen($e->getContent()); - - // test uncompression - $roundTrip = @call_user_func($test['inv'], $e->getContent()); - $desc = "HTTP_Encoder : {$test['method']} : uncompress possible"; - $passed = assertTrue($variedContent == $roundTrip, $desc); - - // test expected compressed size - $desc = "HTTP_Encoder : {$test['method']} : compressed to " - . sprintf('%4.2f%% of original', $ret/$variedLength*100); - $passed = assertTrue(abs($ret - $test['exp']) < 100, $desc); - if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { - echo "\n--- {$test['method']}: expected bytes: " - , "{$test['exp']}. Returned: {$ret} " - , "(off by ". abs($ret - $test['exp']) . " bytes)\n\n"; - } - } -} - -test_HTTP_Encoder(); - -function _gzdecode($data) -{ - $filename = $error = ''; - return _phpman_gzdecode($data, $filename, $error); -} - -// http://www.php.net/manual/en/function.gzdecode.php#82930 -function _phpman_gzdecode($data, &$filename='', &$error='', $maxlength=null) -{ - $len = strlen($data); - if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) { - $error = "Not in GZIP format."; - return null; // Not GZIP format (See RFC 1952) - } - $method = ord(substr($data,2,1)); // Compression method - $flags = ord(substr($data,3,1)); // Flags - if ($flags & 31 != $flags) { - $error = "Reserved bits not allowed."; - return null; - } - // NOTE: $mtime may be negative (PHP integer limitations) - $mtime = unpack("V", substr($data,4,4)); - $mtime = $mtime[1]; - $xfl = substr($data,8,1); - $os = substr($data,8,1); - $headerlen = 10; - $extralen = 0; - $extra = ""; - if ($flags & 4) { - // 2-byte length prefixed EXTRA data in header - if ($len - $headerlen - 2 < 8) { - return false; // invalid - } - $extralen = unpack("v",substr($data,8,2)); - $extralen = $extralen[1]; - if ($len - $headerlen - 2 - $extralen < 8) { - return false; // invalid - } - $extra = substr($data,10,$extralen); - $headerlen += 2 + $extralen; - } - $filenamelen = 0; - $filename = ""; - if ($flags & 8) { - // C-style string - if ($len - $headerlen - 1 < 8) { - return false; // invalid - } - $filenamelen = strpos(substr($data,$headerlen),chr(0)); - if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) { - return false; // invalid - } - $filename = substr($data,$headerlen,$filenamelen); - $headerlen += $filenamelen + 1; - } - $commentlen = 0; - $comment = ""; - if ($flags & 16) { - // C-style string COMMENT data in header - if ($len - $headerlen - 1 < 8) { - return false; // invalid - } - $commentlen = strpos(substr($data,$headerlen),chr(0)); - if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) { - return false; // Invalid header format - } - $comment = substr($data,$headerlen,$commentlen); - $headerlen += $commentlen + 1; - } - $headercrc = ""; - if ($flags & 2) { - // 2-bytes (lowest order) of CRC32 on header present - if ($len - $headerlen - 2 < 8) { - return false; // invalid - } - $calccrc = crc32(substr($data,0,$headerlen)) & 0xffff; - $headercrc = unpack("v", substr($data,$headerlen,2)); - $headercrc = $headercrc[1]; - if ($headercrc != $calccrc) { - $error = "Header checksum failed."; - return false; // Bad header CRC - } - $headerlen += 2; - } - // GZIP FOOTER - $datacrc = unpack("V",substr($data,-8,4)); - $datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF); - $isize = unpack("V",substr($data,-4)); - $isize = $isize[1]; - // decompression: - $bodylen = $len-$headerlen-8; - if ($bodylen < 1) { - // IMPLEMENTATION BUG! - return null; - } - $body = substr($data,$headerlen,$bodylen); - $data = ""; - if ($bodylen > 0) { - switch ($method) { - case 8: - // Currently the only supported compression method: - $data = gzinflate($body,$maxlength); - break; - default: - $error = "Unknown compression method."; - return false; - } - } // zero-byte body content is allowed - // Verifiy CRC32 - $crc = sprintf("%u",crc32($data)); - $crcOK = $crc == $datacrc; - $lenOK = $isize == strlen($data); - if (!$lenOK || !$crcOK) { - $error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.'); - return false; - } - return $data; + 'Any browser' + ,'ae' => 'compress, x-gzip' + ,'exp' => array('gzip', 'x-gzip') + ,'desc' => 'recognize "x-gzip" as gzip' + ) + ,array( + 'ua' => 'Any browser' + ,'ae' => 'compress, x-gzip;q=0.5' + ,'exp' => array('gzip', 'x-gzip') + ,'desc' => 'gzip w/ non-zero q' + ) + ,array( + 'ua' => 'Any browser' + ,'ae' => 'compress, x-gzip;q=0' + ,'exp' => array('compress', 'compress') + ,'desc' => 'gzip w/ zero q' + ) + ,array( + 'ua' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)' + ,'ae' => 'gzip, deflate' + ,'exp' => array('', '') + ,'desc' => 'IE6 w/o "enhanced security"' + ) + ,array( + 'ua' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)' + ,'ae' => 'gzip, deflate' + ,'exp' => array('deflate', 'deflate') + ,'desc' => 'IE6 w/ "enhanced security"' + ) + ,array( + 'ua' => 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.01)' + ,'ae' => 'gzip, deflate' + ,'exp' => array('', '') + ,'desc' => 'IE5.5' + ) + ,array( + 'ua' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.25' + ,'ae' => 'gzip,deflate' + ,'exp' => array('deflate', 'deflate') + ,'desc' => 'Opera identifying as IE6' + ) + ); + + foreach ($methodTests as $test) { + $_SERVER['HTTP_USER_AGENT'] = $test['ua']; + $_SERVER['HTTP_ACCEPT_ENCODING'] = $test['ae']; + $exp = $test['exp']; + $ret = HTTP_Encoder::getAcceptedEncoding(); + $passed = assertTrue($exp == $ret, 'HTTP_Encoder : ' . $test['desc']); + + 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"; + } + } + + $variedContent = file_get_contents($thisDir . '/_test_files/html/before.html') + . file_get_contents($thisDir . '/_test_files/css/subsilver.css') + . file_get_contents($thisDir . '/../examples/jquery-1.2.3.js'); + $variedLength = strlen($variedContent); + + $encodingTests = array( + array('method' => 'deflate', 'inv' => 'gzinflate', 'exp' => 32157) + ,array('method' => 'gzip', 'inv' => '_gzdecode', 'exp' => 32175) + ,array('method' => 'compress', 'inv' => 'gzuncompress', 'exp' => 32211) + ); + + foreach ($encodingTests as $test) { + $e = new HTTP_Encoder(array( + 'content' => $variedContent + ,'method' => $test['method'] + )); + $e->encode(9); + $ret = strlen($e->getContent()); + + // test uncompression + $roundTrip = @call_user_func($test['inv'], $e->getContent()); + $desc = "HTTP_Encoder : {$test['method']} : uncompress possible"; + $passed = assertTrue($variedContent == $roundTrip, $desc); + + // test expected compressed size + $desc = "HTTP_Encoder : {$test['method']} : compressed to " + . sprintf('%4.2f%% of original', $ret/$variedLength*100); + $passed = assertTrue(abs($ret - $test['exp']) < 100, $desc); + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { + echo "\n--- {$test['method']}: expected bytes: " + , "{$test['exp']}. Returned: {$ret} " + , "(off by ". abs($ret - $test['exp']) . " bytes)\n\n"; + } + } +} + +test_HTTP_Encoder(); + +function _gzdecode($data) +{ + $filename = $error = ''; + return _phpman_gzdecode($data, $filename, $error); +} + +// http://www.php.net/manual/en/function.gzdecode.php#82930 +function _phpman_gzdecode($data, &$filename='', &$error='', $maxlength=null) +{ + $len = strlen($data); + if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) { + $error = "Not in GZIP format."; + return null; // Not GZIP format (See RFC 1952) + } + $method = ord(substr($data,2,1)); // Compression method + $flags = ord(substr($data,3,1)); // Flags + if ($flags & 31 != $flags) { + $error = "Reserved bits not allowed."; + return null; + } + // NOTE: $mtime may be negative (PHP integer limitations) + $mtime = unpack("V", substr($data,4,4)); + $mtime = $mtime[1]; + $xfl = substr($data,8,1); + $os = substr($data,8,1); + $headerlen = 10; + $extralen = 0; + $extra = ""; + if ($flags & 4) { + // 2-byte length prefixed EXTRA data in header + if ($len - $headerlen - 2 < 8) { + return false; // invalid + } + $extralen = unpack("v",substr($data,8,2)); + $extralen = $extralen[1]; + if ($len - $headerlen - 2 - $extralen < 8) { + return false; // invalid + } + $extra = substr($data,10,$extralen); + $headerlen += 2 + $extralen; + } + $filenamelen = 0; + $filename = ""; + if ($flags & 8) { + // C-style string + if ($len - $headerlen - 1 < 8) { + return false; // invalid + } + $filenamelen = strpos(substr($data,$headerlen),chr(0)); + if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) { + return false; // invalid + } + $filename = substr($data,$headerlen,$filenamelen); + $headerlen += $filenamelen + 1; + } + $commentlen = 0; + $comment = ""; + if ($flags & 16) { + // C-style string COMMENT data in header + if ($len - $headerlen - 1 < 8) { + return false; // invalid + } + $commentlen = strpos(substr($data,$headerlen),chr(0)); + if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) { + return false; // Invalid header format + } + $comment = substr($data,$headerlen,$commentlen); + $headerlen += $commentlen + 1; + } + $headercrc = ""; + if ($flags & 2) { + // 2-bytes (lowest order) of CRC32 on header present + if ($len - $headerlen - 2 < 8) { + return false; // invalid + } + $calccrc = crc32(substr($data,0,$headerlen)) & 0xffff; + $headercrc = unpack("v", substr($data,$headerlen,2)); + $headercrc = $headercrc[1]; + if ($headercrc != $calccrc) { + $error = "Header checksum failed."; + return false; // Bad header CRC + } + $headerlen += 2; + } + // GZIP FOOTER + $datacrc = unpack("V",substr($data,-8,4)); + $datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF); + $isize = unpack("V",substr($data,-4)); + $isize = $isize[1]; + // decompression: + $bodylen = $len-$headerlen-8; + if ($bodylen < 1) { + // IMPLEMENTATION BUG! + return null; + } + $body = substr($data,$headerlen,$bodylen); + $data = ""; + if ($bodylen > 0) { + switch ($method) { + case 8: + // Currently the only supported compression method: + $data = gzinflate($body,$maxlength); + break; + default: + $error = "Unknown compression method."; + return false; + } + } // zero-byte body content is allowed + // Verifiy CRC32 + $crc = sprintf("%u",crc32($data)); + $crcOK = $crc == $datacrc; + $lenOK = $isize == strlen($data); + if (!$lenOK || !$crcOK) { + $error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.'); + return false; + } + return $data; } \ No newline at end of file diff --git a/web/test/test_Javascript.php b/web/test/test_Javascript.php index 7e74759..86fbfb2 100644 --- a/web/test/test_Javascript.php +++ b/web/test/test_Javascript.php @@ -1,37 +1,37 @@ - false - )); - - $passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript'); - - 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"; - } -} - -test_Javascript(); + false + )); + + $passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript'); + + 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"; + } +} + +test_Javascript(); diff --git a/web/test/test_Lines.php b/web/test/test_Lines.php index 0e7fd27..17ec39a 100644 --- a/web/test/test_Lines.php +++ b/web/test/test_Lines.php @@ -1,33 +1,33 @@ - true - ,'quiet' => true - ,'encodeOutput' => false - ,'files' => array( - "{$thisDir}/_test_files/minify/email.js" - ,"{$thisDir}/_test_files/minify/QueryString.js" - ,"{$thisDir}/_test_files/js/before.js" - ) - )); - - $passed = assertTrue($exp === $ret['content'], 'Minify_Lines'); - - if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { - echo "\n---Output: " .strlen($ret['content']). " bytes\n\n{$ret['content']}\n\n"; - if (!$passed) { - echo "---Expected: " .strlen($exp). " bytes\n\n{$exp}\n\n\n"; - } - } -} - + true + ,'quiet' => true + ,'encodeOutput' => false + ,'files' => array( + "{$thisDir}/_test_files/minify/email.js" + ,"{$thisDir}/_test_files/minify/QueryString.js" + ,"{$thisDir}/_test_files/js/before.js" + ) + )); + + $passed = assertTrue($exp === $ret['content'], 'Minify_Lines'); + + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { + echo "\n---Output: " .strlen($ret['content']). " bytes\n\n{$ret['content']}\n\n"; + if (!$passed) { + echo "---Expected: " .strlen($exp). " bytes\n\n{$exp}\n\n\n"; + } + } +} + test_Lines(); \ No newline at end of file diff --git a/web/test/test_Minify.php b/web/test/test_Minify.php index 7c709ab..d8a2be3 100644 --- a/web/test/test_Minify.php +++ b/web/test/test_Minify.php @@ -1,101 +1,101 @@ - true - ,'statusCode' => 304 - ,'content' => '', - 'headers' => array( - 'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $_SERVER['REQUEST_TIME'] + 1800), - 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), - 'ETag' => "\"{$lastModified}pub\"", - 'Cache-Control' => 'max-age=1800, public, must-revalidate', - '_responseCode' => 'HTTP/1.0 304 Not Modified', - ) - ); - $output = Minify::serve('Files', array( - 'files' => $thisDir . '/_test_files/css/styles.css' // controller casts to array - ,'quiet' => true - ,'lastModifiedTime' => $lastModified - ,'encodeOutput' => false - )); - $passed = assertTrue($expected === $output, 'Minify : 304 response'); - 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"; - } - } - - assertTrue( - //! class_exists('Cache_Lite_File', false) - ! class_exists('HTTP_Encoder', false) - && ! class_exists('Minify_CSS', false) - && ! class_exists('Minify_Cache', false) - ,'Encoder.php, CSS.php, Cache.php not loaded' - ); - - // Test minifying JS and serving with Expires header - - $content = preg_replace('/\\r\\n?/', "\n", file_get_contents($minifyTestPath . '/minified.js')); - $lastModified = filemtime($minifyTestPath . '/minified.js'); - $expected = array( - 'success' => true - ,'statusCode' => 200 - // Minify_Javascript always converts to \n line endings - ,'content' => $content - ,'headers' => array ( - 'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $tomorrow), - 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), - 'ETag' => "\"{$lastModified}pub\"", - 'Cache-Control' => 'max-age=86400, public, must-revalidate', - 'Content-Length' => strlen($content), - 'Content-Type' => 'application/x-javascript; charset=UTF-8', - ) - ); - $output = Minify::serve('Files', array( - 'files' => array( - $minifyTestPath . '/email.js' - ,$minifyTestPath . '/QueryString.js' - ) - ,'quiet' => true - ,'maxAge' => 86400 - ,'encodeOutput' => false - )); - $passed = assertTrue($expected === $output, 'Minify : JS and Expires'); - - 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"; - } - } - - // Test minifying CSS and responding with Etag/Last-Modified - - // needed to expose E_STRICT warning in Cache_Lite_File - Minify::setCache(); - - // don't allow conditional headers - unset($_SERVER['HTTP_IF_NONE_MATCH'], $_SERVER['HTTP_IF_MODIFIED_SINCE']); + true + ,'statusCode' => 304 + ,'content' => '', + 'headers' => array( + 'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $_SERVER['REQUEST_TIME'] + 1800), + 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), + 'ETag' => "\"{$lastModified}pub\"", + 'Cache-Control' => 'max-age=1800, public, must-revalidate', + '_responseCode' => 'HTTP/1.0 304 Not Modified', + ) + ); + $output = Minify::serve('Files', array( + 'files' => $thisDir . '/_test_files/css/styles.css' // controller casts to array + ,'quiet' => true + ,'lastModifiedTime' => $lastModified + ,'encodeOutput' => false + )); + $passed = assertTrue($expected === $output, 'Minify : 304 response'); + 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"; + } + } + + assertTrue( + //! class_exists('Cache_Lite_File', false) + ! class_exists('HTTP_Encoder', false) + && ! class_exists('Minify_CSS', false) + && ! class_exists('Minify_Cache', false) + ,'Encoder.php, CSS.php, Cache.php not loaded' + ); + + // Test minifying JS and serving with Expires header + + $content = preg_replace('/\\r\\n?/', "\n", file_get_contents($minifyTestPath . '/minified.js')); + $lastModified = filemtime($minifyTestPath . '/minified.js'); + $expected = array( + 'success' => true + ,'statusCode' => 200 + // Minify_Javascript always converts to \n line endings + ,'content' => $content + ,'headers' => array ( + 'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $tomorrow), + 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), + 'ETag' => "\"{$lastModified}pub\"", + 'Cache-Control' => 'max-age=86400, public, must-revalidate', + 'Content-Length' => strlen($content), + 'Content-Type' => 'application/x-javascript; charset=UTF-8', + ) + ); + $output = Minify::serve('Files', array( + 'files' => array( + $minifyTestPath . '/email.js' + ,$minifyTestPath . '/QueryString.js' + ) + ,'quiet' => true + ,'maxAge' => 86400 + ,'encodeOutput' => false + )); + $passed = assertTrue($expected === $output, 'Minify : JS and Expires'); + + 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"; + } + } + + // Test minifying CSS and responding with Etag/Last-Modified + + // needed to expose E_STRICT warning in Cache_Lite_File + Minify::setCache(); + + // don't allow conditional headers + unset($_SERVER['HTTP_IF_NONE_MATCH'], $_SERVER['HTTP_IF_MODIFIED_SINCE']); $pathToWebTest = str_replace( DIRECTORY_SEPARATOR @@ -107,37 +107,37 @@ function test_Minify() ,$pathToWebTest ,file_get_contents($minifyTestPath . '/minified.css') ); - - $expected = array( - 'success' => true - ,'statusCode' => 200 - ,'content' => $expectedContent - ,'headers' => array ( - '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' => strlen($expectedContent), - 'Content-Type' => 'text/css; charset=UTF-8', - ) - ); - $output = Minify::serve('Files', array( - 'files' => array( - $thisDir . '/_test_files/css/styles.css' - ,$thisDir . '/_test_files/css/subsilver.css' - ) - ,'quiet' => true - ,'lastModifiedTime' => $lastModified - ,'encodeOutput' => false + + $expected = array( + 'success' => true + ,'statusCode' => 200 + ,'content' => $expectedContent + ,'headers' => array ( + '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' => strlen($expectedContent), + 'Content-Type' => 'text/css; charset=UTF-8', + ) + ); + $output = Minify::serve('Files', array( + 'files' => array( + $thisDir . '/_test_files/css/styles.css' + ,$thisDir . '/_test_files/css/subsilver.css' + ) + ,'quiet' => true + ,'lastModifiedTime' => $lastModified + ,'encodeOutput' => false ,'maxAge' => false - )); - - $passed = assertTrue($expected === $output, 'Minify : CSS and Etag/Last-Modified'); - 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"; - } - } -} - -test_Minify(); + )); + + $passed = assertTrue($expected === $output, 'Minify : CSS and Etag/Last-Modified'); + 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"; + } + } +} + +test_Minify(); diff --git a/web/test/test_Minify_Build.php b/web/test/test_Minify_Build.php index b45ccd5..b4e3677 100644 --- a/web/test/test_Minify_Build.php +++ b/web/test/test_Minify_Build.php @@ -1,35 +1,35 @@ -lastModified == filemtime($file1) - ,'Minify_Build : single file path'); - - $b = new Minify_Build(array($file1, $file2)); - assertTrue($maxTime == $b->lastModified - ,'Minify_Build : multiple file paths'); - - $b = new Minify_Build(array( - $file1 - ,new Minify_Source(array('filepath' => $file2)) - )); - - assertTrue($maxTime == $b->lastModified - ,'Minify_Build : file path and a Minify_Source'); - assertTrue($b->uri('/path') == "/path?{$maxTime}" - ,'Minify_Build : uri() with no querystring'); - assertTrue($b->uri('/path?hello') == "/path?hello&{$maxTime}" - ,'Minify_Build : uri() with existing querystring'); -} - +lastModified == filemtime($file1) + ,'Minify_Build : single file path'); + + $b = new Minify_Build(array($file1, $file2)); + assertTrue($maxTime == $b->lastModified + ,'Minify_Build : multiple file paths'); + + $b = new Minify_Build(array( + $file1 + ,new Minify_Source(array('filepath' => $file2)) + )); + + assertTrue($maxTime == $b->lastModified + ,'Minify_Build : file path and a Minify_Source'); + assertTrue($b->uri('/path') == "/path?{$maxTime}" + ,'Minify_Build : uri() with no querystring'); + assertTrue($b->uri('/path?hello') == "/path?hello&{$maxTime}" + ,'Minify_Build : uri() with existing querystring'); +} + test_Minify_Build(); \ No newline at end of file diff --git a/web/test/test_all.php b/web/test/test_all.php index c1efd27..fe0cb3a 100644 --- a/web/test/test_all.php +++ b/web/test/test_all.php @@ -1,10 +1,10 @@ - file_get_contents($_FILES['subject']['tmp_name']) - ,'method' => $_POST['method'] - )); - header('Content-Type: application/octet-stream'); - header('Content-Transfer-Encoding: binary'); - header("Content-Disposition: attachment; filename=\"{$_FILES['subject']['name']}." - . ($_POST['method'] == 'deflate' - ? 'zd' - : ($_POST['method'] == 'gzip' - ? 'zg' - : 'zc' - ) - ) . '"'); - $he->encode(9); - echo $he->getContent(); - exit(); -} - -?> -

-

Encode
-as - - -

+ file_get_contents($_FILES['subject']['tmp_name']) + ,'method' => $_POST['method'] + )); + header('Content-Type: application/octet-stream'); + header('Content-Transfer-Encoding: binary'); + header("Content-Disposition: attachment; filename=\"{$_FILES['subject']['name']}." + . ($_POST['method'] == 'deflate' + ? 'zd' + : ($_POST['method'] == 'gzip' + ? 'zg' + : 'zc' + ) + ) . '"'); + $he->encode(9); + echo $he->getContent(); + exit(); +} + +?> + +

Encode
+as + + +

\ No newline at end of file diff --git a/web/tools/minifyFile.php b/web/tools/minifyFile.php index 4f7db4b..a2f5a1a 100644 --- a/web/tools/minifyFile.php +++ b/web/tools/minifyFile.php @@ -1,50 +1,53 @@ - array('Minify_CSS', 'minify') - ,'jsMinifier' => array('Minify_Javascript', 'minify') - ); - } - $func = array('Minify_' . $type, 'minify'); - - $out = call_user_func($func, file_get_contents($_FILES['subject']['tmp_name']), $arg2); - - header('Content-Type: application/octet-stream'); - header('Content-Transfer-Encoding: binary'); - header('Content-Disposition: attachment; filename="' - . preg_replace('/\\.(\w+)$/', '.min.$1', $_FILES['subject']['name']) - . '"'); - - echo $out; - exit(); -} - -?> -
-

Minify
- -

+ array('Minify_CSS', 'minify') + ,'jsMinifier' => array('Minify_Javascript', 'minify') + ); + } + $func = array('Minify_' . $type, 'minify'); + + $out = call_user_func($func, file_get_contents($_FILES['subject']['tmp_name']), $arg2); + + header('Content-Type: application/octet-stream'); + header('Content-Transfer-Encoding: binary'); + header('Content-Disposition: attachment; filename="' + . preg_replace('/\\.(\w+)$/', '.min.$1', $_FILES['subject']['name']) + . '"'); + + //@unlink($_FILES['subject']['tmp_name']); + echo $out; + exit(); +} + +?> + +

Minify
+ +

\ No newline at end of file diff --git a/web/version1/minify.php b/web/version1/minify.php index ed048fc..3a05bd6 100644 --- a/web/version1/minify.php +++ b/web/version1/minify.php @@ -1,6 +1,6 @@ -