From 379feaba99c0a1c6da8572261eb7486755082491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Fri, 22 Jan 2016 00:30:38 +0200 Subject: [PATCH] do some trivial codestyle fixes unix newlines, trailing spaces --- lib/DooDigestAuth.php | 4 +- lib/HTTP/ConditionalGet.php | 91 +++--- lib/HTTP/Encoder.php | 110 +++---- lib/Minify.php | 115 ++++--- lib/Minify/Build.php | 35 +- lib/Minify/CSS.php | 202 ++++++------ lib/Minify/CSS/Compressor.php | 519 +++++++++++++++--------------- lib/Minify/CSS/UriRewriter.php | 90 +++--- lib/Minify/CSSmin.php | 170 +++++----- lib/Minify/Cache/APC.php | 7 +- lib/Minify/Cache/File.php | 35 +- lib/Minify/Cache/Memcache.php | 43 +-- lib/Minify/Cache/WinCache.php | 23 +- lib/Minify/Cache/XCache.php | 3 + lib/Minify/Cache/ZendPlatform.php | 10 +- lib/Minify/CacheInterface.php | 1 - lib/Minify/ClosureCompiler.php | 5 + lib/Minify/CommentPreserver.php | 30 +- lib/Minify/Controller/Base.php | 12 +- lib/Minify/Controller/Files.php | 16 +- lib/Minify/Controller/Groups.php | 17 +- lib/Minify/Controller/MinApp.php | 14 +- lib/Minify/Controller/Page.php | 20 +- lib/Minify/DebugDetector.php | 55 ++-- lib/Minify/Env.php | 4 + lib/Minify/HTML.php | 511 ++++++++++++++--------------- lib/Minify/HTML/Helper.php | 6 +- lib/Minify/ImportProcessor.php | 437 ++++++++++++------------- lib/Minify/JS/ClosureCompiler.php | 1 + lib/Minify/LessCssSource.php | 3 + lib/Minify/Lines.php | 31 +- lib/Minify/Loader.php | 2 + lib/Minify/Logger.php | 12 +- lib/Minify/Packer.php | 9 +- lib/Minify/ServeConfiguration.php | 2 +- lib/Minify/Source.php | 10 +- lib/Minify/SourceSet.php | 1 + lib/Minify/YUICompressor.php | 48 +-- lib/MrClay/Cli.php | 42 ++- lib/MrClay/Cli/Arg.php | 5 + 40 files changed, 1424 insertions(+), 1327 deletions(-) diff --git a/lib/DooDigestAuth.php b/lib/DooDigestAuth.php index 69bc4ed..b979009 100644 --- a/lib/DooDigestAuth.php +++ b/lib/DooDigestAuth.php @@ -38,7 +38,7 @@ class DooDigestAuth{ * @param string $fail_url URL to be redirect if the User cancel the login * @return string The username if login success. */ - public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){ + public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL) { $realm = "Restricted area - $realm"; //user => password @@ -114,8 +114,8 @@ class DooDigestAuth{ $data['uri'] = $match[1]; $res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match); $data['response'] = $match[1]; + return $data; } - } diff --git a/lib/HTTP/ConditionalGet.php b/lib/HTTP/ConditionalGet.php index 93b7e75..72d11c8 100644 --- a/lib/HTTP/ConditionalGet.php +++ b/lib/HTTP/ConditionalGet.php @@ -1,6 +1,6 @@ - * + * * E.g. Shortcut for the above * * HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache @@ -40,7 +40,7 @@ * } * echo $content; * - * + * * E.g. Static content with some static includes: * * // before content @@ -64,7 +64,7 @@ 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. * @@ -74,36 +74,36 @@ class HTTP_ConditionalGet { /** * @param array $spec options - * + * * 'isPublic': (bool) if false, the Cache-Control header will contain * "private", allowing only browser caching. (default false) - * + * * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers * will be sent with content. This is recommended. * * 'encoding': (string) if set, the header "Vary: Accept-Encoding" will * always be sent and a truncated version of the encoding will be appended - * to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient + * to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient * checking of the client's If-None-Match header, as the encoding portion of * the ETag will be stripped before comparison. - * + * * '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 + * 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. - * + * * 'eTag': (string) if given, this will be used as the ETag header rather * than values based on lastModifiedTime or contentHash. Also the encoding * string will not be appended to the given value as described above. - * + * * 'invalidate': (bool) if true, the client cache will be considered invalid - * without testing. Effectively this disables conditional GET. + * 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 + * + * '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. */ public function __construct($spec) @@ -113,7 +113,7 @@ class HTTP_ConditionalGet { : 'private'; $maxAge = 0; // backwards compatibility (can be removed later) - if (isset($spec['setExpires']) + if (isset($spec['setExpires']) && is_numeric($spec['setExpires']) && ! isset($spec['maxAge'])) { $spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME']; @@ -121,7 +121,7 @@ class HTTP_ConditionalGet { if (isset($spec['maxAge'])) { $maxAge = $spec['maxAge']; $this->_headers['Expires'] = self::gmtDate( - $_SERVER['REQUEST_TIME'] + $spec['maxAge'] + $_SERVER['REQUEST_TIME'] + $spec['maxAge'] ); } $etagAppend = ''; @@ -156,14 +156,14 @@ class HTTP_ConditionalGet { ? 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: + * + * Otherwise something like: * * array( * 'Cache-Control' => 'max-age=0, public' @@ -171,7 +171,7 @@ class HTTP_ConditionalGet { * ) * * - * @return array + * @return array */ public function getHeaders() { @@ -180,13 +180,13 @@ class HTTP_ConditionalGet { /** * 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 + * 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) @@ -196,9 +196,9 @@ class HTTP_ConditionalGet { /** * 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. @@ -218,7 +218,7 @@ class HTTP_ConditionalGet { header($name . ': ' . $val); } } - + /** * Exit if the client's cache is valid for this resource * @@ -227,8 +227,8 @@ class HTTP_ConditionalGet { * @param int $lastModifiedTime if given, both ETag AND Last-Modified headers * will be sent with content. This is recommended. * - * @param bool $isPublic (default false) if true, the Cache-Control header - * will contain "public", allowing proxies to cache the content. Otherwise + * @param bool $isPublic (default false) if true, the Cache-Control header + * will contain "public", allowing proxies to cache the content. Otherwise * "private" will be sent, allowing only browser caching. * * @param array $options (default empty) additional options for constructor @@ -245,24 +245,23 @@ class HTTP_ConditionalGet { exit(); } } - - + /** * 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; @@ -297,7 +296,7 @@ class HTTP_ConditionalGet { { if (null === $this->_etag) { // lmTime is copied to ETag, so this condition implies that the - // server sent neither ETag nor Last-Modified, so the client can't + // server sent neither ETag nor Last-Modified, so the client can't // possibly has a valid cache. return false; } @@ -305,6 +304,7 @@ class HTTP_ConditionalGet { if ($isValid) { $this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified'; } + return $isValid; } @@ -320,16 +320,18 @@ class HTTP_ConditionalGet { ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : $_SERVER['HTTP_IF_NONE_MATCH']; $clientEtags = explode(',', $clientEtagList); - + $compareTo = $this->normalizeEtag($this->_etag); foreach ($clientEtags as $clientEtag) { if ($this->normalizeEtag($clientEtag) === $compareTo) { // respond with the client's matched ETag, even if it's not what // we would've sent by default $this->_headers['ETag'] = trim($clientEtag); + return true; } } + return false; } @@ -340,6 +342,7 @@ class HTTP_ConditionalGet { */ protected function normalizeEtag($etag) { $etag = trim($etag); + return $this->_stripEtag ? preg_replace('/;\\w\\w"$/', '"', $etag) : $etag; @@ -356,11 +359,13 @@ class HTTP_ConditionalGet { // strip off IE's extra data (semicolon) list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2); if (strtotime($ifModifiedSince) >= $this->_lmTime) { - // Apache 2.2's behavior. If there was no ETag match, send the + // Apache 2.2's behavior. If there was no ETag match, send the // non-encoded version of the ETag value. $this->_headers['ETag'] = $this->normalizeEtag($this->_etag); + return true; } + return false; } } diff --git a/lib/HTTP/Encoder.php b/lib/HTTP/Encoder.php index 8f34779..6e236ee 100644 --- a/lib/HTTP/Encoder.php +++ b/lib/HTTP/Encoder.php @@ -1,14 +1,14 @@ @@ -26,7 +26,7 @@ * header('Content-Type: text/css'); // needed if not HTML * HTTP_Encoder::output($css); * - * + * * * // Just sniff for the accepted encoding * $encoding = HTTP_Encoder::getAcceptedEncoding(); @@ -34,11 +34,11 @@ * * For more control over headers, use getHeaders() and getData() and send your * own output. - * - * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate, + * + * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate, * and gzcompress functions for gzip, deflate, and compress-encoding * respectively. - * + * * @package Minify * @subpackage HTTP * @author Stephen Clay @@ -46,47 +46,45 @@ class HTTP_Encoder { /** - * Should the encoder allow HTTP encoding to IE6? - * - * If you have many IE6 users and the bandwidth savings is worth troubling + * Should the encoder allow HTTP encoding to IE6? + * + * If you have many IE6 users and the bandwidth savings is worth troubling * some of them, set this to true. - * + * * By default, encoding is only offered to IE7+. When this is true, * getAcceptedEncoding() will return an encoding for IE6 if its user agent * string contains "SV1". This has been documented in many places as "safe", - * but there seem to be remaining, intermittent encoding bugs in patched + * but there seem to be remaining, intermittent encoding bugs in patched * IE6 on the wild web. - * + * * @var bool */ public static $encodeToIe6 = true; - - + /** * Default compression level for zlib operations - * + * * This level is used if encode() is not given a $compressionLevel - * + * * @var int */ public static $compressionLevel = 6; - /** * Get an HTTP Encoder object - * + * * @param array $spec options - * + * * 'content': (string required) content to be encoded - * + * * 'type': (string) if set, the Content-Type header will have this value. - * + * * 'method: (string) only set this if you are forcing a particular encoding * method. If not set, the best method will be chosen by getAcceptedEncoding() * The available methods are 'gzip', 'deflate', 'compress', and '' (no * encoding) */ - public function __construct($spec) + public function __construct($spec) { $this->_useMbStrlen = (function_exists('mb_strlen') && (ini_get('mbstring.func_overload') !== '') @@ -109,19 +107,19 @@ class HTTP_Encoder { /** * Get content in current form - * + * * Call after encode() for encoded content. - * + * * @return string */ - public function getContent() + public function getContent() { return $this->_content; } - + /** * Get array of output headers to be sent - * + * * E.g. * * array( @@ -131,7 +129,7 @@ class HTTP_Encoder { * ) * * - * @return array + * @return array */ public function getHeaders() { @@ -140,11 +138,11 @@ class HTTP_Encoder { /** * Send output headers - * + * * You must call this before headers are sent and it probably cannot be * used in conjunction with zlib output buffering / mod_gzip. Errors are * not handled purposefully. - * + * * @see getHeaders() */ public function sendHeaders() @@ -153,10 +151,10 @@ class HTTP_Encoder { header($name . ': ' . $val); } } - + /** * Send output headers and content - * + * * A shortcut for sendHeaders() and echo getContent() * * You must call this before headers are sent and it probably cannot be @@ -170,21 +168,21 @@ class HTTP_Encoder { } /** - * Determine the client's best encoding method from the HTTP Accept-Encoding + * Determine the client's best encoding method from the HTTP Accept-Encoding * header. - * + * * If no Accept-Encoding header is set, or the browser is IE before v6 SP2, * this will return ('', ''), the "identity" encoding. - * + * * A syntax-aware scan is done of the Accept-Encoding, so the method must - * be non 0. The methods are favored in order of gzip, deflate, then - * compress. Deflate is always smallest and generally faster, but is + * be non 0. The methods are favored in order of gzip, deflate, then + * compress. Deflate is always smallest and generally faster, but is * rarely sent by servers, so client support could be buggier. - * + * * @param bool $allowCompress allow the older compress encoding - * + * * @param bool $allowDeflate allow the more recent deflate encoding - * + * * @return array two values, 1st is the actual encoding method, 2nd is the * alias of that method to use in the Content-Encoding header (some browsers * call gzip "x-gzip" etc.) @@ -192,7 +190,7 @@ class HTTP_Encoder { public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true) { // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - + if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) || self::isBuggyIe()) { @@ -213,7 +211,7 @@ class HTTP_Encoder { return array('gzip', $m[1]); } if ($allowDeflate) { - // deflate checks + // deflate checks $aeRev = strrev($ae); if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit || 0 === strpos($aeRev, 'etalfed,') // gecko @@ -230,24 +228,25 @@ class HTTP_Encoder { ,$m)) { return array('compress', $m[1]); } + return array('', ''); } /** * Encode (compress) the content - * + * * If the encode method is '' (none) or compression level is 0, or the 'zlib' * extension isn't loaded, we return false. - * + * * Then the appropriate gz_* function is called to compress the content. If * this fails, false is returned. - * - * The header "Vary: Accept-Encoding" is added. If encoding is successful, + * + * The header "Vary: Accept-Encoding" is added. If encoding is successful, * the Content-Length header is updated, and Content-Encoding is also added. - * + * * @param int $compressionLevel given to zlib functions. If not given, the * class default will be used. - * + * * @return bool success true if the content was actually compressed */ public function encode($compressionLevel = null) @@ -279,19 +278,20 @@ class HTTP_Encoder { : (string)strlen($encoded); $this->_headers['Content-Encoding'] = $this->_encodeMethod[1]; $this->_content = $encoded; + return true; } - + /** * Encode and send appropriate headers and content * * This is a convenience method for common use of the class - * + * * @param string $content - * + * * @param int $compressionLevel given to zlib functions. If not given, the * class default will be used. - * + * * @return bool success true if the content was actually compressed */ public static function output($content, $compressionLevel = null) @@ -302,6 +302,7 @@ class HTTP_Encoder { $he = new HTTP_Encoder(array('content' => $content)); $ret = $he->encode($compressionLevel); $he->sendAll(); + return $ret; } @@ -323,11 +324,12 @@ class HTTP_Encoder { } // no regex = faaast $version = (float)substr($ua, 30); + return self::$encodeToIe6 ? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1'))) : ($version < 7); } - + protected $_content = ''; protected $_headers = array(); protected $_encodeMethod = array('', ''); diff --git a/lib/Minify.php b/lib/Minify.php index f99330e..8ecc4d8 100644 --- a/lib/Minify.php +++ b/lib/Minify.php @@ -1,9 +1,9 @@ "/* See https://github.com/mrclay/minify/blob/master/docs/CommonProblems.wiki.md#imports-can-appear-in-invalid-locations-in-combined-css-files */\n" ); } - + /** - * Serve a request for a minified file. - * + * Serve a request for a minified file. + * * Here are the available options and defaults: - * - * 'isPublic' : send "public" instead of "private" in Cache-Control + * + * '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' : set to false to disable content encoding, and not send * the Vary header (default true) - * - * 'encodeMethod' : generally you should let this be determined by + * + * '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' 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') - * + * 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) - * + * * 'bubbleCssImports' : If true, all @import declarations in combined CSS * files will be move to the top. Note this may alter effective CSS values * due to a change in order. (default false) - * + * * '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() * * 'concatOnly' : set to true to disable minification and simply concatenate the files. * For JS, no minifier will be used. For CSS, only URI rewriting is still performed. - * - * 'minifiers' : to override Minify's default choice of minifier function for - * a particular content-type, specify your callback under the key of the + * + * '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: + * 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 + * // 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 + * + * '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. @@ -186,13 +186,13 @@ class Minify { * 'importWarning' : serve() will check CSS files for @import declarations that * appear too late in the combined stylesheet. If found, serve() will prepend * the output with this warning. To disable this, set this option to empty string. - * + * * Any controller options are documented in that controller's createConfiguration() method. - * + * * @param Minify_ControllerInterface $controller instance of subclass of Minify_Controller_Base - * + * * @param array $options controller/serve options - * + * * @return null|array if the 'quiet' option is set to true, an array * with keys "success" (bool), "statusCode" (int), "content" (string), and * "headers" (array). @@ -216,6 +216,7 @@ class Minify { $this->errorExit($this->options['badRequestHeader'], self::URL_DEBUG); } else { list(,$statusCode) = explode(' ', $this->options['badRequestHeader']); + return array( 'success' => false, 'statusCode' => (int)$statusCode, @@ -224,14 +225,14 @@ class Minify { ); } } - + $this->controller = $controller; - + if ($this->options['debug']) { $this->setupDebug(); $this->options['maxAge'] = 0; } - + // determine encoding if ($this->options['encodeOutput']) { $sendVary = true; @@ -249,7 +250,7 @@ class Minify { } else { $this->options['encodeMethod'] = ''; // identity (no encoding) } - + // check client cache $cgOptions = array( 'lastModifiedTime' => $this->options['lastModifiedTime'], @@ -268,6 +269,7 @@ class Minify { // client's cache is valid if (! $this->options['quiet']) { $cg->sendHeaders(); + return; } else { return array( @@ -282,7 +284,7 @@ class Minify { $headers = $cg->getHeaders(); unset($cg); } - + if ($this->options['contentType'] === self::TYPE_CSS && $this->options['rewriteCssUris']) { $this->setupUriRewrites(); } @@ -300,20 +302,20 @@ class Minify { } } } - + // check server cache if (! $this->options['debug']) { // using cache - // the goal is to use only the cache methods to sniff the length and + // 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 = $this->_getCacheId(); $fullCacheId = ($this->options['encodeMethod']) ? $cacheId . '.gz' : $cacheId; // check cache for valid entry - $cacheIsReady = $this->cache->isValid($fullCacheId, $this->options['lastModifiedTime']); + $cacheIsReady = $this->cache->isValid($fullCacheId, $this->options['lastModifiedTime']); if ($cacheIsReady) { - $cacheContentLength = $this->cache->getSize($fullCacheId); + $cacheContentLength = $this->cache->getSize($fullCacheId); } else { // generate & cache content try { @@ -347,7 +349,7 @@ class Minify { // still need to encode $content = gzencode($content, $this->options['encodeLevel']); } - + // add headers if ($cacheIsReady) { $headers['Content-Length'] = $cacheContentLength; @@ -395,11 +397,11 @@ class Minify { * Return combined minified content for a set of sources * * No internal caching will be used and the content will not be HTTP encoded. - * + * * @param array $sources array of filepaths and/or Minify_Source objects - * + * * @param array $options (optional) array of options for serve. - * + * * @return string */ public function combine($sources, $options = array()) @@ -422,6 +424,7 @@ class Minify { $out = $this->serve($controller, $options); $this->cache = $tmpCache; + return $out['content']; } @@ -487,7 +490,7 @@ class Minify { )); } } - + /** * Combines sources and minifies the result. * @@ -498,7 +501,7 @@ class Minify { protected function combineMinify() { $type = $this->options['contentType']; // ease readability - + // when combining scripts, make sure all statements separated and // trailing single line comment is terminated $implodeSeparator = ($type === self::TYPE_JS) ? "\n;" : ''; @@ -573,11 +576,11 @@ class Minify { } while ($source); $content = implode($implodeSeparator, $content); - + if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) { $content = $this->handleCssImports($content); } - + // do any post-processing (esp. for editing build URIs) if ($this->options['postprocessorRequire']) { require_once $this->options['postprocessorRequire']; @@ -585,13 +588,14 @@ class Minify { if ($this->options['postprocessor']) { $content = call_user_func($this->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 + * + * Any settings that could affect output are taken into consideration * * @param string $prefix * @@ -610,9 +614,10 @@ class Minify { $this->options['bubbleCssImports'], Minify::VERSION, ))); + return "{$prefix}_{$name}_{$md5}"; } - + /** * Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top. * @@ -626,6 +631,7 @@ class Minify { // bubble CSS imports preg_match_all('/@import.*?;/', $css, $imports); $css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css); + return $css; } @@ -644,6 +650,7 @@ class Minify { // { appears before @import : prepend warning $css = $this->options['importWarning'] . $css; } + return $css; } @@ -672,6 +679,7 @@ class Minify { Minify_Logger::log('ContentType mismatch'); $this->sources = array(); + return $options; } @@ -686,6 +694,7 @@ class Minify { Minify_Logger::log('ContentType mismatch'); $this->sources = array(); + return $options; } } diff --git a/lib/Minify/Build.php b/lib/Minify/Build.php index c084f9b..e4428e0 100644 --- a/lib/Minify/Build.php +++ b/lib/Minify/Build.php @@ -1,48 +1,48 @@ * // 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 + * + * @var int */ public $lastModified = 0; - + /** * String to use as ampersand in uri(). Set this to '&' if * you are not HTML-escaping URIs. @@ -50,20 +50,20 @@ class Minify_Build { * @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 - * @param boolean $forceAmpersand (default = false) Force the use of ampersand to + * @param boolean $forceAmpersand (default = false) Force the use of ampersand to * append the timestamp to the URI. * @return string */ @@ -71,17 +71,18 @@ class Minify_Build { $sep = ($forceAmpersand || 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) + public function __construct($sources) { $max = 0; foreach ((array)$sources as $source) { diff --git a/lib/Minify/CSS.php b/lib/Minify/CSS.php index ef4c5d9..da389ea 100644 --- a/lib/Minify/CSS.php +++ b/lib/Minify/CSS.php @@ -1,101 +1,101 @@ - - * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) - * - * @deprecated Use Minify_CSSmin - */ -class Minify_CSS { - - /** - * Minify a CSS string - * - * @param string $css - * - * @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. - * - * 'removeCharsets': (default true) remove all @charset at-rules - * - * 'prependRelativePath': (default null) if given, this string will be - * prepended to all relative URIs in import/url declarations - * - * 'currentDir': (default null) if given, this is assumed to be the - * directory of the current CSS file. Using this, minify will rewrite - * all relative URIs in import/url declarations to correctly point to - * the desired files. For this to work, the files *must* exist and be - * visible by the PHP process. - * - * 'symlinks': (default = array()) If the CSS file is stored in - * a symlink-ed directory, provide an array of link paths to - * target paths, where the link paths are within the document root. Because - * paths need to be normalized for this to work, use "//" to substitute - * the doc root in the link paths (the array keys). E.g.: - * - * array('//symlink' => '/real/target/path') // unix - * array('//static' => 'D:\\staticStorage') // Windows - * - * - * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) - * see Minify_CSS_UriRewriter::rewrite - * - * @return string - */ - public static function minify($css, $options = array()) - { - $options = array_merge(array( - 'compress' => true, - 'removeCharsets' => true, - 'preserveComments' => true, - 'currentDir' => null, - 'docRoot' => $_SERVER['DOCUMENT_ROOT'], - 'prependRelativePath' => null, - 'symlinks' => array(), - ), $options); - - if ($options['removeCharsets']) { - $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); - } - if ($options['compress']) { - if (! $options['preserveComments']) { - $css = Minify_CSS_Compressor::process($css, $options); - } else { - $css = Minify_CommentPreserver::process( - $css - ,array('Minify_CSS_Compressor', 'process') - ,array($options) - ); - } - } - if (! $options['currentDir'] && ! $options['prependRelativePath']) { - return $css; - } - if ($options['currentDir']) { - return Minify_CSS_UriRewriter::rewrite( - $css - ,$options['currentDir'] - ,$options['docRoot'] - ,$options['symlinks'] - ); - } else { - return Minify_CSS_UriRewriter::prepend( - $css - ,$options['prependRelativePath'] - ); - } - } -} + + * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) + * + * @deprecated Use Minify_CSSmin + */ +class Minify_CSS { + + /** + * Minify a CSS string + * + * @param string $css + * + * @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. + * + * 'removeCharsets': (default true) remove all @charset at-rules + * + * 'prependRelativePath': (default null) if given, this string will be + * prepended to all relative URIs in import/url declarations + * + * 'currentDir': (default null) if given, this is assumed to be the + * directory of the current CSS file. Using this, minify will rewrite + * all relative URIs in import/url declarations to correctly point to + * the desired files. For this to work, the files *must* exist and be + * visible by the PHP process. + * + * 'symlinks': (default = array()) If the CSS file is stored in + * a symlink-ed directory, provide an array of link paths to + * target paths, where the link paths are within the document root. Because + * paths need to be normalized for this to work, use "//" to substitute + * the doc root in the link paths (the array keys). E.g.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) + * see Minify_CSS_UriRewriter::rewrite + * + * @return string + */ + public static function minify($css, $options = array()) + { + $options = array_merge(array( + 'compress' => true, + 'removeCharsets' => true, + 'preserveComments' => true, + 'currentDir' => null, + 'docRoot' => $_SERVER['DOCUMENT_ROOT'], + 'prependRelativePath' => null, + 'symlinks' => array(), + ), $options); + + if ($options['removeCharsets']) { + $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); + } + if ($options['compress']) { + if (! $options['preserveComments']) { + $css = Minify_CSS_Compressor::process($css, $options); + } else { + $css = Minify_CommentPreserver::process( + $css + ,array('Minify_CSS_Compressor', 'process') + ,array($options) + ); + } + } + if (! $options['currentDir'] && ! $options['prependRelativePath']) { + return $css; + } + if ($options['currentDir']) { + return Minify_CSS_UriRewriter::rewrite( + $css + ,$options['currentDir'] + ,$options['docRoot'] + ,$options['symlinks'] + ); + } else { + return Minify_CSS_UriRewriter::prepend( + $css + ,$options['prependRelativePath'] + ); + } + } +} diff --git a/lib/Minify/CSS/Compressor.php b/lib/Minify/CSS/Compressor.php index f4fc297..aa9f81f 100644 --- a/lib/Minify/CSS/Compressor.php +++ b/lib/Minify/CSS/Compressor.php @@ -1,257 +1,262 @@ - - * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) - * - * @deprecated Use CSSmin (tubalmartin/cssmin) - */ -class Minify_CSS_Compressor { - - /** - * Minify a CSS string - * - * @param string $css - * - * @param array $options (currently ignored) - * - * @return string - */ - public static function process($css, $options = array()) - { - $obj = new Minify_CSS_Compressor($options); - return $obj->_process($css); - } - - /** - * @var array - */ - protected $_options = null; - - /** - * Are we "in" a hack? I.e. are some browsers targetted until the next comment? - * - * @var bool - */ - protected $_inHack = false; - - - /** - * Constructor - * - * @param array $options (currently ignored) - */ - private function __construct($options) { - $this->_options = $options; - } - - /** - * Minify a CSS string - * - * @param string $css - * - * @return string - */ - protected function _process($css) - { - $css = str_replace("\r\n", "\n", $css); - - // preserve empty comment after '>' - // http://www.webdevout.net/css-hacks#in_css-selectors - $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); - - // preserve empty comment between property and value - // http://css-discuss.incutio.com/?page=BoxModelHack - $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); - $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); - - // apply callback to all valid comments (and strip out surrounding ws - $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' - ,array($this, '_commentCB'), $css); - - // remove ws around { } and last semicolon in declaration block - $css = preg_replace('/\\s*{\\s*/', '{', $css); - $css = preg_replace('/;?\\s*}\\s*/', '}', $css); - - // remove ws surrounding semicolons - $css = preg_replace('/\\s*;\\s*/', ';', $css); - - // remove ws around urls - $css = preg_replace('/ - url\\( # url( - \\s* - ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) - \\s* - \\) # ) - /x', 'url($1)', $css); - - // remove ws between rules and colons - $css = preg_replace('/ - \\s* - ([{;]) # 1 = beginning of block or rule separator - \\s* - ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) - \\s* - : - \\s* - (\\b|[#\'"-]) # 3 = first character of a value - /x', '$1$2:$3', $css); - - // remove ws in selectors - $css = preg_replace_callback('/ - (?: # non-capture - \\s* - [^~>+,\\s]+ # selector part - \\s* - [,>+~] # combinators - )+ - \\s* - [^~>+,\\s]+ # selector part - { # open declaration block - /x' - ,array($this, '_selectorsCB'), $css); - - // minimize hex colors - $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' - , '$1#$2$3$4$5', $css); - - // remove spaces between font families - $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' - ,array($this, '_fontFamilyCB'), $css); - - $css = preg_replace('/@import\\s+url/', '@import url', $css); - - // replace any ws involving newlines with a single newline - $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); - - // separate common descendent selectors w/ newlines (to limit line lengths) - $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); - - // Use newline after 1st numeric value (to limit line lengths). - $css = preg_replace('/ - ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value - \\s+ - /x' - ,"$1\n", $css); - - // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ - $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); - - return trim($css); - } - - /** - * Replace what looks like a set of selectors - * - * @param array $m regex matches - * - * @return string - */ - protected function _selectorsCB($m) - { - // remove ws around the combinators - return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); - } - - /** - * Process a comment and return a replacement - * - * @param array $m regex matches - * - * @return string - */ - protected function _commentCB($m) - { - $hasSurroundingWs = (trim($m[0]) !== $m[1]); - $m = $m[1]; - // $m is the comment content w/o the surrounding tokens, - // but the return value will replace the entire comment. - if ($m === 'keep') { - return '/**/'; - } - if ($m === '" "') { - // component of http://tantek.com/CSS/Examples/midpass.html - return '/*" "*/'; - } - if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { - // component of http://tantek.com/CSS/Examples/midpass.html - return '/*";}}/* */'; - } - if ($this->_inHack) { - // inversion: feeding only to one browser - if (preg_match('@ - ^/ # comment started like /*/ - \\s* - (\\S[\\s\\S]+?) # has at least some non-ws content - \\s* - /\\* # ends like /*/ or /**/ - @x', $m, $n)) { - // end hack mode after this comment, but preserve the hack and comment content - $this->_inHack = false; - return "/*/{$n[1]}/**/"; - } - } - if (substr($m, -1) === '\\') { // comment ends like \*/ - // begin hack mode and preserve hack - $this->_inHack = true; - return '/*\\*/'; - } - if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ - // begin hack mode and preserve hack - $this->_inHack = true; - return '/*/*/'; - } - if ($this->_inHack) { - // a regular comment ends hack mode but should be preserved - $this->_inHack = false; - return '/**/'; - } - // Issue 107: if there's any surrounding whitespace, it may be important, so - // replace the comment with a single space - return $hasSurroundingWs // remove all other comments - ? ' ' - : ''; - } - - /** - * Process a font-family listing and return a replacement - * - * @param array $m regex matches - * - * @return string - */ - protected function _fontFamilyCB($m) - { - // Issue 210: must not eliminate WS between words in unquoted families - $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - $out = 'font-family:'; - while (null !== ($piece = array_shift($pieces))) { - if ($piece[0] !== '"' && $piece[0] !== "'") { - $piece = preg_replace('/\\s+/', ' ', $piece); - $piece = preg_replace('/\\s?,\\s?/', ',', $piece); - } - $out .= $piece; - } - return $out . $m[2]; - } -} + + * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) + * + * @deprecated Use CSSmin (tubalmartin/cssmin) + */ +class Minify_CSS_Compressor { + + /** + * Minify a CSS string + * + * @param string $css + * + * @param array $options (currently ignored) + * + * @return string + */ + public static function process($css, $options = array()) + { + $obj = new Minify_CSS_Compressor($options); + + return $obj->_process($css); + } + + /** + * @var array + */ + protected $_options = null; + + /** + * Are we "in" a hack? I.e. are some browsers targetted until the next comment? + * + * @var bool + */ + protected $_inHack = false; + + /** + * Constructor + * + * @param array $options (currently ignored) + */ + private function __construct($options) { + $this->_options = $options; + } + + /** + * Minify a CSS string + * + * @param string $css + * + * @return string + */ + protected function _process($css) + { + $css = str_replace("\r\n", "\n", $css); + + // preserve empty comment after '>' + // http://www.webdevout.net/css-hacks#in_css-selectors + $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); + + // preserve empty comment between property and value + // http://css-discuss.incutio.com/?page=BoxModelHack + $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); + $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); + + // apply callback to all valid comments (and strip out surrounding ws + $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' + ,array($this, '_commentCB'), $css); + + // remove ws around { } and last semicolon in declaration block + $css = preg_replace('/\\s*{\\s*/', '{', $css); + $css = preg_replace('/;?\\s*}\\s*/', '}', $css); + + // remove ws surrounding semicolons + $css = preg_replace('/\\s*;\\s*/', ';', $css); + + // remove ws around urls + $css = preg_replace('/ + url\\( # url( + \\s* + ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) + \\s* + \\) # ) + /x', 'url($1)', $css); + + // remove ws between rules and colons + $css = preg_replace('/ + \\s* + ([{;]) # 1 = beginning of block or rule separator + \\s* + ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) + \\s* + : + \\s* + (\\b|[#\'"-]) # 3 = first character of a value + /x', '$1$2:$3', $css); + + // remove ws in selectors + $css = preg_replace_callback('/ + (?: # non-capture + \\s* + [^~>+,\\s]+ # selector part + \\s* + [,>+~] # combinators + )+ + \\s* + [^~>+,\\s]+ # selector part + { # open declaration block + /x' + ,array($this, '_selectorsCB'), $css); + + // minimize hex colors + $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' + , '$1#$2$3$4$5', $css); + + // remove spaces between font families + $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' + ,array($this, '_fontFamilyCB'), $css); + + $css = preg_replace('/@import\\s+url/', '@import url', $css); + + // replace any ws involving newlines with a single newline + $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); + + // separate common descendent selectors w/ newlines (to limit line lengths) + $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); + + // Use newline after 1st numeric value (to limit line lengths). + $css = preg_replace('/ + ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value + \\s+ + /x' + ,"$1\n", $css); + + // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ + $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); + + return trim($css); + } + + /** + * Replace what looks like a set of selectors + * + * @param array $m regex matches + * + * @return string + */ + protected function _selectorsCB($m) + { + // remove ws around the combinators + return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); + } + + /** + * Process a comment and return a replacement + * + * @param array $m regex matches + * + * @return string + */ + protected function _commentCB($m) + { + $hasSurroundingWs = (trim($m[0]) !== $m[1]); + $m = $m[1]; + // $m is the comment content w/o the surrounding tokens, + // but the return value will replace the entire comment. + if ($m === 'keep') { + return '/**/'; + } + if ($m === '" "') { + // component of http://tantek.com/CSS/Examples/midpass.html + return '/*" "*/'; + } + if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { + // component of http://tantek.com/CSS/Examples/midpass.html + return '/*";}}/* */'; + } + if ($this->_inHack) { + // inversion: feeding only to one browser + if (preg_match('@ + ^/ # comment started like /*/ + \\s* + (\\S[\\s\\S]+?) # has at least some non-ws content + \\s* + /\\* # ends like /*/ or /**/ + @x', $m, $n)) { + // end hack mode after this comment, but preserve the hack and comment content + $this->_inHack = false; + + return "/*/{$n[1]}/**/"; + } + } + if (substr($m, -1) === '\\') { // comment ends like \*/ + // begin hack mode and preserve hack + $this->_inHack = true; + + return '/*\\*/'; + } + if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ + // begin hack mode and preserve hack + $this->_inHack = true; + + return '/*/*/'; + } + if ($this->_inHack) { + // a regular comment ends hack mode but should be preserved + $this->_inHack = false; + + return '/**/'; + } + // Issue 107: if there's any surrounding whitespace, it may be important, so + // replace the comment with a single space + return $hasSurroundingWs // remove all other comments + ? ' ' + : ''; + } + + /** + * Process a font-family listing and return a replacement + * + * @param array $m regex matches + * + * @return string + */ + protected function _fontFamilyCB($m) + { + // Issue 210: must not eliminate WS between words in unquoted families + $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + $out = 'font-family:'; + while (null !== ($piece = array_shift($pieces))) { + if ($piece[0] !== '"' && $piece[0] !== "'") { + $piece = preg_replace('/\\s+/', ' ', $piece); + $piece = preg_replace('/\\s?,\\s?/', ',', $piece); + } + $out .= $piece; + } + + return $out . $m[2]; + } +} diff --git a/lib/Minify/CSS/UriRewriter.php b/lib/Minify/CSS/UriRewriter.php index 52f1d43..ad6ae0e 100644 --- a/lib/Minify/CSS/UriRewriter.php +++ b/lib/Minify/CSS/UriRewriter.php @@ -1,6 +1,6 @@ */ class Minify_CSS_UriRewriter { - + /** * rewrite() and rewriteRelative() append debugging information here * * @var string */ public static $debugText = ''; - + /** * In CSS content, rewrite file relative URIs as root relative - * + * * @param string $css - * + * * @param string $currentDir The directory of the current CSS file. - * - * @param string $docRoot The document root of the web site in which + * + * @param string $docRoot The document root of the web site in which * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']). - * - * @param array $symlinks (default = array()) If the CSS file is stored in + * + * @param array $symlinks (default = array()) If the CSS file is stored in * a symlink-ed directory, provide an array of link paths to - * target paths, where the link paths are within the document root. Because - * paths need to be normalized for this to work, use "//" to substitute + * target paths, where the link paths are within the document root. Because + * paths need to be normalized for this to work, use "//" to substitute * the doc root in the link paths (the array keys). E.g.: * * array('//symlink' => '/real/target/path') // unix * array('//static' => 'D:\\staticStorage') // Windows * - * + * * @return string */ - public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array()) + public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array()) { self::$_docRoot = self::_realpath( $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT'] @@ -57,16 +57,16 @@ class Minify_CSS_UriRewriter { $link = strtr($link, '/', DIRECTORY_SEPARATOR); self::$_symlinks[$link] = self::_realpath($target); } - + self::$debugText .= "docRoot : " . self::$_docRoot . "\n" . "currentDir : " . self::$_currentDir . "\n"; if (self::$_symlinks) { self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n"; } self::$debugText .= "\n"; - + $css = self::_trimUrls($css); - + // rewrite $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' ,array(self::$className, '_processUriCB'), $css); @@ -75,22 +75,22 @@ class Minify_CSS_UriRewriter { return $css; } - + /** * In CSS content, prepend a path to relative URIs - * + * * @param string $css - * + * * @param string $path The path to prepend. - * + * * @return string */ public static function prepend($css, $path) { self::$_prependPath = $path; - + $css = self::_trimUrls($css); - + // append $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' ,array(self::$className, '_processUriCB'), $css); @@ -98,9 +98,10 @@ class Minify_CSS_UriRewriter { ,array(self::$className, '_processUriCB'), $css); self::$_prependPath = null; + return $css; } - + /** * Get a root relative URI from a file relative URI * @@ -111,7 +112,7 @@ class Minify_CSS_UriRewriter { * , '/home/user/www' // doc root * ); * // returns '/img/hello.gif' - * + * * // example where static files are stored in a symlinked directory * Minify_CSS_UriRewriter::rewriteRelative( * 'hello.gif' @@ -121,55 +122,55 @@ class Minify_CSS_UriRewriter { * ); * // returns '/static/theme/hello.gif' * - * + * * @param string $uri file relative URI - * + * * @param string $realCurrentDir realpath of the current file's directory. - * + * * @param string $realDocRoot realpath of the site document root. - * - * @param array $symlinks (default = array()) If the file is stored in + * + * @param array $symlinks (default = array()) If the file is stored in * a symlink-ed directory, provide an array of link paths to - * real target paths, where the link paths "appear" to be within the document + * real target paths, where the link paths "appear" to be within the document * root. E.g.: * * array('/home/foo/www/not/real/path' => '/real/target/path') // unix * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows * - * + * * @return string */ public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) { // prepend path with current dir separator (OS-independent) - $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) + $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); - + self::$debugText .= "file-relative URI : {$uri}\n" . "path prepended : {$path}\n"; - + // "unresolve" a symlink back to doc root foreach ($symlinks as $link => $target) { if (0 === strpos($path, $target)) { // replace $target with $link $path = $link . substr($path, strlen($target)); - + self::$debugText .= "symlink unresolved : {$path}\n"; - + break; } } // strip doc root $path = substr($path, strlen($realDocRoot)); - + self::$debugText .= "docroot stripped : {$path}\n"; - + // fix to root-relative URI $uri = strtr($path, '/\\', '//'); $uri = self::removeDots($uri); - + self::$debugText .= "traversals removed : {$uri}\n\n"; - + return $uri; } @@ -187,9 +188,10 @@ class Minify_CSS_UriRewriter { do { $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed); } while ($changed); + return $uri; } - + /** * Defines which class to call as part of callbacks, change this * if you extend Minify_CSS_UriRewriter @@ -201,9 +203,9 @@ class Minify_CSS_UriRewriter { /** * Get realpath with any trailing slash removed. If realpath() fails, * just remove the trailing slash. - * + * * @param string $path - * + * * @return mixed path with no trailing slash */ protected static function _realpath($path) @@ -212,6 +214,7 @@ class Minify_CSS_UriRewriter { if ($realPath !== false) { $path = $realPath; } + return rtrim($path, '/\\'); } @@ -300,6 +303,7 @@ class Minify_CSS_UriRewriter { } } } + return $isImport ? "@import {$quoteChar}{$uri}{$quoteChar}" : "url({$quoteChar}{$uri}{$quoteChar})"; diff --git a/lib/Minify/CSSmin.php b/lib/Minify/CSSmin.php index 4403383..8c3a64f 100644 --- a/lib/Minify/CSSmin.php +++ b/lib/Minify/CSSmin.php @@ -1,85 +1,85 @@ - - */ -class Minify_CSSmin { - - /** - * Minify a CSS string - * - * @param string $css - * - * @param array $options available options: - * - * 'removeCharsets': (default true) remove all @charset at-rules - * - * 'prependRelativePath': (default null) if given, this string will be - * prepended to all relative URIs in import/url declarations - * - * 'currentDir': (default null) if given, this is assumed to be the - * directory of the current CSS file. Using this, minify will rewrite - * all relative URIs in import/url declarations to correctly point to - * the desired files. For this to work, the files *must* exist and be - * visible by the PHP process. - * - * 'symlinks': (default = array()) If the CSS file is stored in - * a symlink-ed directory, provide an array of link paths to - * target paths, where the link paths are within the document root. Because - * paths need to be normalized for this to work, use "//" to substitute - * the doc root in the link paths (the array keys). E.g.: - * - * array('//symlink' => '/real/target/path') // unix - * array('//static' => 'D:\\staticStorage') // Windows - * - * - * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) - * see Minify_CSS_UriRewriter::rewrite - * - * @return string - */ - public static function minify($css, $options = array()) - { - $options = array_merge(array( - 'compress' => true, - 'removeCharsets' => true, - 'currentDir' => null, - 'docRoot' => $_SERVER['DOCUMENT_ROOT'], - 'prependRelativePath' => null, - 'symlinks' => array(), - ), $options); - - if ($options['removeCharsets']) { - $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); - } - if ($options['compress']) { - $obj = new CSSmin(); - $css = $obj->run($css); - } - if (! $options['currentDir'] && ! $options['prependRelativePath']) { - return $css; - } - if ($options['currentDir']) { - return Minify_CSS_UriRewriter::rewrite( - $css - ,$options['currentDir'] - ,$options['docRoot'] - ,$options['symlinks'] - ); - } else { - return Minify_CSS_UriRewriter::prepend( - $css - ,$options['prependRelativePath'] - ); - } - } -} + + */ +class Minify_CSSmin { + + /** + * Minify a CSS string + * + * @param string $css + * + * @param array $options available options: + * + * 'removeCharsets': (default true) remove all @charset at-rules + * + * 'prependRelativePath': (default null) if given, this string will be + * prepended to all relative URIs in import/url declarations + * + * 'currentDir': (default null) if given, this is assumed to be the + * directory of the current CSS file. Using this, minify will rewrite + * all relative URIs in import/url declarations to correctly point to + * the desired files. For this to work, the files *must* exist and be + * visible by the PHP process. + * + * 'symlinks': (default = array()) If the CSS file is stored in + * a symlink-ed directory, provide an array of link paths to + * target paths, where the link paths are within the document root. Because + * paths need to be normalized for this to work, use "//" to substitute + * the doc root in the link paths (the array keys). E.g.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT']) + * see Minify_CSS_UriRewriter::rewrite + * + * @return string + */ + public static function minify($css, $options = array()) + { + $options = array_merge(array( + 'compress' => true, + 'removeCharsets' => true, + 'currentDir' => null, + 'docRoot' => $_SERVER['DOCUMENT_ROOT'], + 'prependRelativePath' => null, + 'symlinks' => array(), + ), $options); + + if ($options['removeCharsets']) { + $css = preg_replace('/@charset[^;]+;\\s*/', '', $css); + } + if ($options['compress']) { + $obj = new CSSmin(); + $css = $obj->run($css); + } + if (! $options['currentDir'] && ! $options['prependRelativePath']) { + return $css; + } + if ($options['currentDir']) { + return Minify_CSS_UriRewriter::rewrite( + $css + ,$options['currentDir'] + ,$options['docRoot'] + ,$options['symlinks'] + ); + } else { + return Minify_CSS_UriRewriter::prepend( + $css + ,$options['prependRelativePath'] + ); + } + } +} diff --git a/lib/Minify/Cache/APC.php b/lib/Minify/Cache/APC.php index 4a89278..2e65501 100644 --- a/lib/Minify/Cache/APC.php +++ b/lib/Minify/Cache/APC.php @@ -6,11 +6,11 @@ /** * APC-based cache class for Minify - * + * * * Minify::setCache(new Minify_Cache_APC()); * - * + * * @package Minify * @author Chris Edwards **/ @@ -57,6 +57,7 @@ class Minify_Cache_APC implements Minify_CacheInterface { if (! $this->_fetch($id)) { return false; } + return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data); @@ -124,10 +125,12 @@ class Minify_Cache_APC implements Minify_CacheInterface { $ret = apc_fetch($id); if (false === $ret) { $this->_id = null; + return false; } list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; + return true; } } diff --git a/lib/Minify/Cache/File.php b/lib/Minify/Cache/File.php index 9dba142..d4b6f42 100644 --- a/lib/Minify/Cache/File.php +++ b/lib/Minify/Cache/File.php @@ -1,11 +1,11 @@ fetch($id)) { @unlink($file); $this->_log("Minify_Cache_File: Post-write read failed for '$file'"); + return false; } + return true; } - + /** * 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 (is_file($file) && (filemtime($file) >= $srcMtime)); } - + /** * Send the cached content to output * @@ -83,15 +86,15 @@ class Minify_Cache_File implements Minify_CacheInterface { flock($fp, LOCK_UN); fclose($fp); } else { - readfile($this->_path . '/' . $id); + readfile($this->_path . '/' . $id); } } - + /** * Fetch the cached content * * @param string $id cache id (e.g. a filename) - * + * * @return string */ public function fetch($id) @@ -105,12 +108,13 @@ class Minify_Cache_File implements Minify_CacheInterface { $ret = stream_get_contents($fp); flock($fp, LOCK_UN); fclose($fp); + return $ret; } else { return file_get_contents($this->_path . '/' . $id); } } - + /** * Fetch the cache path used * @@ -140,6 +144,7 @@ class Minify_Cache_File implements Minify_CacheInterface { : self::_tmp(); $tmp = rtrim($tmp, DIRECTORY_SEPARATOR); } + return $tmp; } @@ -191,7 +196,7 @@ class Minify_Cache_File implements Minify_CacheInterface { { Minify_Logger::log($msg); } - + private $_path = null; private $_locking = null; } diff --git a/lib/Minify/Cache/Memcache.php b/lib/Minify/Cache/Memcache.php index 2861fe0..ad6c480 100644 --- a/lib/Minify/Cache/Memcache.php +++ b/lib/Minify/Cache/Memcache.php @@ -6,7 +6,7 @@ /** * Memcache-based cache class for Minify - * + * * * // fall back to disk caching if memcache can't connect * $memcache = new Memcache; @@ -18,13 +18,13 @@ * **/ class Minify_Cache_Memcache implements Minify_CacheInterface { - + /** - * Create a Minify_Cache_Memcache object, to be passed to + * Create a Minify_Cache_Memcache object, to be passed to * Minify::setCache(). * * @param Memcache $memcache already-connected instance - * + * * @param int $expire seconds until expiration (default = 0 * meaning the item will not get an expiration date) */ @@ -33,26 +33,26 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { $this->_mc = $memcache; $this->_exp = $expire; } - + /** * Write data to cache. * * @param string $id cache id - * + * * @param string $data - * + * * @return bool success */ public function store($id, $data) { return $this->_mc->set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", 0, $this->_exp); } - + /** * Get the size of a cache entry * * @param string $id cache id - * + * * @return int size in bytes */ public function getSize($id) @@ -60,25 +60,26 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { if (! $this->_fetch($id)) { return false; } + return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data); } - + /** * Does a valid cache entry exist? * * @param string $id cache id - * + * * @param int $srcMtime mtime of the original source file(s) - * + * * @return bool exists */ public function isValid($id, $srcMtime) { return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); } - + /** * Send the cached content to output * @@ -90,12 +91,12 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { ? $this->_data : ''; } - + /** * Fetch the cached content * * @param string $id cache id - * + * * @return string */ public function fetch($id) @@ -104,20 +105,20 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { ? $this->_data : ''; } - + private $_mc = null; private $_exp = null; - + // cache of most recently fetched id private $_lm = null; private $_data = null; private $_id = null; - + /** * Fetch data and timestamp from memcache, store in instance - * + * * @param string $id - * + * * @return bool success */ private function _fetch($id) @@ -128,10 +129,12 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { $ret = $this->_mc->get($id); if (false === $ret) { $this->_id = null; + return false; } list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; + return true; } } diff --git a/lib/Minify/Cache/WinCache.php b/lib/Minify/Cache/WinCache.php index 504da79..4097500 100644 --- a/lib/Minify/Cache/WinCache.php +++ b/lib/Minify/Cache/WinCache.php @@ -6,11 +6,11 @@ /** * WinCache-based cache class for Minify - * + * * * Minify::setCache(new Minify_Cache_WinCache()); * - * + * * @package Minify * @author Matthias Fax **/ @@ -32,7 +32,7 @@ class Minify_Cache_WinCache implements Minify_CacheInterface } $this->_exp = $expire; } - + /** * Write data to cache. * @@ -46,7 +46,7 @@ class Minify_Cache_WinCache implements Minify_CacheInterface { return wincache_ucache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp); } - + /** * Get the size of a cache entry * @@ -59,9 +59,10 @@ class Minify_Cache_WinCache implements Minify_CacheInterface if (!$this->_fetch($id)) { return false; } + return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data); } - + /** * Does a valid cache entry exist? * @@ -75,7 +76,7 @@ class Minify_Cache_WinCache implements Minify_CacheInterface { return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); } - + /** * Send the cached content to output * @@ -85,7 +86,7 @@ class Minify_Cache_WinCache implements Minify_CacheInterface { echo $this->_fetch($id) ? $this->_data : ''; } - + /** * Fetch the cached content * @@ -97,14 +98,14 @@ class Minify_Cache_WinCache implements Minify_CacheInterface { return $this->_fetch($id) ? $this->_data : ''; } - + private $_exp = NULL; - + // cache of most recently fetched id private $_lm = NULL; private $_data = NULL; private $_id = NULL; - + /** * Fetch data and timestamp from WinCache, store in instance * @@ -121,10 +122,12 @@ class Minify_Cache_WinCache implements Minify_CacheInterface $ret = wincache_ucache_get($id, $suc); if (!$suc) { $this->_id = NULL; + return false; } list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; + return true; } } \ No newline at end of file diff --git a/lib/Minify/Cache/XCache.php b/lib/Minify/Cache/XCache.php index 59b18c2..4edd261 100644 --- a/lib/Minify/Cache/XCache.php +++ b/lib/Minify/Cache/XCache.php @@ -54,6 +54,7 @@ class Minify_Cache_XCache implements Minify_CacheInterface { if (! $this->_fetch($id)) { return false; } + return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data); @@ -117,10 +118,12 @@ class Minify_Cache_XCache implements Minify_CacheInterface { $ret = xcache_get($id); if (false === $ret) { $this->_id = null; + return false; } list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; + return true; } } diff --git a/lib/Minify/Cache/ZendPlatform.php b/lib/Minify/Cache/ZendPlatform.php index 25801ba..9e5d248 100644 --- a/lib/Minify/Cache/ZendPlatform.php +++ b/lib/Minify/Cache/ZendPlatform.php @@ -4,12 +4,11 @@ * @package Minify */ - /** * ZendPlatform-based cache class for Minify * * Based on Minify_Cache_APC, uses output_cache_get/put (currently deprecated) - * + * * * Minify::setCache(new Minify_Cache_ZendPlatform()); * @@ -19,7 +18,6 @@ */ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { - /** * Create a Minify_Cache_ZendPlatform object, to be passed to * Minify::setCache(). @@ -33,7 +31,6 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { $this->_exp = $expire; } - /** * Write data to cache. * @@ -48,7 +45,6 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { return output_cache_put($id, "{$_SERVER['REQUEST_TIME']}|{$data}"); } - /** * Get the size of a cache entry * @@ -75,6 +71,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { public function isValid($id, $srcMtime) { $ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime)); + return $ret; } @@ -90,7 +87,6 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { : ''; } - /** * Fetch the cached content * @@ -127,10 +123,12 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { $ret = output_cache_get($id, $this->_exp); if (false === $ret) { $this->_id = null; + return false; } list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; + return true; } } diff --git a/lib/Minify/CacheInterface.php b/lib/Minify/CacheInterface.php index ffc5fe7..56b79b8 100644 --- a/lib/Minify/CacheInterface.php +++ b/lib/Minify/CacheInterface.php @@ -4,7 +4,6 @@ * @package Minify */ - /** * Interface for Minify cache adapters * diff --git a/lib/Minify/ClosureCompiler.php b/lib/Minify/ClosureCompiler.php index 99658cb..487cafe 100644 --- a/lib/Minify/ClosureCompiler.php +++ b/lib/Minify/ClosureCompiler.php @@ -73,6 +73,7 @@ class Minify_ClosureCompiler public static function minify($js, $options = array()) { $min = new static(); + return $min->process($js, $options); } @@ -123,6 +124,7 @@ class Minify_ClosureCompiler $this->getCompilerCommandLine(), $this->getOptionsCommandLine($userOptions) ); + return join(' ', $args) . ' ' . escapeshellarg($tmpFile); } @@ -137,6 +139,7 @@ class Minify_ClosureCompiler self::$javaExecutable, '-jar', escapeshellarg(self::$jarFile) ); + return $server; } @@ -215,6 +218,7 @@ class Minify_ClosureCompiler throw new Minify_ClosureCompiler_Exception('Could not create temp file in "' . $dir . '".'); } file_put_contents($tmpFile, $content); + return $tmpFile; } @@ -232,6 +236,7 @@ class Minify_ClosureCompiler if (!in_array($result_code, $expectedCodes)) { throw new Minify_ClosureCompiler_Exception("Unpexpected return code: $result_code"); } + return $output; } } diff --git a/lib/Minify/CommentPreserver.php b/lib/Minify/CommentPreserver.php index 7a359bf..00db9ff 100644 --- a/lib/Minify/CommentPreserver.php +++ b/lib/Minify/CommentPreserver.php @@ -1,41 +1,41 @@ */ class Minify_CommentPreserver { - + /** * String to be prepended to each preserved comment * * @var string */ public static $prepend = "\n"; - + /** * String to be appended to each preserved comment * * @var string */ public static $append = "\n"; - + /** * Process a string outside of C-style comments that begin with "/*!" * - * On each non-empty string outside these comments, the given processor - * function will be called. The comments will be surrounded by + * On each non-empty string outside these comments, the given processor + * function will be called. The comments will be surrounded by * Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append. - * + * * @param string $content * @param callback $processor function - * @param array $args array of extra arguments to pass to the processor + * @param array $args array of extra arguments to pass to the processor * function (default = array()) * @return string */ @@ -47,7 +47,7 @@ class Minify_CommentPreserver { if ('' !== $beforeComment) { $callArgs = $args; array_unshift($callArgs, $beforeComment); - $ret .= call_user_func_array($processor, $callArgs); + $ret .= call_user_func_array($processor, $callArgs); } if (false === $comment) { break; @@ -55,17 +55,18 @@ class Minify_CommentPreserver { $ret .= $comment; $content = $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 3rd are the surrounding - * strings. If no comment is found, the entire string is returned as the + * strings. If no comment is found, the entire string is returned as the * 1st element and the other two are false. */ private static function _nextComment($in) @@ -84,6 +85,7 @@ class Minify_CommentPreserver { $ret[] = (0 === $endChars) ? '' : substr($in, -$endChars); + return $ret; } } diff --git a/lib/Minify/Controller/Base.php b/lib/Minify/Controller/Base.php index b601d77..1810099 100644 --- a/lib/Minify/Controller/Base.php +++ b/lib/Minify/Controller/Base.php @@ -1,14 +1,14 @@ */ @@ -36,13 +36,13 @@ abstract class Minify_Controller_Base implements Minify_ControllerInterface { /** * Create controller sources and options for Minify::serve() - * + * * @param array $options controller and Minify options - * + * * @return Minify_ServeConfiguration */ abstract public function createConfiguration(array $options); - + /** * Send message to the Minify logger * diff --git a/lib/Minify/Controller/Files.php b/lib/Minify/Controller/Files.php index 23c54e0..d687305 100644 --- a/lib/Minify/Controller/Files.php +++ b/lib/Minify/Controller/Files.php @@ -1,12 +1,12 @@ * $options = [ @@ -40,17 +40,17 @@ class Minify_Controller_Files extends Minify_Controller_Base { /** * Set up file sources - * + * * @param array $options controller and Minify options * @return Minify_ServeConfiguration - * + * * Controller options: - * + * * 'files': (required) array of complete file paths, or a single path */ public function createConfiguration(array $options) { // strip controller options - + $files = $options['files']; // if $files is a single object, casting will break it if (is_object($files)) { @@ -59,7 +59,7 @@ class Minify_Controller_Files extends Minify_Controller_Base { $files = (array)$files; } unset($options['files']); - + $sources = array(); foreach ($files as $file) { if ($file instanceof Minify_SourceInterface) { @@ -72,9 +72,11 @@ class Minify_Controller_Files extends Minify_Controller_Base { )); } catch (Minify_Source_FactoryException $e) { $this->log($e->getMessage()); + return new Minify_ServeConfiguration($options); } } + return new Minify_ServeConfiguration($options, $sources); } } diff --git a/lib/Minify/Controller/Groups.php b/lib/Minify/Controller/Groups.php index 9fec54c..e911b88 100644 --- a/lib/Minify/Controller/Groups.php +++ b/lib/Minify/Controller/Groups.php @@ -1,33 +1,33 @@ - * Minify::serve('Groups', array( + * 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 - * + * * @package Minify * @author Stephen Clay */ class Minify_Controller_Groups extends Minify_Controller_Files { - + /** * Set up groups of files as sources - * + * * @param array $options controller and Minify options * * 'groups': (required) array mapping PATH_INFO strings to arrays @@ -41,7 +41,7 @@ class Minify_Controller_Groups extends Minify_Controller_Files { unset($options['groups']); $server = $this->env->server(); - + // mod_fcgid places PATH_INFO in ORIG_PATH_INFO $pathInfo = isset($server['ORIG_PATH_INFO']) ? substr($server['ORIG_PATH_INFO'], 1) @@ -52,6 +52,7 @@ class Minify_Controller_Groups extends Minify_Controller_Files { if (false === $pathInfo || ! isset($groups[$pathInfo])) { // no PATH_INFO or not a valid group $this->log("Missing PATH_INFO or no group set for \"$pathInfo\""); + return new Minify_ServeConfiguration($options); } diff --git a/lib/Minify/Controller/MinApp.php b/lib/Minify/Controller/MinApp.php index 6f58380..6517e8a 100644 --- a/lib/Minify/Controller/MinApp.php +++ b/lib/Minify/Controller/MinApp.php @@ -6,7 +6,7 @@ /** * Controller class for requests to /min/index.php - * + * * @package Minify * @author Stephen Clay */ @@ -14,7 +14,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { /** * Set up groups of files as sources - * + * * @param array $options controller and Minify options * * @return array Minify options @@ -59,11 +59,13 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $keys = explode(',', $get['g']); if ($keys != array_unique($keys)) { $this->log("Duplicate group key found."); + return new Minify_ServeConfiguration($options); } foreach ($keys as $key) { if (! isset($localOptions['groups'][$key])) { $this->log("A group configuration for \"{$key}\" was not found"); + return new Minify_ServeConfiguration($options); } $files = $localOptions['groups'][$key]; @@ -91,6 +93,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { } else { $secondMissingResource = basename($file); $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource'"); + return new Minify_ServeConfiguration($options); } } @@ -101,7 +104,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { // try user files // The following restrictions are to limit the URLs that minify will // respond to. - if (// verify at least one file, files are single comma separated, + if (// verify at least one file, files are single comma separated, // and are all same extension ! preg_match('/^[^,]+\\.(css|less|js)(?:,[^,]+\\.\\1)*$/', $get['f'], $m) // no "//" @@ -110,12 +113,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { || strpos($get['f'], '\\') !== false ) { $this->log("GET param 'f' was invalid"); + return new Minify_ServeConfiguration($options); } $ext = ".{$m[1]}"; $files = explode(',', $get['f']); if ($files != array_unique($files)) { $this->log("Duplicate files were specified"); + return new Minify_ServeConfiguration($options); } if (isset($get['b'])) { @@ -127,6 +132,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $base = "/{$get['b']}/"; } else { $this->log("GET param 'b' was invalid"); + return new Minify_ServeConfiguration($options); } } else { @@ -160,6 +166,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { } else { $secondMissingResource = $uri; $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'"); + return new Minify_ServeConfiguration($options); } } @@ -172,6 +179,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { if (!$sources) { $this->log("No sources to serve"); + return new Minify_ServeConfiguration($options); } diff --git a/lib/Minify/Controller/Page.php b/lib/Minify/Controller/Page.php index 5668c63..8120479 100644 --- a/lib/Minify/Controller/Page.php +++ b/lib/Minify/Controller/Page.php @@ -1,34 +1,34 @@ */ 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 + * + * 'minifyAll': should all CSS and Javascript blocks be individually * minified? (default false) */ public function createConfiguration(array $options) { diff --git a/lib/Minify/DebugDetector.php b/lib/Minify/DebugDetector.php index f20112e..44e4eb8 100644 --- a/lib/Minify/DebugDetector.php +++ b/lib/Minify/DebugDetector.php @@ -1,27 +1,28 @@ - - */ -class Minify_DebugDetector { - public static function shouldDebugRequest(Minify_Env $env) - { - if ($env->get('debug')) { - return true; - } - $cookieValue = $env->cookie('minifyDebug'); - if ($cookieValue) { - foreach (preg_split('/\\s+/', $cookieValue) as $debugUri) { - $pattern = '@' . preg_quote($debugUri, '@') . '@i'; - $pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern); - if (preg_match($pattern, $env->getRequestUri())) { - return true; - } - } - } - return false; - } -} + + */ +class Minify_DebugDetector { + public static function shouldDebugRequest(Minify_Env $env) + { + if ($env->get('debug')) { + return true; + } + $cookieValue = $env->cookie('minifyDebug'); + if ($cookieValue) { + foreach (preg_split('/\\s+/', $cookieValue) as $debugUri) { + $pattern = '@' . preg_quote($debugUri, '@') . '@i'; + $pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern); + if (preg_match($pattern, $env->getRequestUri())) { + return true; + } + } + } + + return false; + } +} diff --git a/lib/Minify/Env.php b/lib/Minify/Env.php index d7260bb..7622b5e 100644 --- a/lib/Minify/Env.php +++ b/lib/Minify/Env.php @@ -41,6 +41,7 @@ class Minify_Env { if (null === $key) { return $this->server; } + return isset($this->server[$key]) ? $this->server[$key] : null; @@ -51,6 +52,7 @@ class Minify_Env { if (null === $key) { return $this->cookie; } + return isset($this->cookie[$key]) ? $this->cookie[$key] : null; @@ -61,6 +63,7 @@ class Minify_Env { if (null === $key) { return $this->get; } + return isset($this->get[$key]) ? $this->get[$key] : null; @@ -87,6 +90,7 @@ class Minify_Env { ,0 ,strlen($server['SCRIPT_FILENAME']) - strlen($server['SCRIPT_NAME']) ); + return rtrim($docRoot, '\\'); } } diff --git a/lib/Minify/HTML.php b/lib/Minify/HTML.php index 2b0a4ab..ce01650 100644 --- a/lib/Minify/HTML.php +++ b/lib/Minify/HTML.php @@ -1,255 +1,256 @@ - - */ -class Minify_HTML { - /** - * @var boolean - */ - protected $_jsCleanComments = true; - - /** - * "Minify" an HTML page - * - * @param string $html - * - * @param array $options - * - * 'cssMinifier' : (optional) callback function to process content of STYLE - * elements. - * - * 'jsMinifier' : (optional) callback function to process content of SCRIPT - * elements. Note: the type attribute is ignored. - * - * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If - * unset, minify will sniff for an XHTML doctype. - * - * @return string - */ - public static function minify($html, $options = array()) { - $min = new self($html, $options); - return $min->process(); - } - - - /** - * Create a minifier object - * - * @param string $html - * - * @param array $options - * - * 'cssMinifier' : (optional) callback function to process content of STYLE - * elements. - * - * 'jsMinifier' : (optional) callback function to process content of SCRIPT - * elements. Note: the type attribute is ignored. - * - * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block - * - * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If - * unset, minify will sniff for an XHTML doctype. - */ - public function __construct($html, $options = array()) - { - $this->_html = str_replace("\r\n", "\n", trim($html)); - if (isset($options['xhtml'])) { - $this->_isXhtml = (bool)$options['xhtml']; - } - if (isset($options['cssMinifier'])) { - $this->_cssMinifier = $options['cssMinifier']; - } - if (isset($options['jsMinifier'])) { - $this->_jsMinifier = $options['jsMinifier']; - } - if (isset($options['jsCleanComments'])) { - $this->_jsCleanComments = (bool)$options['jsCleanComments']; - } - } - - - /** - * Minify the markeup given in the constructor - * - * @return string - */ - public function process() - { - if ($this->_isXhtml === null) { - $this->_isXhtml = (false !== strpos($this->_html, '_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); - $this->_placeholders = array(); - - // replace SCRIPTs (and minify) with placeholders - $this->_html = preg_replace_callback( - '/(\\s*)]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' - ,array($this, '_removeScriptCB') - ,$this->_html); - - // replace STYLEs (and minify) with placeholders - $this->_html = preg_replace_callback( - '/\\s*]*>)([\\s\\S]*?)<\\/style>\\s*/i' - ,array($this, '_removeStyleCB') - ,$this->_html); - - // remove HTML comments (not containing IE conditional comments). - $this->_html = preg_replace_callback( - '//' - ,array($this, '_commentCB') - ,$this->_html); - - // replace PREs with placeholders - $this->_html = preg_replace_callback('/\\s*]*?>[\\s\\S]*?<\\/pre>)\\s*/i' - ,array($this, '_removePreCB') - ,$this->_html); - - // replace TEXTAREAs with placeholders - $this->_html = preg_replace_callback( - '/\\s*]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' - ,array($this, '_removeTextareaCB') - ,$this->_html); - - // trim each line. - // @todo take into account attribute values that span multiple lines. - $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); - - // remove ws around block/undisplayed elements - $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body' - .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form' - .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav' - .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)' - .'|ul|video)\\b[^>]*>)/i', '$1', $this->_html); - - // remove ws outside of all elements - $this->_html = preg_replace( - '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?$1$2$3<' - ,$this->_html); - - // use newlines before 1st attribute in open tags (to limit line lengths) - $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); - - // fill placeholders - $this->_html = str_replace( - array_keys($this->_placeholders) - ,array_values($this->_placeholders) - ,$this->_html - ); - // issue 229: multi-pass to catch scripts that didn't get replaced in textareas - $this->_html = str_replace( - array_keys($this->_placeholders) - ,array_values($this->_placeholders) - ,$this->_html - ); - return $this->_html; - } - - protected function _commentCB($m) - { - return (0 === strpos($m[1], '[') || false !== strpos($m[1], '_replacementHash . count($this->_placeholders) . '%'; - $this->_placeholders[$placeholder] = $content; - return $placeholder; - } - - protected $_isXhtml = null; - protected $_replacementHash = null; - protected $_placeholders = array(); - protected $_cssMinifier = null; - protected $_jsMinifier = null; - - protected function _removePreCB($m) - { - return $this->_reservePlace("_reservePlace("\\s*$)/', '', $css); - - // remove CDATA section markers - $css = $this->_removeCdata($css); - - // minify - $minifier = $this->_cssMinifier - ? $this->_cssMinifier - : 'trim'; - $css = call_user_func($minifier, $css); - - return $this->_reservePlace($this->_needsCdata($css) - ? "{$openStyle}/**/" - : "{$openStyle}{$css}" - ); - } - - protected function _removeScriptCB($m) - { - $openScript = "_jsCleanComments) { - $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); - } - - // remove CDATA section markers - $js = $this->_removeCdata($js); - - // minify - $minifier = $this->_jsMinifier - ? $this->_jsMinifier - : 'trim'; - $js = call_user_func($minifier, $js); - - return $this->_reservePlace($this->_needsCdata($js) - ? "{$ws1}{$openScript}/**/{$ws2}" - : "{$ws1}{$openScript}{$js}{$ws2}" - ); - } - - protected function _removeCdata($str) - { - return (false !== strpos($str, ''), '', $str) - : $str; - } - - protected function _needsCdata($str) - { - return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); - } -} + + */ +class Minify_HTML { + /** + * @var boolean + */ + protected $_jsCleanComments = true; + + /** + * "Minify" an HTML page + * + * @param string $html + * + * @param array $options + * + * 'cssMinifier' : (optional) callback function to process content of STYLE + * elements. + * + * 'jsMinifier' : (optional) callback function to process content of SCRIPT + * elements. Note: the type attribute is ignored. + * + * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If + * unset, minify will sniff for an XHTML doctype. + * + * @return string + */ + public static function minify($html, $options = array()) { + $min = new self($html, $options); + + return $min->process(); + } + + /** + * Create a minifier object + * + * @param string $html + * + * @param array $options + * + * 'cssMinifier' : (optional) callback function to process content of STYLE + * elements. + * + * 'jsMinifier' : (optional) callback function to process content of SCRIPT + * elements. Note: the type attribute is ignored. + * + * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block + * + * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If + * unset, minify will sniff for an XHTML doctype. + */ + public function __construct($html, $options = array()) + { + $this->_html = str_replace("\r\n", "\n", trim($html)); + if (isset($options['xhtml'])) { + $this->_isXhtml = (bool)$options['xhtml']; + } + if (isset($options['cssMinifier'])) { + $this->_cssMinifier = $options['cssMinifier']; + } + if (isset($options['jsMinifier'])) { + $this->_jsMinifier = $options['jsMinifier']; + } + if (isset($options['jsCleanComments'])) { + $this->_jsCleanComments = (bool)$options['jsCleanComments']; + } + } + + /** + * Minify the markeup given in the constructor + * + * @return string + */ + public function process() + { + if ($this->_isXhtml === null) { + $this->_isXhtml = (false !== strpos($this->_html, '_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); + $this->_placeholders = array(); + + // replace SCRIPTs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/(\\s*)]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' + ,array($this, '_removeScriptCB') + ,$this->_html); + + // replace STYLEs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/\\s*]*>)([\\s\\S]*?)<\\/style>\\s*/i' + ,array($this, '_removeStyleCB') + ,$this->_html); + + // remove HTML comments (not containing IE conditional comments). + $this->_html = preg_replace_callback( + '//' + ,array($this, '_commentCB') + ,$this->_html); + + // replace PREs with placeholders + $this->_html = preg_replace_callback('/\\s*]*?>[\\s\\S]*?<\\/pre>)\\s*/i' + ,array($this, '_removePreCB') + ,$this->_html); + + // replace TEXTAREAs with placeholders + $this->_html = preg_replace_callback( + '/\\s*]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' + ,array($this, '_removeTextareaCB') + ,$this->_html); + + // trim each line. + // @todo take into account attribute values that span multiple lines. + $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); + + // remove ws around block/undisplayed elements + $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body' + .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form' + .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav' + .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)' + .'|ul|video)\\b[^>]*>)/i', '$1', $this->_html); + + // remove ws outside of all elements + $this->_html = preg_replace( + '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?$1$2$3<' + ,$this->_html); + + // use newlines before 1st attribute in open tags (to limit line lengths) + $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); + + // fill placeholders + $this->_html = str_replace( + array_keys($this->_placeholders) + ,array_values($this->_placeholders) + ,$this->_html + ); + // issue 229: multi-pass to catch scripts that didn't get replaced in textareas + $this->_html = str_replace( + array_keys($this->_placeholders) + ,array_values($this->_placeholders) + ,$this->_html + ); + + return $this->_html; + } + + protected function _commentCB($m) + { + return (0 === strpos($m[1], '[') || false !== strpos($m[1], '_replacementHash . count($this->_placeholders) . '%'; + $this->_placeholders[$placeholder] = $content; + + return $placeholder; + } + + protected $_isXhtml = null; + protected $_replacementHash = null; + protected $_placeholders = array(); + protected $_cssMinifier = null; + protected $_jsMinifier = null; + + protected function _removePreCB($m) + { + return $this->_reservePlace("_reservePlace("\\s*$)/', '', $css); + + // remove CDATA section markers + $css = $this->_removeCdata($css); + + // minify + $minifier = $this->_cssMinifier + ? $this->_cssMinifier + : 'trim'; + $css = call_user_func($minifier, $css); + + return $this->_reservePlace($this->_needsCdata($css) + ? "{$openStyle}/**/" + : "{$openStyle}{$css}" + ); + } + + protected function _removeScriptCB($m) + { + $openScript = "_jsCleanComments) { + $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); + } + + // remove CDATA section markers + $js = $this->_removeCdata($js); + + // minify + $minifier = $this->_jsMinifier + ? $this->_jsMinifier + : 'trim'; + $js = call_user_func($minifier, $js); + + return $this->_reservePlace($this->_needsCdata($js) + ? "{$ws1}{$openScript}/**/{$ws2}" + : "{$ws1}{$openScript}{$js}{$ws2}" + ); + } + + protected function _removeCdata($str) + { + return (false !== strpos($str, ''), '', $str) + : $str; + } + + protected function _needsCdata($str) + { + return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); + } +} diff --git a/lib/Minify/HTML/Helper.php b/lib/Minify/HTML/Helper.php index e31c59f..5be4300 100644 --- a/lib/Minify/HTML/Helper.php +++ b/lib/Minify/HTML/Helper.php @@ -48,6 +48,7 @@ class Minify_HTML_Helper { $h->setGroup($keyOrFiles, $opts['farExpires']); } $uri = $h->getRawUri($opts['farExpires'], $opts['debug']); + return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']); } @@ -75,6 +76,7 @@ class Minify_HTML_Helper { } elseif ($farExpires && $this->_lastModified) { $path .= "&" . $this->_lastModified; } + return $path; } @@ -156,6 +158,7 @@ class Minify_HTML_Helper { } } } + return $max; } @@ -163,7 +166,6 @@ class Minify_HTML_Helper { protected $_filePaths = array(); protected $_lastModified = null; - /** * In a given array of strings, find the character they all have at * a particular index @@ -186,6 +188,7 @@ class Minify_HTML_Helper { return ''; } } + return $c; } @@ -225,6 +228,7 @@ class Minify_HTML_Helper { ? $uri : $bUri; } + return $uri; } } diff --git a/lib/Minify/ImportProcessor.php b/lib/Minify/ImportProcessor.php index bdfae54..ce8e099 100644 --- a/lib/Minify/ImportProcessor.php +++ b/lib/Minify/ImportProcessor.php @@ -1,216 +1,221 @@ - - * @author Simon Schick - */ -class Minify_ImportProcessor { - - public static $filesIncluded = array(); - - public static function process($file) - { - self::$filesIncluded = array(); - self::$_isCss = (strtolower(substr($file, -4)) === '.css'); - $obj = new Minify_ImportProcessor(dirname($file)); - return $obj->_getContent($file); - } - - // allows callback funcs to know the current directory - private $_currentDir = null; - - // allows callback funcs to know the directory of the file that inherits this one - private $_previewsDir = null; - - // allows _importCB to write the fetched content back to the obj - private $_importedContent = ''; - - private static $_isCss = null; - - /** - * @param String $currentDir - * @param String $previewsDir Is only used internally - */ - private function __construct($currentDir, $previewsDir = "") - { - $this->_currentDir = $currentDir; - $this->_previewsDir = $previewsDir; - } - - private function _getContent($file, $is_imported = false) - { - $file = realpath($file); - if (! $file - || in_array($file, self::$filesIncluded) - || false === ($content = @file_get_contents($file)) - ) { - // file missing, already included, or failed read - return ''; - } - self::$filesIncluded[] = realpath($file); - $this->_currentDir = dirname($file); - - // remove UTF-8 BOM if present - if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) { - $content = substr($content, 3); - } - // ensure uniform EOLs - $content = str_replace("\r\n", "\n", $content); - - // process @imports - $content = preg_replace_callback( - '/ - @import\\s+ - (?:url\\(\\s*)? # maybe url( - [\'"]? # maybe quote - (.*?) # 1 = URI - [\'"]? # maybe end quote - (?:\\s*\\))? # maybe ) - ([a-zA-Z,\\s]*)? # 2 = media list - ; # end token - /x' - ,array($this, '_importCB') - ,$content - ); - - // You only need to rework the import-path if the script is imported - if (self::$_isCss && $is_imported) { - // rewrite remaining relative URIs - $content = preg_replace_callback( - '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' - ,array($this, '_urlCB') - ,$content - ); - } - - return $this->_importedContent . $content; - } - - private function _importCB($m) - { - $url = $m[1]; - $mediaList = preg_replace('/\\s+/', '', $m[2]); - - if (strpos($url, '://') > 0) { - // protocol, leave in place for CSS, comment for JS - return self::$_isCss - ? $m[0] - : "/* Minify_ImportProcessor will not include remote content */"; - } - if ('/' === $url[0]) { - // protocol-relative or root path - $url = ltrim($url, '/'); - $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR - . strtr($url, '/', DIRECTORY_SEPARATOR); - } else { - // relative to current path - $file = $this->_currentDir . DIRECTORY_SEPARATOR - . strtr($url, '/', DIRECTORY_SEPARATOR); - } - $obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir); - $content = $obj->_getContent($file, true); - if ('' === $content) { - // failed. leave in place for CSS, comment for JS - return self::$_isCss - ? $m[0] - : "/* Minify_ImportProcessor could not fetch '{$file}' */"; - } - return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList)) - ? $content - : "@media {$mediaList} {\n{$content}\n}\n"; - } - - private function _urlCB($m) - { - // $m[1] is either quoted or not - $quote = ($m[1][0] === "'" || $m[1][0] === '"') - ? $m[1][0] - : ''; - $url = ($quote === '') - ? $m[1] - : substr($m[1], 1, strlen($m[1]) - 2); - if ('/' !== $url[0]) { - if (strpos($url, '//') > 0) { - // probably starts with protocol, do not alter - } else { - // prepend path with current dir separator (OS-independent) - $path = $this->_currentDir - . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR); - // update the relative path by the directory of the file that imported this one - $url = self::getPathDiff(realpath($this->_previewsDir), $path); - } - } - return "url({$quote}{$url}{$quote})"; - } - - /** - * @param string $from - * @param string $to - * @param string $ps - * @return string - */ - private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR) - { - $realFrom = $this->truepath($from); - $realTo = $this->truepath($to); - - $arFrom = explode($ps, rtrim($realFrom, $ps)); - $arTo = explode($ps, rtrim($realTo, $ps)); - while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) - { - array_shift($arFrom); - array_shift($arTo); - } - return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo); - } - - /** - * This function is to replace PHP's extremely buggy realpath(). - * @param string $path The original path, can be relative etc. - * @return string The resolved path, it might not exist. - * @see http://stackoverflow.com/questions/4049856/replace-phps-realpath - */ - function truepath($path) - { - // whether $path is unix or not - $unipath = strlen($path) == 0 || $path{0} != '/'; - // attempts to detect if path is relative in which case, add cwd - if (strpos($path, ':') === false && $unipath) - $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path; - - // resolve path parts (single dot, double dot and double delimiters) - $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); - $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); - $absolutes = array(); - foreach ($parts as $part) { - if ('.' == $part) - continue; - if ('..' == $part) { - array_pop($absolutes); - } else { - $absolutes[] = $part; - } - } - $path = implode(DIRECTORY_SEPARATOR, $absolutes); - // resolve any symlinks - if (file_exists($path) && linkinfo($path) > 0) - $path = readlink($path); - // put initial separator that could have been lost - $path = !$unipath ? '/' . $path : $path; - return $path; - } -} + + * @author Simon Schick + */ +class Minify_ImportProcessor { + + public static $filesIncluded = array(); + + public static function process($file) + { + self::$filesIncluded = array(); + self::$_isCss = (strtolower(substr($file, -4)) === '.css'); + $obj = new Minify_ImportProcessor(dirname($file)); + + return $obj->_getContent($file); + } + + // allows callback funcs to know the current directory + private $_currentDir = null; + + // allows callback funcs to know the directory of the file that inherits this one + private $_previewsDir = null; + + // allows _importCB to write the fetched content back to the obj + private $_importedContent = ''; + + private static $_isCss = null; + + /** + * @param String $currentDir + * @param String $previewsDir Is only used internally + */ + private function __construct($currentDir, $previewsDir = "") + { + $this->_currentDir = $currentDir; + $this->_previewsDir = $previewsDir; + } + + private function _getContent($file, $is_imported = false) + { + $file = realpath($file); + if (! $file + || in_array($file, self::$filesIncluded) + || false === ($content = @file_get_contents($file)) + ) { + // file missing, already included, or failed read + return ''; + } + self::$filesIncluded[] = realpath($file); + $this->_currentDir = dirname($file); + + // remove UTF-8 BOM if present + if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) { + $content = substr($content, 3); + } + // ensure uniform EOLs + $content = str_replace("\r\n", "\n", $content); + + // process @imports + $content = preg_replace_callback( + '/ + @import\\s+ + (?:url\\(\\s*)? # maybe url( + [\'"]? # maybe quote + (.*?) # 1 = URI + [\'"]? # maybe end quote + (?:\\s*\\))? # maybe ) + ([a-zA-Z,\\s]*)? # 2 = media list + ; # end token + /x' + ,array($this, '_importCB') + ,$content + ); + + // You only need to rework the import-path if the script is imported + if (self::$_isCss && $is_imported) { + // rewrite remaining relative URIs + $content = preg_replace_callback( + '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' + ,array($this, '_urlCB') + ,$content + ); + } + + return $this->_importedContent . $content; + } + + private function _importCB($m) + { + $url = $m[1]; + $mediaList = preg_replace('/\\s+/', '', $m[2]); + + if (strpos($url, '://') > 0) { + // protocol, leave in place for CSS, comment for JS + return self::$_isCss + ? $m[0] + : "/* Minify_ImportProcessor will not include remote content */"; + } + if ('/' === $url[0]) { + // protocol-relative or root path + $url = ltrim($url, '/'); + $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR + . strtr($url, '/', DIRECTORY_SEPARATOR); + } else { + // relative to current path + $file = $this->_currentDir . DIRECTORY_SEPARATOR + . strtr($url, '/', DIRECTORY_SEPARATOR); + } + $obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir); + $content = $obj->_getContent($file, true); + if ('' === $content) { + // failed. leave in place for CSS, comment for JS + return self::$_isCss + ? $m[0] + : "/* Minify_ImportProcessor could not fetch '{$file}' */"; + } + + return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList)) + ? $content + : "@media {$mediaList} {\n{$content}\n}\n"; + } + + private function _urlCB($m) + { + // $m[1] is either quoted or not + $quote = ($m[1][0] === "'" || $m[1][0] === '"') + ? $m[1][0] + : ''; + $url = ($quote === '') + ? $m[1] + : substr($m[1], 1, strlen($m[1]) - 2); + if ('/' !== $url[0]) { + if (strpos($url, '//') > 0) { + // probably starts with protocol, do not alter + } else { + // prepend path with current dir separator (OS-independent) + $path = $this->_currentDir + . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR); + // update the relative path by the directory of the file that imported this one + $url = self::getPathDiff(realpath($this->_previewsDir), $path); + } + } + + return "url({$quote}{$url}{$quote})"; + } + + /** + * @param string $from + * @param string $to + * @param string $ps + * @return string + */ + private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR) + { + $realFrom = $this->truepath($from); + $realTo = $this->truepath($to); + + $arFrom = explode($ps, rtrim($realFrom, $ps)); + $arTo = explode($ps, rtrim($realTo, $ps)); + while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) + { + array_shift($arFrom); + array_shift($arTo); + } + + return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo); + } + + /** + * This function is to replace PHP's extremely buggy realpath(). + * @param string $path The original path, can be relative etc. + * @return string The resolved path, it might not exist. + * @see http://stackoverflow.com/questions/4049856/replace-phps-realpath + */ + public function truepath($path) + { + // whether $path is unix or not + $unipath = strlen($path) == 0 || $path{0} != '/'; + // attempts to detect if path is relative in which case, add cwd + if (strpos($path, ':') === false && $unipath) + $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path; + + // resolve path parts (single dot, double dot and double delimiters) + $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); + $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); + $absolutes = array(); + foreach ($parts as $part) { + if ('.' == $part) + continue; + if ('..' == $part) { + array_pop($absolutes); + } else { + $absolutes[] = $part; + } + } + $path = implode(DIRECTORY_SEPARATOR, $absolutes); + // resolve any symlinks + if (file_exists($path) && linkinfo($path) > 0) + $path = readlink($path); + // put initial separator that could have been lost + $path = !$unipath ? '/' . $path : $path; + + return $path; + } +} diff --git a/lib/Minify/JS/ClosureCompiler.php b/lib/Minify/JS/ClosureCompiler.php index 6683884..ade3c2f 100644 --- a/lib/Minify/JS/ClosureCompiler.php +++ b/lib/Minify/JS/ClosureCompiler.php @@ -81,6 +81,7 @@ class Minify_JS_ClosureCompiler { public static function minify($js, array $options = array()) { $obj = new self($options); + return $obj->min($js); } diff --git a/lib/Minify/LessCssSource.php b/lib/Minify/LessCssSource.php index e887706..e5a4314 100644 --- a/lib/Minify/LessCssSource.php +++ b/lib/Minify/LessCssSource.php @@ -35,6 +35,7 @@ class Minify_LessCssSource extends Minify_Source { $lastModified = max($lastModified, $mtime); } + return $lastModified; } @@ -90,6 +91,7 @@ class Minify_LessCssSource extends Minify_Source { */ private function getCacheId($prefix = 'minify') { $md5 = md5($this->filepath); + return "{$prefix}_less_{$md5}"; } @@ -102,6 +104,7 @@ class Minify_LessCssSource extends Minify_Source { $less = new lessc(); // do not spend CPU time letting less doing minify $less->setPreserveComments(true); + return $less; } } diff --git a/lib/Minify/Lines.php b/lib/Minify/Lines.php index e999a6c..e046c59 100644 --- a/lib/Minify/Lines.php +++ b/lib/Minify/Lines.php @@ -1,6 +1,6 @@ * @@ -15,7 +15,7 @@ class Minify_Logger { /** - * Set logger object. + * Set logger object. * * The object should have a method "log" that accepts a value as 1st argument and * an optional string label as the 2nd. @@ -28,7 +28,7 @@ class Minify_Logger { ? $obj : null; } - + /** * Pass a message to the logger (if set) * @@ -39,7 +39,7 @@ class Minify_Logger { if (! self::$_logger) return; self::$_logger->log($msg, $label); } - + /** * @var mixed logger object (like FirePHP) or null (i.e. no logger available) */ diff --git a/lib/Minify/Packer.php b/lib/Minify/Packer.php index 1826990..f6fbcd8 100644 --- a/lib/Minify/Packer.php +++ b/lib/Minify/Packer.php @@ -4,19 +4,19 @@ * * To use this class you must first download the PHP port of Packer * and place the file "class.JavaScriptPacker.php" in /lib (or your - * include_path). + * include_path). * @link http://joliclic.free.fr/php/javascript-packer/en/ * * Be aware that, as long as HTTP encoding is used, scripts minified with JSMin * will provide better client-side performance, as they need not be unpacked in * client-side code. - * - * @package Minify + * + * @package Minify */ /** * Minify Javascript using Dean Edward's Packer - * + * * @package Minify */ class Minify_Packer { @@ -24,6 +24,7 @@ class Minify_Packer { { // @todo: set encoding options based on $options :) $packer = new JavascriptPacker($code, 'Normal', true, false); + return trim($packer->pack()); } } diff --git a/lib/Minify/ServeConfiguration.php b/lib/Minify/ServeConfiguration.php index c6c69ab..a2938a1 100644 --- a/lib/Minify/ServeConfiguration.php +++ b/lib/Minify/ServeConfiguration.php @@ -31,7 +31,7 @@ class Minify_ServeConfiguration { * @param Minify_SourceInterface[] $sources * @param string $selectionId */ - function __construct(array $options, array $sources = array(), $selectionId = '') + public function __construct(array $options, array $sources = array(), $selectionId = '') { $this->options = $options; $this->sources = $sources; diff --git a/lib/Minify/Source.php b/lib/Minify/Source.php index 34eff47..34daadd 100644 --- a/lib/Minify/Source.php +++ b/lib/Minify/Source.php @@ -1,15 +1,15 @@ */ diff --git a/lib/Minify/SourceSet.php b/lib/Minify/SourceSet.php index caca395..970ae08 100644 --- a/lib/Minify/SourceSet.php +++ b/lib/Minify/SourceSet.php @@ -24,6 +24,7 @@ class Minify_SourceSet { $source->getId(), $source->getMinifier(), $source->getMinifierOptions() ); } + return md5(serialize($info)); } } diff --git a/lib/Minify/YUICompressor.php b/lib/Minify/YUICompressor.php index 5762e89..17d5e20 100644 --- a/lib/Minify/YUICompressor.php +++ b/lib/Minify/YUICompressor.php @@ -1,17 +1,17 @@ * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar'; * Minify_YUICompressor::$tempDir = '/tmp'; @@ -25,7 +25,7 @@ * array('stack-size' => '2048k') * * @todo unit tests, $options docs - * + * * @package Minify * @author Stephen Clay */ @@ -38,7 +38,7 @@ class Minify_YUICompressor { * @var string */ public static $jarFile = null; - + /** * Writable temp directory. This must be set before calling minifyJs() * or minifyCss(). @@ -46,46 +46,46 @@ class Minify_YUICompressor { * @var string */ public static $tempDir = null; - + /** * Filepath of "java" executable (may be needed if not in shell's PATH) * * @var string */ public static $javaExecutable = 'java'; - + /** * Minify a Javascript string - * + * * @param string $js - * + * * @param array $options (verbose is ignored) - * + * * @see http://www.julienlecomte.net/yuicompressor/README - * - * @return string + * + * @return string */ public static function minifyJs($js, $options = array()) { return self::_minify('js', $js, $options); } - + /** * Minify a CSS string - * + * * @param string $css - * + * * @param array $options (verbose is ignored) - * + * * @see http://www.julienlecomte.net/yuicompressor/README - * - * @return string + * + * @return string */ public static function minifyCss($css, $options = array()) { return self::_minify('css', $css, $options); } - + private static function _minify($type, $content, $options) { self::_prepare(); @@ -98,9 +98,10 @@ class Minify_YUICompressor { if ($result_code != 0) { throw new Exception('Minify_YUICompressor : YUI compressor execution failed.'); } + return implode("\n", $output); } - + private static function _getCmd($userOptions, $type, $tmpFile) { $o = array_merge( @@ -122,21 +123,22 @@ class Minify_YUICompressor { . ' -jar ' . escapeshellarg(self::$jarFile) . " --type {$type}" . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) - ? " --charset {$o['charset']}" + ? " --charset {$o['charset']}" : '') . (is_numeric($o['line-break']) && $o['line-break'] >= 0 ? ' --line-break ' . (int)$o['line-break'] : ''); if ($type === 'js') { foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) { - $cmd .= $o[$opt] + $cmd .= $o[$opt] ? " --{$opt}" : ''; } } + return $cmd . ' ' . escapeshellarg($tmpFile); } - + private static function _prepare() { if (! is_file(self::$jarFile)) { diff --git a/lib/MrClay/Cli.php b/lib/MrClay/Cli.php index 9aa8e06..f382c82 100644 --- a/lib/MrClay/Cli.php +++ b/lib/MrClay/Cli.php @@ -1,4 +1,4 @@ - false // option was missing * ,'b' => true // option was present @@ -67,7 +67,7 @@ class Cli { * @var resource */ protected $_stdout = null; - + /** * @param bool $exitIfNoStdin (default true) Exit() if STDIN is not defined */ @@ -116,6 +116,7 @@ class Cli { $arg = new Arg($required); } $this->_args[$letter] = $arg; + return $arg; } @@ -130,7 +131,7 @@ class Cli { /* * Read and validate options - * + * * @return bool true if all options are valid */ public function validate() @@ -139,17 +140,17 @@ class Cli { $this->errors = array(); $this->values = array(); $this->_stdin = null; - + if ($this->isHelpRequest) { return false; } - + $lettersUsed = ''; foreach ($this->_args as $letter => $arg) { /* @var Arg $arg */ $options .= $letter; $lettersUsed .= $letter; - + if ($arg->mayHaveValue || $arg->mustHaveValue) { $options .= ($arg->mustHaveValue ? ':' : '::'); } @@ -201,14 +202,15 @@ class Cli { array_splice($argvCopy, $k, 2); } } - + // check that value isn't really another option if (strlen($lettersUsed) > 1) { $pattern = "/^-[" . str_replace($letter, '', $lettersUsed) . "]/i"; if (preg_match($pattern, $v)) { $this->addError($letter, "Value was read as another option: %s", $v); + return false; - } + } } if ($arg->assertFile || $arg->assertDir) { if ($v[0] !== '/' && $v[0] !== '~') { @@ -249,6 +251,7 @@ class Cli { } $this->moreArgs = $argvCopy; reset($this->moreArgs); + return empty($this->errors); } @@ -270,12 +273,13 @@ class Cli { $r[$k] = $v; } } + return $r; } - + /** * Get a short list of errors with options - * + * * @return string */ public function getErrorReport() @@ -288,6 +292,7 @@ class Cli { $r .= " $letter : " . implode(', ', $arr) . "\n"; } $r .= "\n"; + return $r; } @@ -318,9 +323,10 @@ class Cli { $desc = wordwrap($desc, 70); $r .= $flag . str_replace("\n", "\n ", $desc) . "\n\n"; } + return $r; } - + /** * Get resource of open input stream. May be STDIN or a file pointer * to the file specified by an option with 'STDIN'. @@ -333,17 +339,18 @@ class Cli { return STDIN; } else { $this->_stdin = fopen($this->_stdin, 'rb'); + return $this->_stdin; } } - + public function closeInput() { if (null !== $this->_stdin) { fclose($this->_stdin); } } - + /** * Get resource of open output stream. May be STDOUT or a file pointer * to the file specified by an option with 'STDOUT'. The file will be @@ -357,10 +364,11 @@ class Cli { return STDOUT; } else { $this->_stdout = fopen($this->_stdout, 'wb'); + return $this->_stdout; } } - + public function closeOutput() { if (null !== $this->_stdout) { diff --git a/lib/MrClay/Cli/Arg.php b/lib/MrClay/Cli/Arg.php index 5fa5932..9c8b0e3 100644 --- a/lib/MrClay/Cli/Arg.php +++ b/lib/MrClay/Cli/Arg.php @@ -97,6 +97,7 @@ class Arg { public function useAsOutfile() { $this->spec['useAsOutfile'] = true; + return $this->assertFile()->assertWritable(); } @@ -109,6 +110,7 @@ class Arg { public function useAsInfile() { $this->spec['useAsInfile'] = true; + return $this->assertFile()->assertReadable(); } @@ -127,6 +129,7 @@ class Arg { public function setDescription($desc) { $this->description = $desc; + return $this; } @@ -164,6 +167,7 @@ class Arg { } else { throw new BadMethodCallException('Method does not exist'); } + return $this; } @@ -178,6 +182,7 @@ class Arg { if (array_key_exists($name, $this->spec)) { return $this->spec[$name]; } + return null; } }