diff --git a/.gitignore b/.gitignore index 3b38053..d9969c1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ /vendor /.php_cs.cache /static/[0-9]* +/tests/compiler.jar diff --git a/.php_cs b/.php_cs index ddae094..8f25773 100644 --- a/.php_cs +++ b/.php_cs @@ -5,7 +5,7 @@ $finder = Symfony\CS\Finder\DefaultFinder::create() ; return Symfony\CS\Config\Config::create() - ->level(Symfony\CS\FixerInterface::NONE_LEVEL) + ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) ->setUsingCache(true) ->fixers(array( 'linefeed', @@ -21,6 +21,7 @@ return Symfony\CS\Config\Config::create() 'controls_spaces', 'elseif', '-eof_ending', + '-method_argument_space', )) ->finder($finder) ; diff --git a/.travis.yml b/.travis.yml index e1a2519..0ef27a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ php: - 5.6 - 5.5 - 5.4 - - 5.3 - hhvm matrix: @@ -25,8 +24,14 @@ cache: install: - composer update --no-interaction --prefer-source +before_script: + - wget -c https://dl.google.com/closure-compiler/compiler-latest.zip -O vendor/compiler-latest.zip + - unzip -od vendor/closure-compiler vendor/compiler-latest.zip + - ln -sfn ../$(echo vendor/closure-compiler/closure-compiler-*.jar) tests/compiler.jar + - java -jar tests/compiler.jar --version + script: - composer validate - - phpunit + - phpunit --verbose # vim:ts=2:sw=2:et diff --git a/composer.json b/composer.json index c8fef82..84128f8 100644 --- a/composer.json +++ b/composer.json @@ -23,21 +23,24 @@ "classmap": ["tests/TestCase.php"] }, "require": { - "php": ">=5.3.0", "ext-pcre": "*", - "firephp/firephp-core": "~0.4.0", "intervention/httpauth": "~2.0", "monolog/monolog": "~1.1", "mrclay/jsmin-php": "~2", "mrclay/props-dic": "^2.2", + "php": "^5.3.0 || ^7.0", "tubalmartin/cssmin": "~2.4.8" }, "require-dev": { + "firephp/firephp-core": "~0.4.0", "leafo/lessphp": "~0.4.0", + "leafo/scssphp": "~0.6.6", "meenie/javascript-packer": "~1.1", - "phpunit/phpunit": "4.8.*" + "phpunit/phpunit": "4.8.*", + "tedivm/jshrink": "~1.1.0" }, "suggest": { + "firephp/firephp-core": "Use FirePHP for Log messages", "leafo/lessphp": "LESS support", "meenie/javascript-packer": "Keep track of the Packer PHP port using Composer" }, diff --git a/docs/UserGuide.wiki.md b/docs/UserGuide.wiki.md index a572894..6e37e96 100644 --- a/docs/UserGuide.wiki.md +++ b/docs/UserGuide.wiki.md @@ -10,7 +10,7 @@ In other words, the "f" argument is set to the file path from root without the i To combine multiple files, separate the paths given to "f" with commas. -Let's say you have CSS files at these URLs: +Let's say you have JS files at these URLs: * http://example.com/scripts/library-1.5.js * http://example.com/scripts/site.js diff --git a/lib/HTTP/ConditionalGet.php b/lib/HTTP/ConditionalGet.php index 72d11c8..0f251ee 100644 --- a/lib/HTTP/ConditionalGet.php +++ b/lib/HTTP/ConditionalGet.php @@ -60,7 +60,8 @@ * @subpackage HTTP * @author Stephen Clay */ -class HTTP_ConditionalGet { +class HTTP_ConditionalGet +{ /** * Does the client have a valid copy of the requested resource? @@ -340,7 +341,8 @@ class HTTP_ConditionalGet { * * @return string */ - protected function normalizeEtag($etag) { + protected function normalizeEtag($etag) + { $etag = trim($etag); return $this->_stripEtag diff --git a/lib/HTTP/Encoder.php b/lib/HTTP/Encoder.php index 6e236ee..3bd788c 100644 --- a/lib/HTTP/Encoder.php +++ b/lib/HTTP/Encoder.php @@ -43,7 +43,8 @@ * @subpackage HTTP * @author Stephen Clay */ -class HTTP_Encoder { +class HTTP_Encoder +{ /** * Should the encoder allow HTTP encoding to IE6? @@ -97,8 +98,7 @@ class HTTP_Encoder { $this->_headers['Content-Type'] = $spec['type']; } if (isset($spec['method']) - && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) - { + && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) { $this->_encodeMethod = array($spec['method'], $spec['method']); } else { $this->_encodeMethod = self::getAcceptedEncoding(); @@ -192,8 +192,7 @@ class HTTP_Encoder { // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) - || self::isBuggyIe()) - { + || self::isBuggyIe()) { return array('', ''); } $ae = $_SERVER['HTTP_ACCEPT_ENCODING']; @@ -259,8 +258,7 @@ class HTTP_Encoder { } if ('' === $this->_encodeMethod[0] || ($compressionLevel == 0) - || !extension_loaded('zlib')) - { + || !extension_loaded('zlib')) { return false; } if ($this->_encodeMethod[0] === 'deflate') { diff --git a/lib/Minify.php b/lib/Minify.php index ba5ac4d..7741931 100644 --- a/lib/Minify.php +++ b/lib/Minify.php @@ -24,7 +24,8 @@ use Psr\Log\LoggerInterface; * @license http://opensource.org/licenses/bsd-license.php New BSD License * @link https://github.com/mrclay/minify */ -class Minify { +class Minify +{ /** * API version @@ -87,7 +88,8 @@ class Minify { * @param Minify_CacheInterface $cache * @param LoggerInterface $logger */ - public function __construct(Minify_CacheInterface $cache, LoggerInterface $logger = null) { + public function __construct(Minify_CacheInterface $cache, LoggerInterface $logger = null) + { $this->cache = $cache; $this->logger = $logger; } @@ -272,7 +274,8 @@ class Minify { // depending on what the client accepts, $contentEncoding may be // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling // getAcceptedEncoding(false, false) leaves out compress and deflate as options. - list($this->options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false); + $list = HTTP_Encoder::getAcceptedEncoding(false, false); + list($this->options['encodeMethod'], $contentEncoding) = $list; $sendVary = ! HTTP_Encoder::isBuggyIe(); } } else { @@ -492,11 +495,13 @@ class Minify { * @param string $content * @return string */ - public static function nullMinifier($content) { + public static function nullMinifier($content) + { if (isset($content[0]) && $content[0] === "\xef") { $content = substr($content, 3); } $content = str_replace("\r\n", "\n", $content); + return trim($content); } @@ -505,14 +510,14 @@ class Minify { */ protected function setupUriRewrites() { - foreach($this->sources as $key => $source) { + foreach ($this->sources as $key => $source) { $file = $this->env->normalizePath($source->getFilePath()); $minifyOptions = $source->getMinifierOptions(); if ($file && !isset($minifyOptions['currentDir']) - && !isset($minifyOptions['prependRelativePath']) - ) { + && !isset($minifyOptions['prependRelativePath'])) { + $minifyOptions['currentDir'] = dirname($file); $source->setMinifierOptions($minifyOptions); } @@ -592,9 +597,7 @@ class Minify { ! $source // yes, we ran out of sources || $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits) || $minifier !== $lastMinifier // yes, minifier changed - || $options !== $lastOptions) // yes, options changed - ) - { + || $options !== $lastOptions)) { // yes, options changed // minify previous sources with last settings $imploded = implode($implodeSeparator, $groupToProcessTogether); $groupToProcessTogether = array(); @@ -717,10 +720,10 @@ class Minify { if (!empty($options['contentType'])) { // just verify sources have null content type or match the options if ($sourceType !== null && $sourceType !== $options['contentType']) { - - $this->logger && $this->logger->warning('ContentType mismatch'); + $this->logger && $this->logger->warning("ContentType mismatch: '{$sourceType}' != '{$options['contentType']}'"); $this->sources = array(); + return $options; } @@ -730,10 +733,10 @@ class Minify { if ($type === null) { $type = $sourceType; } elseif ($sourceType !== $type) { - - $this->logger && $this->logger->warning('ContentType mismatch'); + $this->logger && $this->logger->warning("ContentType mismatch: '{$sourceType}' != '{$type}'"); $this->sources = array(); + return $options; } } diff --git a/lib/Minify/App.php b/lib/Minify/App.php index d39a639..840f364 100644 --- a/lib/Minify/App.php +++ b/lib/Minify/App.php @@ -3,7 +3,6 @@ namespace Minify; use Props\Container; -use Psr\Log\LoggerInterface; /** * @property \Minify_CacheInterface $cache @@ -22,7 +21,8 @@ use Psr\Log\LoggerInterface; * @property \Minify_Source_Factory $sourceFactory * @property array $sourceFactoryOptions */ -class App extends Container { +class App extends Container +{ /** * Constructor @@ -64,9 +64,10 @@ class App extends Container { $propNames = array_keys(get_object_vars($config)); - $varNames = array_map(function ($name) { + $prefixer = function ($name) { return "min_$name"; - }, $propNames); + }; + $varNames = array_map($prefixer, $propNames); $vars = compact($varNames); @@ -104,12 +105,14 @@ class App extends Container { if (empty($config->documentRoot)) { return $app->env->getDocRoot(); } + return $app->env->normalizePath($config->documentRoot); }; $this->env = function (App $app) { $config = $app->config; $envArgs = empty($config->envArgs) ? array() : $config->envArgs; + return new \Minify_Env($envArgs); }; @@ -117,6 +120,7 @@ class App extends Container { $format = "%channel%.%level_name%: %message% %context% %extra%"; $handler = new \Monolog\Handler\ErrorLogHandler(); $handler->setFormatter(new \Monolog\Formatter\LineFormatter($format)); + return $handler; }; @@ -142,11 +146,13 @@ class App extends Container { if ($value === true || $value instanceof \FirePHP) { $logger->pushHandler($app->errorLogHandler); $logger->pushHandler(new \Monolog\Handler\FirePHPHandler()); + return $logger; } if ($value instanceof \Monolog\Handler\HandlerInterface) { $logger->pushHandler($value); + return $logger; } @@ -154,6 +160,7 @@ class App extends Container { if (is_object($value) && is_callable(array($value, 'log'))) { $handler = new \Minify\Logger\LegacyHandler($value); $logger->pushHandler($handler); + return $logger; } @@ -232,6 +239,10 @@ class App extends Container { $ret['allowDirs'] = $serveOptions['minApp']['allowDirs']; } + if (isset($serveOptions['checkAllowDirs'])) { + $ret['checkAllowDirs'] = $serveOptions['checkAllowDirs']; + } + if (is_numeric($app->config->uploaderHoursBehind)) { $ret['uploaderHoursBehind'] = $app->config->uploaderHoursBehind; } @@ -240,7 +251,8 @@ class App extends Container { }; } - public function runServer() { + public function runServer() + { if (!$this->env->get('f') && $this->env->get('g') === null) { // no spec given $msg = '

No "f" or "g" parameters were detected.

'; @@ -256,8 +268,10 @@ class App extends Container { * @param mixed $var * @return string */ - private function typeOf($var) { + private function typeOf($var) + { $type = gettype($var); + return $type === 'object' ? get_class($var) : $type; } } diff --git a/lib/Minify/Build.php b/lib/Minify/Build.php index e4428e0..bf9b487 100644 --- a/lib/Minify/Build.php +++ b/lib/Minify/Build.php @@ -34,7 +34,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_Build { +class Minify_Build +{ /** * Last modification time of all files in the build @@ -67,15 +68,14 @@ class Minify_Build { * append the timestamp to the URI. * @return string */ - public function uri($uri, $forceAmpersand = false) { - $sep = ($forceAmpersand || strpos($uri, '?') !== false) - ? self::$ampersand - : '?'; + public function uri($uri, $forceAmpersand = false) + { + $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 diff --git a/lib/Minify/CSS.php b/lib/Minify/CSS.php index da389ea..c311f2e 100644 --- a/lib/Minify/CSS.php +++ b/lib/Minify/CSS.php @@ -16,7 +16,8 @@ * * @deprecated Use Minify_CSSmin */ -class Minify_CSS { +class Minify_CSS +{ /** * Minify a CSS string @@ -70,32 +71,27 @@ class Minify_CSS { 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) - ); + $processor = array('Minify_CSS_Compressor', 'process'); + $css = Minify_CommentPreserver::process($css, $processor, array($options)); } } + if (! $options['currentDir'] && ! $options['prependRelativePath']) { return $css; } + if ($options['currentDir']) { - return Minify_CSS_UriRewriter::rewrite( - $css - ,$options['currentDir'] - ,$options['docRoot'] - ,$options['symlinks'] - ); + $currentDir = $options['currentDir']; + $docRoot = $options['docRoot']; + $symlinks = $options['symlinks']; + return Minify_CSS_UriRewriter::rewrite($css, $currentDir, $docRoot, $symlinks); } else { - return Minify_CSS_UriRewriter::prepend( - $css - ,$options['prependRelativePath'] - ); + return Minify_CSS_UriRewriter::prepend($css, $options['prependRelativePath']); } } } diff --git a/lib/Minify/CSS/Compressor.php b/lib/Minify/CSS/Compressor.php index aa9f81f..ee3c7d6 100644 --- a/lib/Minify/CSS/Compressor.php +++ b/lib/Minify/CSS/Compressor.php @@ -26,7 +26,8 @@ * * @deprecated Use CSSmin (tubalmartin/cssmin) */ -class Minify_CSS_Compressor { +class Minify_CSS_Compressor +{ /** * Minify a CSS string @@ -61,7 +62,8 @@ class Minify_CSS_Compressor { * * @param array $options (currently ignored) */ - private function __construct($options) { + private function __construct($options) + { $this->_options = $options; } @@ -86,8 +88,8 @@ class Minify_CSS_Compressor { $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); + $pattern = '@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'; + $css = preg_replace_callback($pattern, array($this, '_commentCB'), $css); // remove ws around { } and last semicolon in declaration block $css = preg_replace('/\\s*{\\s*/', '{', $css); @@ -97,16 +99,17 @@ class Minify_CSS_Compressor { $css = preg_replace('/\\s*;\\s*/', ';', $css); // remove ws around urls - $css = preg_replace('/ + $pattern = '/ url\\( # url( \\s* ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) \\s* \\) # ) - /x', 'url($1)', $css); + /x'; + $css = preg_replace($pattern, 'url($1)', $css); // remove ws between rules and colons - $css = preg_replace('/ + $pattern = '/ \\s* ([{;]) # 1 = beginning of block or rule separator \\s* @@ -115,10 +118,11 @@ class Minify_CSS_Compressor { : \\s* (\\b|[#\'"-]) # 3 = first character of a value - /x', '$1$2:$3', $css); + /x'; + $css = preg_replace($pattern, '$1$2:$3', $css); // remove ws in selectors - $css = preg_replace_callback('/ + $pattern = '/ (?: # non-capture \\s* [^~>+,\\s]+ # selector part @@ -128,16 +132,16 @@ class Minify_CSS_Compressor { \\s* [^~>+,\\s]+ # selector part { # open declaration block - /x' - ,array($this, '_selectorsCB'), $css); + /x'; + $css = preg_replace_callback($pattern, 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); + $pattern = '/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'; + $css = preg_replace($pattern, '$1#$2$3$4$5', $css); // remove spaces between font families - $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' - ,array($this, '_fontFamilyCB'), $css); + $pattern = '/font-family:([^;}]+)([;}])/'; + $css = preg_replace_callback($pattern, array($this, '_fontFamilyCB'), $css); $css = preg_replace('/@import\\s+url/', '@import url', $css); @@ -145,14 +149,15 @@ class Minify_CSS_Compressor { $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); + $pattern = '/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/'; + $css = preg_replace($pattern, "$1\n$2{", $css); // Use newline after 1st numeric value (to limit line lengths). - $css = preg_replace('/ + $pattern = '/ ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value \\s+ - /x' - ,"$1\n", $css); + /x'; + $css = preg_replace($pattern, "$1\n", $css); // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); @@ -189,52 +194,54 @@ class Minify_CSS_Compressor { 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('@ + $pattern = '@ ^/ # comment started like /*/ \\s* (\\S[\\s\\S]+?) # has at least some non-ws content \\s* /\\* # ends like /*/ or /**/ - @x', $m, $n)) { + @x'; + if (preg_match($pattern, $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 - ? ' ' - : ''; + return $hasSurroundingWs ? ' ' : ''; // remove all other comments } /** @@ -247,8 +254,10 @@ class Minify_CSS_Compressor { 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); + $flags = PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY; + $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, $flags); $out = 'font-family:'; + while (null !== ($piece = array_shift($pieces))) { if ($piece[0] !== '"' && $piece[0] !== "'") { $piece = preg_replace('/\\s+/', ' ', $piece); diff --git a/lib/Minify/CSS/UriRewriter.php b/lib/Minify/CSS/UriRewriter.php index 93bf321..acd1a2c 100644 --- a/lib/Minify/CSS/UriRewriter.php +++ b/lib/Minify/CSS/UriRewriter.php @@ -10,7 +10,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_CSS_UriRewriter { +class Minify_CSS_UriRewriter +{ /** * rewrite() and rewriteRelative() append debugging information here @@ -51,10 +52,10 @@ class Minify_CSS_UriRewriter { // normalize symlinks in order to map to link foreach ($symlinks as $link => $target) { - $link = ($link === '//') - ? self::$_docRoot - : str_replace('//', self::$_docRoot . '/', $link); + + $link = ($link === '//') ? self::$_docRoot : str_replace('//', self::$_docRoot . '/', $link); $link = strtr($link, '/', DIRECTORY_SEPARATOR); + self::$_symlinks[$link] = self::_realpath($target); } @@ -70,10 +71,11 @@ class Minify_CSS_UriRewriter { $css = self::_owlifySvgPaths($css); // rewrite - $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' - ,array(self::$className, '_processUriCB'), $css); - $css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/' - ,array(self::$className, '_processUriCB'), $css); + $pattern = '/@import\\s+([\'"])(.*?)[\'"]/'; + $css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css); + + $pattern = '/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'; + $css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css); $css = self::_unOwlify($css); @@ -98,10 +100,11 @@ class Minify_CSS_UriRewriter { $css = self::_owlifySvgPaths($css); // append - $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' - ,array(self::$className, '_processUriCB'), $css); - $css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/' - ,array(self::$className, '_processUriCB'), $css); + $pattern = '/@import\\s+([\'"])(.*?)[\'"]/'; + $css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css); + + $pattern = '/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'; + $css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css); $css = self::_unOwlify($css); @@ -151,8 +154,8 @@ class Minify_CSS_UriRewriter { public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) { // prepend path with current dir separator (OS-independent) - $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) - . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); + $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR); + $path .= DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); self::$debugText .= "file-relative URI : {$uri}\n" . "path prepended : {$path}\n"; @@ -262,13 +265,14 @@ class Minify_CSS_UriRewriter { */ private static function _trimUrls($css) { - return preg_replace('/ + $pattern = '/ url\\( # url( \\s* ([^\\)]+?) # 1 = URI (assuming does not contain ")") \\s* \\) # ) - /x', 'url($1)', $css); + /x'; + return preg_replace($pattern, 'url($1)', $css); } /** @@ -286,13 +290,15 @@ class Minify_CSS_UriRewriter { $uri = $m[2]; } else { // $m[1] is either quoted or not - $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') - ? $m[1][0] - : ''; - $uri = ($quoteChar === '') - ? $m[1] - : substr($m[1], 1, strlen($m[1]) - 2); + $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : ''; + + $uri = ($quoteChar === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2); } + + if ($uri === '') { + return $m[0]; + } + // if not root/scheme relative and not starts with scheme if (!preg_match('~^(/|[a-z]+\:)~', $uri)) { // URI is file-relative: rewrite depending on options @@ -312,9 +318,11 @@ class Minify_CSS_UriRewriter { } } - return $isImport - ? "@import {$quoteChar}{$uri}{$quoteChar}" - : "url({$quoteChar}{$uri}{$quoteChar})"; + if ($isImport) { + return "@import {$quoteChar}{$uri}{$quoteChar}"; + } else { + return "url({$quoteChar}{$uri}{$quoteChar})"; + } } /** @@ -326,8 +334,10 @@ class Minify_CSS_UriRewriter { * @param string $css * @return string */ - private static function _owlifySvgPaths($css) { - return preg_replace('~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)url(\(\s*#\w+\s*\))~', '$1owl$2', $css); + private static function _owlifySvgPaths($css) + { + $pattern = '~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)url(\(\s*#\w+\s*\))~'; + return preg_replace($pattern, '$1owl$2', $css); } /** @@ -338,7 +348,9 @@ class Minify_CSS_UriRewriter { * @param string $css * @return string */ - private static function _unOwlify($css) { - return preg_replace('~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)owl~', '$1url', $css); + private static function _unOwlify($css) + { + $pattern = '~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)owl~'; + return preg_replace($pattern, '$1url', $css); } } diff --git a/lib/Minify/CSSmin.php b/lib/Minify/CSSmin.php index 8c3a64f..ad5cdbf 100644 --- a/lib/Minify/CSSmin.php +++ b/lib/Minify/CSSmin.php @@ -12,7 +12,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_CSSmin { +class Minify_CSSmin +{ /** * Minify a CSS string diff --git a/lib/Minify/Cache/APC.php b/lib/Minify/Cache/APC.php index 2e65501..479938d 100644 --- a/lib/Minify/Cache/APC.php +++ b/lib/Minify/Cache/APC.php @@ -14,7 +14,8 @@ * @package Minify * @author Chris Edwards **/ -class Minify_Cache_APC implements Minify_CacheInterface { +class Minify_Cache_APC implements Minify_CacheInterface +{ /** * Create a Minify_Cache_APC object, to be passed to @@ -58,9 +59,11 @@ class Minify_Cache_APC implements Minify_CacheInterface { return false; } - return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) - ? mb_strlen($this->_data, '8bit') - : strlen($this->_data); + if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { + return mb_strlen($this->_data, '8bit'); + } else { + return strlen($this->_data);; + } } /** @@ -84,9 +87,7 @@ class Minify_Cache_APC implements Minify_CacheInterface { */ public function display($id) { - echo $this->_fetch($id) - ? $this->_data - : ''; + echo $this->_fetch($id) ? $this->_data : ''; } /** @@ -98,9 +99,7 @@ class Minify_Cache_APC implements Minify_CacheInterface { */ public function fetch($id) { - return $this->_fetch($id) - ? $this->_data - : ''; + return $this->_fetch($id) ? $this->_data : ''; } private $_exp = null; @@ -125,9 +124,9 @@ 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; diff --git a/lib/Minify/Cache/File.php b/lib/Minify/Cache/File.php index cb3a082..7f57472 100644 --- a/lib/Minify/Cache/File.php +++ b/lib/Minify/Cache/File.php @@ -6,7 +6,8 @@ use Psr\Log\LoggerInterface; -class Minify_Cache_File implements Minify_CacheInterface { +class Minify_Cache_File implements Minify_CacheInterface +{ /** * @var string @@ -53,17 +54,18 @@ class Minify_Cache_File implements Minify_CacheInterface { */ public function store($id, $data) { - $flag = $this->locking - ? LOCK_EX - : null; + $flag = $this->locking ? LOCK_EX : null; $file = $this->path . '/' . $id; + if (! @file_put_contents($file, $data, $flag)) { $this->logger->warning("Minify_Cache_File: Write failed to '$file'"); } + // write control if ($data !== $this->fetch($id)) { @unlink($file); $this->logger->warning("Minify_Cache_File: Post-write read failed for '$file'"); + return false; } @@ -105,18 +107,19 @@ class Minify_Cache_File implements Minify_CacheInterface { */ public function display($id) { - if ($this->locking) { - $fp = fopen($this->path . '/' . $id, 'rb'); - flock($fp, LOCK_SH); - fpassthru($fp); - flock($fp, LOCK_UN); - fclose($fp); - } else { + if (!$this->locking) { readfile($this->path . '/' . $id); + return; } + + $fp = fopen($this->path . '/' . $id, 'rb'); + flock($fp, LOCK_SH); + fpassthru($fp); + flock($fp, LOCK_UN); + fclose($fp); } - /** + /** * Fetch the cached content * * @param string $id cache id (e.g. a filename) @@ -125,20 +128,21 @@ class Minify_Cache_File implements Minify_CacheInterface { */ public function fetch($id) { - if ($this->locking) { - $fp = fopen($this->path . '/' . $id, 'rb'); - if (!$fp) { - return false; - } - flock($fp, LOCK_SH); - $ret = stream_get_contents($fp); - flock($fp, LOCK_UN); - fclose($fp); - - return $ret; - } else { + if (!$this->locking) { return file_get_contents($this->path . '/' . $id); } + + $fp = fopen($this->path . '/' . $id, 'rb'); + if (!$fp) { + return false; + } + + flock($fp, LOCK_SH); + $ret = stream_get_contents($fp); + flock($fp, LOCK_UN); + fclose($fp); + + return $ret; } /** @@ -160,6 +164,7 @@ class Minify_Cache_File implements Minify_CacheInterface { public static function tmp() { trigger_error(__METHOD__ . ' is deprecated in Minfy 3.0', E_USER_DEPRECATED); + return sys_get_temp_dir(); } diff --git a/lib/Minify/Cache/Memcache.php b/lib/Minify/Cache/Memcache.php index ad6c480..d70dd07 100644 --- a/lib/Minify/Cache/Memcache.php +++ b/lib/Minify/Cache/Memcache.php @@ -17,7 +17,8 @@ * } * **/ -class Minify_Cache_Memcache implements Minify_CacheInterface { +class Minify_Cache_Memcache implements Minify_CacheInterface +{ /** * Create a Minify_Cache_Memcache object, to be passed to @@ -61,9 +62,11 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { return false; } - return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) - ? mb_strlen($this->_data, '8bit') - : strlen($this->_data); + if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { + return mb_strlen($this->_data, '8bit'); + } else { + return strlen($this->_data); + } } /** @@ -87,12 +90,10 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { */ public function display($id) { - echo $this->_fetch($id) - ? $this->_data - : ''; + echo $this->_fetch($id) ? $this->_data : ''; } - /** + /** * Fetch the cached content * * @param string $id cache id @@ -101,9 +102,7 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { */ public function fetch($id) { - return $this->_fetch($id) - ? $this->_data - : ''; + return $this->_fetch($id) ? $this->_data : ''; } private $_mc = null; @@ -114,7 +113,7 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { private $_data = null; private $_id = null; - /** + /** * Fetch data and timestamp from memcache, store in instance * * @param string $id @@ -126,12 +125,14 @@ class Minify_Cache_Memcache implements Minify_CacheInterface { if ($this->_id === $id) { return true; } + $ret = $this->_mc->get($id); if (false === $ret) { $this->_id = null; return false; } + list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; diff --git a/lib/Minify/Cache/Null.php b/lib/Minify/Cache/Null.php index 38c77c7..4f654a6 100644 --- a/lib/Minify/Cache/Null.php +++ b/lib/Minify/Cache/Null.php @@ -8,7 +8,8 @@ * * @package Minify */ -class Minify_Cache_Null implements Minify_CacheInterface { +class Minify_Cache_Null implements Minify_CacheInterface +{ /** * Write data to cache. * diff --git a/lib/Minify/Cache/WinCache.php b/lib/Minify/Cache/WinCache.php index 4097500..46e285a 100644 --- a/lib/Minify/Cache/WinCache.php +++ b/lib/Minify/Cache/WinCache.php @@ -60,7 +60,11 @@ class Minify_Cache_WinCache implements Minify_CacheInterface return false; } - return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data); + if (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) { + return mb_strlen($this->_data, '8bit'); + } else { + return strlen($this->_data); + } } /** @@ -99,12 +103,12 @@ class Minify_Cache_WinCache implements Minify_CacheInterface return $this->_fetch($id) ? $this->_data : ''; } - private $_exp = NULL; + private $_exp = null; // cache of most recently fetched id - private $_lm = NULL; - private $_data = NULL; - private $_id = NULL; + private $_lm = null; + private $_data = null; + private $_id = null; /** * Fetch data and timestamp from WinCache, store in instance @@ -118,13 +122,15 @@ class Minify_Cache_WinCache implements Minify_CacheInterface if ($this->_id === $id) { return true; } + $suc = false; $ret = wincache_ucache_get($id, $suc); if (!$suc) { - $this->_id = NULL; + $this->_id = null; return false; } + list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; diff --git a/lib/Minify/Cache/XCache.php b/lib/Minify/Cache/XCache.php index 4edd261..f293cb3 100644 --- a/lib/Minify/Cache/XCache.php +++ b/lib/Minify/Cache/XCache.php @@ -17,7 +17,8 @@ * @package Minify * @author Elan Ruusamäe **/ -class Minify_Cache_XCache implements Minify_CacheInterface { +class Minify_Cache_XCache implements Minify_CacheInterface +{ /** * Create a Minify_Cache_XCache object, to be passed to @@ -55,9 +56,11 @@ class Minify_Cache_XCache implements Minify_CacheInterface { return false; } - return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) - ? mb_strlen($this->_data, '8bit') - : strlen($this->_data); + if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) { + return mb_strlen($this->_data, '8bit'); + } else { + return strlen($this->_data); + } } /** @@ -79,9 +82,7 @@ class Minify_Cache_XCache implements Minify_CacheInterface { */ public function display($id) { - echo $this->_fetch($id) - ? $this->_data - : ''; + echo $this->_fetch($id) ? $this->_data : ''; } /** @@ -92,9 +93,7 @@ class Minify_Cache_XCache implements Minify_CacheInterface { */ public function fetch($id) { - return $this->_fetch($id) - ? $this->_data - : ''; + return $this->_fetch($id) ? $this->_data : ''; } private $_exp = null; @@ -115,12 +114,14 @@ class Minify_Cache_XCache implements Minify_CacheInterface { if ($this->_id === $id) { return true; } + $ret = xcache_get($id); if (false === $ret) { $this->_id = null; return false; } + list($this->_lm, $this->_data) = explode('|', $ret, 2); $this->_id = $id; diff --git a/lib/Minify/Cache/ZendPlatform.php b/lib/Minify/Cache/ZendPlatform.php index 9e5d248..76e9920 100644 --- a/lib/Minify/Cache/ZendPlatform.php +++ b/lib/Minify/Cache/ZendPlatform.php @@ -16,7 +16,8 @@ * @package Minify * @author Patrick van Dissel */ -class Minify_Cache_ZendPlatform implements Minify_CacheInterface { +class Minify_Cache_ZendPlatform implements Minify_CacheInterface +{ /** * Create a Minify_Cache_ZendPlatform object, to be passed to @@ -54,9 +55,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { */ public function getSize($id) { - return $this->_fetch($id) - ? strlen($this->_data) - : false; + return $this->_fetch($id) ? strlen($this->_data) : false; } /** @@ -70,9 +69,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { */ public function isValid($id, $srcMtime) { - $ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime)); - - return $ret; + return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); } /** @@ -82,9 +79,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { */ public function display($id) { - echo $this->_fetch($id) - ? $this->_data - : ''; + echo $this->_fetch($id) ? $this->_data : ''; } /** @@ -96,9 +91,7 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { */ public function fetch($id) { - return $this->_fetch($id) - ? $this->_data - : ''; + return $this->_fetch($id) ? $this->_data : ''; } private $_exp = null; @@ -120,12 +113,14 @@ class Minify_Cache_ZendPlatform implements Minify_CacheInterface { if ($this->_id === $id) { return true; } + $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; diff --git a/lib/Minify/CacheInterface.php b/lib/Minify/CacheInterface.php index 56b79b8..1b891b1 100644 --- a/lib/Minify/CacheInterface.php +++ b/lib/Minify/CacheInterface.php @@ -9,7 +9,8 @@ * * @package Minify */ -interface Minify_CacheInterface { +interface Minify_CacheInterface +{ /** * Write data to cache. * diff --git a/lib/Minify/ClosureCompiler.php b/lib/Minify/ClosureCompiler.php index 487cafe..26cbc67 100644 --- a/lib/Minify/ClosureCompiler.php +++ b/lib/Minify/ClosureCompiler.php @@ -24,19 +24,12 @@ * * * - * @todo unit tests, $options docs - * @todo more options support (or should just passthru them all?) - * * @package Minify * @author Stephen Clay * @author Elan Ruusamäe */ class Minify_ClosureCompiler { - const OPTION_CHARSET = 'charset'; - const OPTION_COMPILATION_LEVEL = 'compilation_level'; - const OPTION_WARNING_LEVEL = 'warning_level'; - public static $isDebug = false; /** @@ -61,6 +54,17 @@ class Minify_ClosureCompiler */ public static $javaExecutable = 'java'; + /** + * Default command line options passed to closure-compiler + * + * @var array + */ + public static $defaultOptions = array( + 'charset' => 'utf-8', + 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', + 'warning_level' => 'QUIET', + ); + /** * Minify a Javascript string * @@ -137,7 +141,8 @@ class Minify_ClosureCompiler $this->checkJar(self::$jarFile); $server = array( self::$javaExecutable, - '-jar', escapeshellarg(self::$jarFile) + '-jar', + escapeshellarg(self::$jarFile) ); return $server; @@ -151,24 +156,13 @@ class Minify_ClosureCompiler { $args = array(); - $o = array_merge( - array( - self::OPTION_CHARSET => 'utf-8', - self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS', - self::OPTION_WARNING_LEVEL => 'QUIET', - ), + $options = array_merge( + static::$defaultOptions, $userOptions ); - $charsetOption = $o[self::OPTION_CHARSET]; - if (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption)) { - $args[] = "--charset {$charsetOption}"; - } - - foreach (array(self::OPTION_COMPILATION_LEVEL, self::OPTION_WARNING_LEVEL) as $opt) { - if ($o[$opt]) { - $args[] = "--{$opt} " . escapeshellarg($o[$opt]); - } + foreach ($options as $key => $value) { + $args[] = "--{$key} " . escapeshellarg($value); } return $args; diff --git a/lib/Minify/CommentPreserver.php b/lib/Minify/CommentPreserver.php index 00db9ff..6bdb6f3 100644 --- a/lib/Minify/CommentPreserver.php +++ b/lib/Minify/CommentPreserver.php @@ -10,7 +10,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_CommentPreserver { +class Minify_CommentPreserver +{ /** * String to be prepended to each preserved comment @@ -71,21 +72,16 @@ class Minify_CommentPreserver { */ private static function _nextComment($in) { - if ( - false === ($start = strpos($in, '/*!')) - || false === ($end = strpos($in, '*/', $start + 3)) - ) { + if (false === ($start = strpos($in, '/*!')) || false === ($end = strpos($in, '*/', $start + 3))) { return array($in, false, false); } - $ret = array( - substr($in, 0, $start) - ,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append - ); - $endChars = (strlen($in) - $end - 2); - $ret[] = (0 === $endChars) - ? '' - : substr($in, -$endChars); - return $ret; + $beforeComment = substr($in, 0, $start); + $comment = self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append; + + $endChars = (strlen($in) - $end - 2); + $afterComment = (0 === $endChars) ? '' : substr($in, -$endChars); + + return array($beforeComment, $comment, $afterComment); } } diff --git a/lib/Minify/Controller/Base.php b/lib/Minify/Controller/Base.php index e95e6e9..54a60c9 100644 --- a/lib/Minify/Controller/Base.php +++ b/lib/Minify/Controller/Base.php @@ -15,7 +15,8 @@ use Monolog\Logger; * @package Minify * @author Stephen Clay */ -abstract class Minify_Controller_Base implements Minify_ControllerInterface { +abstract class Minify_Controller_Base implements Minify_ControllerInterface +{ /** * @var Minify_Env diff --git a/lib/Minify/Controller/Files.php b/lib/Minify/Controller/Files.php index da22bd3..85a76d0 100644 --- a/lib/Minify/Controller/Files.php +++ b/lib/Minify/Controller/Files.php @@ -4,7 +4,6 @@ * @package Minify */ -use Psr\Log\LoggerInterface; use Monolog\Logger; /** @@ -29,7 +28,8 @@ use Monolog\Logger; * @package Minify * @author Stephen Clay */ -class Minify_Controller_Files extends Minify_Controller_Base { +class Minify_Controller_Files extends Minify_Controller_Base +{ /** * Set up file sources @@ -41,7 +41,8 @@ class Minify_Controller_Files extends Minify_Controller_Base { * * 'files': (required) array of complete file paths, or a single path */ - public function createConfiguration(array $options) { + public function createConfiguration(array $options) + { // strip controller options $files = $options['files']; diff --git a/lib/Minify/Controller/Groups.php b/lib/Minify/Controller/Groups.php index 5c415e0..8938288 100644 --- a/lib/Minify/Controller/Groups.php +++ b/lib/Minify/Controller/Groups.php @@ -23,7 +23,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_Controller_Groups extends Minify_Controller_Files { +class Minify_Controller_Groups extends Minify_Controller_Files +{ /** * Set up groups of files as sources @@ -35,7 +36,8 @@ class Minify_Controller_Groups extends Minify_Controller_Files { * * @return array Minify options */ - public function createConfiguration(array $options) { + public function createConfiguration(array $options) + { // strip controller options $groups = $options['groups']; unset($options['groups']); @@ -43,12 +45,14 @@ class Minify_Controller_Groups extends Minify_Controller_Files { $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) - : (isset($server['PATH_INFO']) - ? substr($server['PATH_INFO'], 1) - : false - ); + if (isset($server['ORIG_PATH_INFO'])) { + $pathInfo = substr($server['ORIG_PATH_INFO'], 1); + } elseif (isset($server['PATH_INFO'])) { + $pathInfo = substr($server['PATH_INFO'], 1) + } else { + $pathInfo = false; + } + if (false === $pathInfo || ! isset($groups[$pathInfo])) { // no PATH_INFO or not a valid group $this->logger->info("Missing PATH_INFO or no group set for \"$pathInfo\""); diff --git a/lib/Minify/Controller/MinApp.php b/lib/Minify/Controller/MinApp.php index 80a05c5..e8440fe 100644 --- a/lib/Minify/Controller/MinApp.php +++ b/lib/Minify/Controller/MinApp.php @@ -10,7 +10,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_Controller_MinApp extends Minify_Controller_Base { +class Minify_Controller_MinApp extends Minify_Controller_Base +{ /** * Set up groups of files as sources @@ -19,7 +20,8 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { * * @return array Minify options */ - public function createConfiguration(array $options) { + public function createConfiguration(array $options) + { // PHP insecure by default: realpath() and other FS functions can't handle null bytes. $get = $this->env->get(); foreach (array('g', 'b', 'f') as $key) { @@ -29,14 +31,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { } // filter controller options - $localOptions = array_merge( - array( - 'groupsOnly' => false, - 'groups' => array(), - 'symlinks' => array(), - ) - ,(isset($options['minApp']) ? $options['minApp'] : array()) + $defaults = array( + 'groupsOnly' => false, + 'groups' => array(), + 'symlinks' => array(), ); + $minApp = isset($options['minApp']) ? $options['minApp'] : array(); + $localOptions = array_merge($defaults, $minApp); + unset($options['minApp']); // normalize $symlinks in order to map to target @@ -51,7 +53,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $sources = array(); $selectionId = ''; - $firstMissingResource = null; + $firstMissing = null; if (isset($get['g'])) { // add group(s) @@ -81,12 +83,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $sources[] = $source; } catch (Minify_Source_FactoryException $e) { $this->logger->error($e->getMessage()); - if (null === $firstMissingResource) { - $firstMissingResource = basename($file); + if (null === $firstMissing) { + $firstMissing = basename($file); continue; } else { - $secondMissingResource = basename($file); - $this->logger->info("More than one file was missing: '$firstMissingResource', '$secondMissingResource'"); + $secondMissing = basename($file); + $this->logger->info("More than one file was missing: '$firstMissing', '$secondMissing'"); return new Minify_ServeConfiguration($options); } @@ -98,18 +100,18 @@ 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, - // and are all same extension - ! preg_match('/^[^,]+\\.(css|less|js)(?:,[^,]+\\.\\1)*$/', $get['f'], $m) - // no "//" - || strpos($get['f'], '//') !== false - // no "\" - || strpos($get['f'], '\\') !== false - ) { + + // verify at least one file, files are single comma separated, and are all same extension + $validPattern = preg_match('/^[^,]+\\.(css|less|scss|js)(?:,[^,]+\\.\\1)*$/', $get['f'], $m); + $hasComment = strpos($get['f'], '//') !== false; + $hasEscape = strpos($get['f'], '\\') !== false; + + if (!$validPattern || $hasComment || $hasEscape) { $this->logger->info("GET param 'f' was invalid"); return new Minify_ServeConfiguration($options); } + $ext = ".{$m[1]}"; $files = explode(',', $get['f']); if ($files != array_unique($files)) { @@ -117,11 +119,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { return new Minify_ServeConfiguration($options); } + if (isset($get['b'])) { // check for validity - if (preg_match('@^[^/]+(?:/[^/]+)*$@', $get['b']) - && false === strpos($get['b'], '..') - && $get['b'] !== '.') { + $isValidBase = preg_match('@^[^/]+(?:/[^/]+)*$@', $get['b']); + $hasDots = false !== strpos($get['b'], '..'); + $isDot = $get['b'] === '.'; + + if ($isValidBase && !$hasDots && !$isDot) { // valid base $base = "/{$get['b']}/"; } else { @@ -152,12 +157,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $basenames[] = basename($path, $ext); } catch (Minify_Source_FactoryException $e) { $this->logger->error($e->getMessage()); - if (null === $firstMissingResource) { - $firstMissingResource = $uri; + if (null === $firstMissing) { + $firstMissing = $uri; continue; } else { - $secondMissingResource = $uri; - $this->logger->info("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'"); + $secondMissing = $uri; + $this->logger->info("More than one file was missing: '$firstMissing', '$secondMissing`'"); return new Minify_ServeConfiguration($options); } @@ -175,14 +180,14 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { return new Minify_ServeConfiguration($options); } - if (null !== $firstMissingResource) { + if (null !== $firstMissing) { array_unshift($sources, new Minify_Source(array( - 'id' => 'missingFile' + 'id' => 'missingFile', // should not cause cache invalidation - ,'lastModified' => 0 + 'lastModified' => 0, // due to caching, filename is unreliable. - ,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n" - ,'minifier' => 'Minify::nullMinifier' + 'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n", + 'minifier' => 'Minify::nullMinifier', ))); } diff --git a/lib/Minify/Controller/Page.php b/lib/Minify/Controller/Page.php index 8120479..7001fe0 100644 --- a/lib/Minify/Controller/Page.php +++ b/lib/Minify/Controller/Page.php @@ -11,7 +11,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_Controller_Page extends Minify_Controller_Base { +class Minify_Controller_Page extends Minify_Controller_Base +{ /** * Set up source of HTML content @@ -31,7 +32,8 @@ class Minify_Controller_Page extends Minify_Controller_Base { * 'minifyAll': should all CSS and Javascript blocks be individually * minified? (default false) */ - public function createConfiguration(array $options) { + public function createConfiguration(array $options) + { if (isset($options['file'])) { $sourceSpec = array( 'filepath' => $options['file'] @@ -40,8 +42,8 @@ class Minify_Controller_Page extends Minify_Controller_Base { } else { // strip controller options $sourceSpec = array( - 'content' => $options['content'] - ,'id' => $options['id'] + 'content' => $options['content'], + 'id' => $options['id'], ); $f = $options['id']; unset($options['content'], $options['id']); @@ -52,8 +54,8 @@ class Minify_Controller_Page extends Minify_Controller_Base { if (isset($options['minifyAll'])) { // this will be the 2nd argument passed to Minify_HTML::minify() $sourceSpec['minifyOptions'] = array( - 'cssMinifier' => array('Minify_CSSmin', 'minify') - ,'jsMinifier' => array('JSMin\\JSMin', 'minify') + 'cssMinifier' => array('Minify_CSSmin', 'minify'), + 'jsMinifier' => array('JSMin\\JSMin', 'minify'), ); unset($options['minifyAll']); } diff --git a/lib/Minify/ControllerInterface.php b/lib/Minify/ControllerInterface.php index ebd71a4..35b4de3 100644 --- a/lib/Minify/ControllerInterface.php +++ b/lib/Minify/ControllerInterface.php @@ -1,8 +1,8 @@ */ -class Minify_DebugDetector { +class Minify_DebugDetector +{ public static function shouldDebugRequest(Minify_Env $env) { if ($env->get('debug') !== null) { return true; } + $cookieValue = $env->cookie('minifyDebug'); if ($cookieValue) { foreach (preg_split('/\\s+/', $cookieValue) as $debugUri) { diff --git a/lib/Minify/Env.php b/lib/Minify/Env.php index 5727e84..25490bc 100644 --- a/lib/Minify/Env.php +++ b/lib/Minify/Env.php @@ -1,6 +1,7 @@ server['DOCUMENT_ROOT'] = rtrim($this->server['DOCUMENT_ROOT'], '/\\'); } + $this->server['DOCUMENT_ROOT'] = $this->normalizePath($this->server['DOCUMENT_ROOT']); $this->get = $options['get']; $this->post = $options['post']; @@ -45,9 +47,7 @@ class Minify_Env { return $this->server; } - return isset($this->server[$key]) - ? $this->server[$key] - : null; + return isset($this->server[$key]) ? $this->server[$key] : null; } public function cookie($key = null, $default = null) @@ -92,11 +92,13 @@ class Minify_Env { if ($realpath) { $path = $realpath; } + $path = str_replace('\\', '/', $path); $path = rtrim($path, '/'); if (substr($path, 1, 1) === ':') { $path = lcfirst($path); } + return $path; } @@ -116,11 +118,9 @@ class Minify_Env { if (isset($server['SERVER_SOFTWARE']) && 0 !== strpos($server['SERVER_SOFTWARE'], 'Microsoft-IIS/')) { throw new InvalidArgumentException('DOCUMENT_ROOT is not provided and could not be computed'); } - $docRoot = substr( - $server['SCRIPT_FILENAME'] - ,0 - ,strlen($server['SCRIPT_FILENAME']) - strlen($server['SCRIPT_NAME']) - ); + + $substrLength = strlen($server['SCRIPT_FILENAME']) - strlen($server['SCRIPT_NAME']); + $docRoot = substr($server['SCRIPT_FILENAME'], 0, $substrLength); return rtrim($docRoot, '\\'); } diff --git a/lib/Minify/HTML.php b/lib/Minify/HTML.php index ce01650..0cabbfb 100644 --- a/lib/Minify/HTML.php +++ b/lib/Minify/HTML.php @@ -16,7 +16,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_HTML { +class Minify_HTML +{ /** * @var boolean */ @@ -40,7 +41,8 @@ class Minify_HTML { * * @return string */ - public static function minify($html, $options = array()) { + public static function minify($html, $options = array()) + { $min = new self($html, $options); return $min->process(); diff --git a/lib/Minify/HTML/Helper.php b/lib/Minify/HTML/Helper.php index c67d891..bfded8b 100644 --- a/lib/Minify/HTML/Helper.php +++ b/lib/Minify/HTML/Helper.php @@ -10,7 +10,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_HTML_Helper { +class Minify_HTML_Helper +{ public $rewriteWorks = true; public $minAppUri = '/min'; public $groupsConfigFile = ''; @@ -98,8 +99,7 @@ class Minify_HTML_Helper { foreach ($files as $k => $file) { if (0 === strpos($file, '//')) { $file = substr($file, 2); - } elseif (0 === strpos($file, '/') - || 1 === strpos($file, ':\\')) { + } elseif (0 === strpos($file, '/') || 1 === strpos($file, ':\\')) { $file = substr($file, strlen(self::app()->env->getDocRoot()) + 1); } $file = strtr($file, '\\', '/'); @@ -168,11 +168,13 @@ class Minify_HTML_Helper { static $cached; if ($app) { $cached = $app; + return $app; } if ($cached === null) { $cached = (require __DIR__ . '/../../../bootstrap.php'); } + return $cached; } @@ -188,7 +190,8 @@ class Minify_HTML_Helper { * @param int $pos index to check * @return mixed a common char or '' if any do not match */ - protected static function _getCommonCharAtPos($arr, $pos) { + protected static function _getCommonCharAtPos($arr, $pos) + { if (!isset($arr[0][$pos])) { return ''; } @@ -213,7 +216,8 @@ class Minify_HTML_Helper { * @param string $minRoot root-relative URI of the "min" application * @return string */ - protected static function _getShortestUri($paths, $minRoot = '/min/') { + protected static function _getShortestUri($paths, $minRoot = '/min/') + { $pos = 0; $base = ''; while (true) { @@ -238,9 +242,7 @@ class Minify_HTML_Helper { $base = substr($base, 0, strlen($base) - 1); $bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths); - $uri = strlen($uri) < strlen($bUri) - ? $uri - : $bUri; + $uri = strlen($uri) < strlen($bUri) ? $uri : $bUri; } return $uri; diff --git a/lib/Minify/ImportProcessor.php b/lib/Minify/ImportProcessor.php index 715a9bf..09ec64c 100644 --- a/lib/Minify/ImportProcessor.php +++ b/lib/Minify/ImportProcessor.php @@ -18,8 +18,8 @@ * @author Stephen Clay * @author Simon Schick */ -class Minify_ImportProcessor { - +class Minify_ImportProcessor +{ public static $filesIncluded = array(); public static function process($file) @@ -57,8 +57,7 @@ class Minify_ImportProcessor { $file = realpath($file); if (! $file || in_array($file, self::$filesIncluded) - || false === ($content = @file_get_contents($file)) - ) { + || false === ($content = @file_get_contents($file))) { // file missing, already included, or failed read return ''; } @@ -73,8 +72,7 @@ class Minify_ImportProcessor { $content = str_replace("\r\n", "\n", $content); // process @imports - $content = preg_replace_callback( - '/ + $pattern = '/ @import\\s+ (?:url\\(\\s*)? # maybe url( [\'"]? # maybe quote @@ -83,19 +81,14 @@ class Minify_ImportProcessor { (?:\\s*\\))? # maybe ) ([a-zA-Z,\\s]*)? # 2 = media list ; # end token - /x' - ,array($this, '_importCB') - ,$content - ); + /x'; + $content = preg_replace_callback($pattern, 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 - ); + $pattern = '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'; + $content = preg_replace_callback($pattern, array($this, '_urlCB'), $content); } return $this->_importedContent . $content; @@ -139,12 +132,10 @@ class Minify_ImportProcessor { 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); + $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 @@ -173,8 +164,7 @@ class Minify_ImportProcessor { $arFrom = explode($ps, rtrim($realFrom, $ps)); $arTo = explode($ps, rtrim($realTo, $ps)); - while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) - { + while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) { array_shift($arFrom); array_shift($arTo); } @@ -191,28 +181,33 @@ class Minify_ImportProcessor { private function truepath($path) { // whether $path is unix or not - $unipath = strlen($path) == 0 || $path{0} != '/'; + $unipath = (strlen($path) == 0) || ($path{0} != '/'); + // attempts to detect if path is relative in which case, add cwd - if (strpos($path, ':') === false && $unipath) + 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) + 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) + if (file_exists($path) && linkinfo($path) > 0) { $path = readlink($path); + } // put initial separator that could have been lost $path = !$unipath ? '/' . $path : $path; diff --git a/lib/Minify/JS/ClosureCompiler.php b/lib/Minify/JS/ClosureCompiler.php index ade3c2f..20381a2 100644 --- a/lib/Minify/JS/ClosureCompiler.php +++ b/lib/Minify/JS/ClosureCompiler.php @@ -13,7 +13,8 @@ * * @todo can use a stream wrapper to unit test this? */ -class Minify_JS_ClosureCompiler { +class Minify_JS_ClosureCompiler +{ /** * @var string The option key for the maximum POST byte size @@ -228,4 +229,6 @@ class Minify_JS_ClosureCompiler { } } -class Minify_JS_ClosureCompiler_Exception extends Exception {} +class Minify_JS_ClosureCompiler_Exception extends Exception +{ +} diff --git a/lib/Minify/JS/JShrink.php b/lib/Minify/JS/JShrink.php new file mode 100644 index 0000000..70507a3 --- /dev/null +++ b/lib/Minify/JS/JShrink.php @@ -0,0 +1,48 @@ + + * @link https://github.com/tedious/JShrink + * + */ +class JShrink +{ + /** + * Contains the default options for minification. This array is merged with + * the one passed in by the user to create the request specific set of + * options (stored in the $options attribute). + * + * @var string[] + */ + protected static $defaultOptions = array('flaggedComments' => true); + + /** + * Takes a string containing javascript and removes unneeded characters in + * order to shrink the code without altering it's functionality. + * + * @param string $js The raw javascript to be minified + * @param array $options Various runtime options in an associative array + * + * @see JShrink\Minifier::minify() + * @return string + */ + public static function minify($js, array $options = array()) + { + $options = array_merge( + self::$defaultOptions, + $options + ); + + return \JShrink\Minifier::minify($js, $options); + } +} diff --git a/lib/Minify/LessCssSource.php b/lib/Minify/LessCssSource.php index e5a4314..8fa2e63 100644 --- a/lib/Minify/LessCssSource.php +++ b/lib/Minify/LessCssSource.php @@ -1,6 +1,7 @@ cache = $cache; @@ -27,16 +29,11 @@ class Minify_LessCssSource extends Minify_Source { * * @return int */ - public function getLastModified() { + public function getLastModified() + { $cache = $this->getCache(); - $lastModified = 0; - foreach ($cache['files'] as $mtime) { - $lastModified = max($lastModified, $mtime); - - } - - return $lastModified; + return $cache['lastModified']; } /** @@ -44,7 +41,8 @@ class Minify_LessCssSource extends Minify_Source { * * @return string */ - public function getContent() { + public function getContent() + { $cache = $this->getCache(); return $cache['compiled']; @@ -55,7 +53,8 @@ class Minify_LessCssSource extends Minify_Source { * * @return array */ - private function getCache() { + private function getCache() + { // cache for single run // so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache) if (isset($this->parsed)) { @@ -76,12 +75,29 @@ class Minify_LessCssSource extends Minify_Source { $cache = $less->cachedCompile($input); if (!is_array($input) || $cache['updated'] > $input['updated']) { + $cache['lastModified'] = $this->getMaxLastModified($cache); $this->cache->store($cacheId, serialize($cache)); } return $this->parsed = $cache; } + /** + * Calculate maximum last modified of all files, + * as the 'updated' timestamp in cache is not the same as file last modified timestamp: + * @link https://github.com/leafo/lessphp/blob/v0.4.0/lessc.inc.php#L1904 + * @return int + */ + private function getMaxLastModified($cache) + { + $lastModified = 0; + foreach ($cache['files'] as $mtime) { + $lastModified = max($lastModified, $mtime); + } + + return $lastModified; + } + /** * Make a unique cache id for for this source. * @@ -89,10 +105,11 @@ class Minify_LessCssSource extends Minify_Source { * * @return string */ - private function getCacheId($prefix = 'minify') { + private function getCacheId($prefix = 'minify') + { $md5 = md5($this->filepath); - return "{$prefix}_less_{$md5}"; + return "{$prefix}_less2_{$md5}"; } /** @@ -100,7 +117,8 @@ class Minify_LessCssSource extends Minify_Source { * * @return lessc */ - private function getCompiler() { + private function getCompiler() + { $less = new lessc(); // do not spend CPU time letting less doing minify $less->setPreserveComments(true); diff --git a/lib/Minify/Lines.php b/lib/Minify/Lines.php index e046c59..499a9cc 100644 --- a/lib/Minify/Lines.php +++ b/lib/Minify/Lines.php @@ -11,7 +11,8 @@ * @author Stephen Clay * @author Adam Pedersen (Issue 55 fix) */ -class Minify_Lines { +class Minify_Lines +{ /** * Add line numbers in C-style comments @@ -36,16 +37,9 @@ class Minify_Lines { */ public static function minify($content, $options = array()) { - $id = (isset($options['id']) && $options['id']) - ? $options['id'] - : ''; + $id = (isset($options['id']) && $options['id']) ? $options['id'] : ''; $content = str_replace("\r\n", "\n", $content); - // Hackily rewrite strings with XPath expressions that are - // likely to throw off our dumb parser (for Prototype 1.6.1). - $content = str_replace('"/*"', '"/"+"*"', $content); - $content = preg_replace('@([\'"])(\\.?//?)\\*@', '$1$2$1+$1*', $content); - $lines = explode("\n", $content); $numLines = count($lines); // determine left padding @@ -53,6 +47,7 @@ class Minify_Lines { $inComment = false; $i = 0; $newLines = array(); + while (null !== ($line = array_shift($lines))) { if (('' !== $id) && (0 == $i % 50)) { if ($inComment) { @@ -61,24 +56,25 @@ class Minify_Lines { array_push($newLines, '', "/* {$id} */", ''); } } + ++$i; $newLines[] = self::_addNote($line, $i, $inComment, $padTo); $inComment = self::_eolInComment($line, $inComment); } + $content = implode("\n", $newLines) . "\n"; // check for desired URI rewriting if (isset($options['currentDir'])) { Minify_CSS_UriRewriter::$debugText = ''; - $content = Minify_CSS_UriRewriter::rewrite( - $content - ,$options['currentDir'] - ,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] - ,isset($options['symlinks']) ? $options['symlinks'] : array() - ); + $docRoot = isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']; + $symlinks = isset($options['symlinks']) ? $options['symlinks'] : array(); + + $content = Minify_CSS_UriRewriter::rewrite($content, $options['currentDir'], $docRoot, $symlinks); + $content = "/* Minify_CSS_UriRewriter::\$debugText\n\n" - . Minify_CSS_UriRewriter::$debugText . "*/\n" - . $content; + . Minify_CSS_UriRewriter::$debugText . "*/\n" + . $content; } return $content; @@ -89,33 +85,43 @@ class Minify_Lines { * * @param string $line current line of code * - * @param bool $inComment was the parser in a comment at the - * beginning of the line? + * @param bool $inComment was the parser in a C-style comment at the + * beginning of the previous line? * * @return bool */ private static function _eolInComment($line, $inComment) { - // crude way to avoid things like // */ - $line = preg_replace('~//.*?(\\*/|/\\*).*~', '', $line); - while (strlen($line)) { - $search = $inComment - ? '*/' - : '/*'; - $pos = strpos($line, $search); - if (false === $pos) { - return $inComment; - } else { - if ($pos == 0 - || ($inComment - ? substr($line, $pos, 3) - : substr($line, $pos-1, 3)) != '*/*') - { - $inComment = ! $inComment; + if ($inComment) { + // only "*/" can end the comment + $index = self::_find($line, '*/'); + if ($index === false) { + return true; } - $line = substr($line, $pos + 2); + + // stop comment and keep walking line + $inComment = false; + @$line = (string)substr($line, $index + 2); + continue; } + + // look for "//" and "/*" + $single = self::_find($line, '//'); + $multi = self::_find($line, '/*'); + if ($multi === false) { + return false; + } + + if ($single === false || $multi < $single) { + // start comment and keep walking line + $inComment = true; + @$line = (string)substr($line, $multi + 2); + continue; + } + + // a single-line comment preceeded it + return false; } return $inComment; @@ -137,8 +143,67 @@ class Minify_Lines { */ private static function _addNote($line, $note, $inComment, $padTo) { - return $inComment - ? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line - : '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; + if ($inComment) { + $line = '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line; + } else { + $line = '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; + } + + return rtrim($line); + } + + /** + * Find a token trying to avoid false positives + * + * @param string $str String containing the token + * @param string $token Token being checked + * @return bool + */ + private static function _find($str, $token) + { + switch ($token) { + case '//': + $fakes = array( + '://' => 1, + '"//' => 1, + '\'//' => 1, + '".//' => 2, + '\'.//' => 2, + ); + break; + case '/*': + $fakes = array( + '"/*' => 1, + '\'/*' => 1, + '"//*' => 2, + '\'//*' => 2, + '".//*' => 3, + '\'.//*' => 3, + '*/*' => 1, + '\\/*' => 1, + ); + break; + default: + $fakes = array(); + } + + $index = strpos($str, $token); + $offset = 0; + + while ($index !== false) { + foreach ($fakes as $fake => $skip) { + $check = substr($str, $index - $skip, strlen($fake)); + if ($check === $fake) { + // move offset and scan again + $offset += $index + strlen($token); + $index = strpos($str, $token, $offset); + break; + } + } + // legitimate find + return $index; + } + + return $index; } } diff --git a/lib/Minify/Logger/LegacyHandler.php b/lib/Minify/Logger/LegacyHandler.php index 07f9991..273413c 100644 --- a/lib/Minify/Logger/LegacyHandler.php +++ b/lib/Minify/Logger/LegacyHandler.php @@ -2,7 +2,6 @@ namespace Minify\Logger; -use Monolog\Logger; use Monolog\Handler\AbstractProcessingHandler; class LegacyHandler extends AbstractProcessingHandler diff --git a/lib/Minify/Packer.php b/lib/Minify/Packer.php index f6fbcd8..7ddca79 100644 --- a/lib/Minify/Packer.php +++ b/lib/Minify/Packer.php @@ -19,7 +19,8 @@ * * @package Minify */ -class Minify_Packer { +class Minify_Packer +{ public static function minify($code, $options = array()) { // @todo: set encoding options based on $options :) diff --git a/lib/Minify/ScssCssSource.php b/lib/Minify/ScssCssSource.php new file mode 100644 index 0000000..9aa58f0 --- /dev/null +++ b/lib/Minify/ScssCssSource.php @@ -0,0 +1,176 @@ +cache = $cache; + } + + /** + * Get last modified of all parsed files + * + * @return int + */ + public function getLastModified() + { + $cache = $this->getCache(); + + return $cache['updated']; + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + $cache = $this->getCache(); + + return $cache['content']; + } + + /** + * Make a unique cache id for for this source. + * + * @param string $prefix + * + * @return string + */ + private function getCacheId($prefix = 'minify') + { + $md5 = md5($this->filepath); + + return "{$prefix}_scss_{$md5}"; + } + + /** + * Get SCSS cache object + * + * Runs the compilation if needed + * + * Implements Leafo\ScssPhp\Server logic because we need to get parsed files without parsing actual content + * + * @return array + */ + private function getCache() + { + // cache for single run + // so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache) + if (isset($this->parsed)) { + return $this->parsed; + } + + // check from cache first + $cache = null; + $cacheId = $this->getCacheId(); + if ($this->cache->isValid($cacheId, 0)) { + if ($cache = $this->cache->fetch($cacheId)) { + $cache = unserialize($cache); + } + } + + $input = $cache ? $cache : $this->filepath; + + if ($this->cacheIsStale($cache)) { + $cache = $this->compile($this->filepath); + } + + if (!is_array($input) || $cache['updated'] > $input['updated']) { + $this->cache->store($cacheId, serialize($cache)); + } + + return $this->parsed = $cache; + } + + /** + * Determine whether .scss file needs to be re-compiled. + * + * @param array $cache Cache object + * + * @return boolean True if compile required. + */ + private function cacheIsStale($cache) + { + if (!$cache) { + return true; + } + + $updated = $cache['updated']; + foreach ($cache['files'] as $import => $mtime) { + $filemtime = filemtime($import); + + if ($filemtime !== $mtime || $filemtime > $updated) { + return true; + } + } + + return false; + } + + /** + * Compile .scss file + * + * @param string $filename Input path (.scss) + * + * @see Server::compile() + * @return array meta data result of the compile + */ + private function compile($filename) + { + $start = microtime(true); + $scss = new Compiler(); + + // set import path directory the input filename resides + // otherwise @import statements will not find the files + // and will treat the @import line as css import + $scss->setImportPaths(dirname($filename)); + + $css = $scss->compile(file_get_contents($filename), $filename); + $elapsed = round((microtime(true) - $start), 4); + + $v = Version::VERSION; + $ts = date('r', $start); + $css = "/* compiled by scssphp $v on $ts (${elapsed}s) */\n\n" . $css; + + $imports = $scss->getParsedFiles(); + + $updated = 0; + foreach ($imports as $mtime) { + $updated = max($updated, $mtime); + } + + return array( + 'elapsed' => $elapsed, // statistic, can be dropped + 'updated' => $updated, + 'content' => $css, + 'files' => $imports, + ); + } +} diff --git a/lib/Minify/ServeConfiguration.php b/lib/Minify/ServeConfiguration.php index a2938a1..12d9d2f 100644 --- a/lib/Minify/ServeConfiguration.php +++ b/lib/Minify/ServeConfiguration.php @@ -9,7 +9,8 @@ * * @package Minify */ -class Minify_ServeConfiguration { +class Minify_ServeConfiguration +{ /** * @var Minify_SourceInterface[] diff --git a/lib/Minify/Source.php b/lib/Minify/Source.php index b44107e..1769d6e 100644 --- a/lib/Minify/Source.php +++ b/lib/Minify/Source.php @@ -13,7 +13,8 @@ * @package Minify * @author Stephen Clay */ -class Minify_Source implements Minify_SourceInterface { +class Minify_Source implements Minify_SourceInterface +{ /** * @var int time of last modification @@ -70,14 +71,15 @@ class Minify_Source implements Minify_SourceInterface { if (isset($spec['filepath'])) { $ext = pathinfo($spec['filepath'], PATHINFO_EXTENSION); switch ($ext) { - case 'js' : $this->contentType = Minify::TYPE_JS; - break; - case 'less' : // fallthrough - case 'css' : $this->contentType = Minify::TYPE_CSS; - break; - case 'htm' : // fallthrough - case 'html' : $this->contentType = Minify::TYPE_HTML; - break; + case 'js': $this->contentType = Minify::TYPE_JS; + break; + case 'less': // fallthrough + case 'scss': // fallthrough + case 'css': $this->contentType = Minify::TYPE_CSS; + break; + case 'htm': // fallthrough + case 'html': $this->contentType = Minify::TYPE_HTML; + break; } $this->filepath = $spec['filepath']; $this->id = $spec['filepath']; @@ -96,9 +98,7 @@ class Minify_Source implements Minify_SourceInterface { } else { $this->getContentFunc = $spec['getContentFunc']; } - $this->lastModified = isset($spec['lastModified']) - ? $spec['lastModified'] - : time(); + $this->lastModified = isset($spec['lastModified']) ? $spec['lastModified'] : time(); } if (isset($spec['contentType'])) { $this->contentType = $spec['contentType']; @@ -203,11 +203,12 @@ class Minify_Source implements Minify_SourceInterface { /** * {@inheritdoc} */ - public function setupUriRewrites() { + public function setupUriRewrites() + { if ($this->filepath && !isset($this->minifyOptions['currentDir']) - && !isset($this->minifyOptions['prependRelativePath']) - ) { + && !isset($this->minifyOptions['prependRelativePath'])) { + $this->minifyOptions['currentDir'] = dirname($this->filepath); } } diff --git a/lib/Minify/Source/Factory.php b/lib/Minify/Source/Factory.php index 7033179..f36d1b2 100644 --- a/lib/Minify/Source/Factory.php +++ b/lib/Minify/Source/Factory.php @@ -1,6 +1,7 @@ setHandler('~\.less$~i', function ($spec) use ($cache) { - return new Minify_LessCssSource($spec, $cache); - }); + $this->setHandler('~\.less$~i', function ($spec) use ($cache) { + return new Minify_LessCssSource($spec, $cache); + }); + + $this->setHandler('~\.scss~i', function ($spec) use ($cache) { + return new Minify_ScssCssSource($spec, $cache); + }); $this->setHandler('~\.(js|css)$~i', function ($spec) { return new Minify_Source($spec); diff --git a/lib/Minify/Source/FactoryException.php b/lib/Minify/Source/FactoryException.php index 0d0ba0e..54567f6 100644 --- a/lib/Minify/Source/FactoryException.php +++ b/lib/Minify/Source/FactoryException.php @@ -1,3 +1,5 @@ */ -class Minify_YUICompressor { +class Minify_YUICompressor +{ /** * Filepath of the YUI Compressor jar file. This must be set before @@ -92,6 +93,7 @@ class Minify_YUICompressor { if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) { throw new Exception('Minify_YUICompressor : could not create temp file in "'.self::$tempDir.'".'); } + file_put_contents($tmpFile, $content); exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code); unlink($tmpFile); @@ -104,23 +106,20 @@ class Minify_YUICompressor { private static function _getCmd($userOptions, $type, $tmpFile) { - $o = array_merge( - array( - 'charset' => '' - ,'line-break' => 5000 - ,'type' => $type - ,'nomunge' => false - ,'preserve-semi' => false - ,'disable-optimizations' => false - ,'stack-size' => '' - ) - ,$userOptions + $defaults = array( + 'charset' => '', + 'line-break' => 5000, + 'type' => $type, + 'nomunge' => false, + 'preserve-semi' => false, + 'disable-optimizations' => false, + 'stack-size' => '', ); + $o = array_merge($defaults, $userOptions); + $cmd = self::$javaExecutable - . (!empty($o['stack-size']) - ? ' -Xss' . $o['stack-size'] - : '') - . ' -jar ' . escapeshellarg(self::$jarFile) + . (!empty($o['stack-size']) ? ' -Xss' . $o['stack-size'] : '') + . ' -jar ' . escapeshellarg(self::$jarFile) . " --type {$type}" . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset']) ? " --charset {$o['charset']}" diff --git a/lib/MrClay/Cli.php b/lib/MrClay/Cli.php index f382c82..e9ef3b3 100644 --- a/lib/MrClay/Cli.php +++ b/lib/MrClay/Cli.php @@ -18,7 +18,8 @@ use InvalidArgumentException; * @author Steve Clay * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -class Cli { +class Cli +{ /** * @var array validation errors diff --git a/lib/MrClay/Cli/Arg.php b/lib/MrClay/Cli/Arg.php index 9c8b0e3..b7a46e9 100644 --- a/lib/MrClay/Cli/Arg.php +++ b/lib/MrClay/Cli/Arg.php @@ -43,7 +43,8 @@ use BadMethodCallException; * @author Steve Clay * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -class Arg { +class Arg +{ /** * @return array */ diff --git a/min_extras/tools/minifyTextarea.php b/min_extras/tools/minifyTextarea.php index fbdfcef..a5b5926 100644 --- a/min_extras/tools/minifyTextarea.php +++ b/min_extras/tools/minifyTextarea.php @@ -55,7 +55,7 @@ if ($env->post('method') === 'Minify and serve') { } $tpl = array(); -$tpl['classes'] = array('Minify_HTML', 'JSMin\\JSMin', 'Minify_CSS'); +$tpl['classes'] = array('Minify_HTML', 'JSMin\\JSMin', 'Minify_CSS', 'Minify_Lines'); if (in_array($env->post('method'), $tpl['classes'])) { diff --git a/server-info.php b/server-info.php index 20b1312..c829f9b 100644 --- a/server-info.php +++ b/server-info.php @@ -5,7 +5,7 @@ * * Change to true to expose this info. */ -$enabled = true; +$enabled = false; /////////////////////// diff --git a/tests/LessSourceTest.php b/tests/LessSourceTest.php index f61045c..a2b5158 100644 --- a/tests/LessSourceTest.php +++ b/tests/LessSourceTest.php @@ -24,9 +24,7 @@ class LessSourceTest extends TestCase touch($includedLess); $mtime1 = filemtime($mainLess); - var_dump($mtime1); $mtime2 = filemtime($includedLess); - var_dump($mtime2); $max = max($mtime1, $mtime2); diff --git a/tests/MinifyClosureCompilerTest.php b/tests/MinifyClosureCompilerTest.php index ea93eea..84dc1e3 100644 --- a/tests/MinifyClosureCompilerTest.php +++ b/tests/MinifyClosureCompilerTest.php @@ -63,7 +63,7 @@ class MinifyClosureCompilerTest extends TestCase $src = "function unused() {};"; $minExpected = ''; $options = array( - Minify_ClosureCompiler::OPTION_COMPILATION_LEVEL => 'ADVANCED_OPTIMIZATIONS' + 'compilation_level' => 'ADVANCED_OPTIMIZATIONS' ); $minOutput = Minify_ClosureCompiler::minify($src, $options); $this->assertSame($minExpected, $minOutput, 'advanced optimizations'); @@ -80,12 +80,36 @@ class MinifyClosureCompilerTest extends TestCase { $this->assertHasJar(); - $src = file_get_contents(self::$test_files . '/bug-513.js'); + $src = $this->getDataFile('bug-513.js'); $minExpected = 'var a=4;'; $minOutput = Minify_ClosureCompiler::minify($src); $this->assertSame($minExpected, $minOutput, 'advanced optimizations'); } + /** + * Test that language_in parameter has effect. + */ + public function testLanguageOptions() + { + $this->assertHasJar(); + + $src = $this->getDataFile('js/jscomp.polyfill.js'); + $exp = $this->getDataFile('js/jscomp.polyfill.min.js'); + $options = array( + 'language_in' => 'ECMASCRIPT3', + ); + + $res = Minify_ClosureCompiler::minify($src, $options); + $this->assertSame($exp, $res); + + $options = array( + 'language_in' => 'ECMASCRIPT6', + ); + $exp = $this->getDataFile('js/jscomp.polyfilled.min.js'); + $res = Minify_ClosureCompiler::minify($src, $options); + $this->assertSame($exp, $res); + } + protected function assertHasJar() { $this->assertNotEmpty(Minify_ClosureCompiler::$jarFile); diff --git a/tests/MinifyLinesTest.php b/tests/MinifyLinesTest.php index 6e93434..87f27c5 100644 --- a/tests/MinifyLinesTest.php +++ b/tests/MinifyLinesTest.php @@ -4,8 +4,6 @@ class MinifyLinesTest extends TestCase { public function test_lines() { - $exp = file_get_contents(self::$test_files . "/minify/lines_output.js"); - $env = new Minify_Env(array( 'server' => array( 'DOCUMENT_ROOT' => dirname(__DIR__), @@ -15,15 +13,27 @@ class MinifyLinesTest extends TestCase $controller = new Minify_Controller_Files($env, $sourceFactory); $minify = new Minify(new Minify_Cache_Null()); - $ret = $minify->serve($controller, array( - 'debug' => true - ,'quiet' => true - ,'encodeOutput' => false - ,'files' => array( - self::$test_files . "/js/before.js" - ) - )); + $files = glob(self::$test_files . "/lines/*.in.js"); - $this->assertEquals($exp, $ret['content']); + // uncomment to debug one + //$files = array(self::$test_files . "/lines/basic.in.js"); + + foreach ($files as $file) { + $ret = $minify->serve($controller, array( + 'debug' => true, + 'quiet' => true, + 'encodeOutput' => false, + 'files' => array($file), + )); + + $outFile = str_replace('.in.js', '.out.js', $file); + + $exp = file_get_contents($outFile); + + // uncomment to set up expected output + //file_put_contents($outFile, $ret['content']); + + $this->assertEquals($exp, $ret['content'], "Did not match: " . basename($outFile)); + } } } diff --git a/tests/MinifyNailgunClosureCompilerTest.php b/tests/MinifyNailgunClosureCompilerTest.php index caeeb70..572ed5f 100644 --- a/tests/MinifyNailgunClosureCompilerTest.php +++ b/tests/MinifyNailgunClosureCompilerTest.php @@ -20,7 +20,7 @@ class MinifyNailgunClosureCompilerTest extends TestCase /** * Test minimisation with the minimum necessary settings */ - public function test2() + public function test1() { $this->assertHasJar(); $src = " @@ -40,8 +40,10 @@ class MinifyNailgunClosureCompilerTest extends TestCase protected function assertHasJar() { $this->assertNotEmpty(Minify_ClosureCompiler::$jarFile); + $this->assertNotEmpty(Minify_NailgunClosureCompiler::$ngJarFile); try { $this->assertFileExists(Minify_ClosureCompiler::$jarFile, "Have closure compiler compiler.jar"); + $this->assertFileExists(Minify_NailgunClosureCompiler::$ngJarFile, "Have nailgun.jar"); } catch (Exception $e) { $this->markTestSkipped($e->getMessage()); } diff --git a/tests/ScssSourceTest.php b/tests/ScssSourceTest.php new file mode 100644 index 0000000..a8123dd --- /dev/null +++ b/tests/ScssSourceTest.php @@ -0,0 +1,42 @@ +markTestSkipped('scssphp is not compatible with this PHP version.'); + } + + $this->realDocRoot = $_SERVER['DOCUMENT_ROOT']; + $_SERVER['DOCUMENT_ROOT'] = self::$document_root; + } + + /** + * @link https://github.com/mrclay/minify/issues/500 + */ + public function testTimestamp() + { + $baseDir = self::$test_files; + + $mainLess = "$baseDir/main.scss"; + $includedLess = "$baseDir/_included.scss"; + + // touch timestamp with 1s difference + touch($mainLess); + sleep(1); + touch($includedLess); + + $mtime1 = filemtime($mainLess); + $mtime2 = filemtime($includedLess); + + $max = max($mtime1, $mtime2); + + $options = array( + 'groupsConfigFile' => "$baseDir/htmlHelper_groupsConfig.php", + ); + $res = Minify_HTML_Helper::getUri('scss', $options); + + $this->assertEquals("/min/g=scss&{$max}", $res); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index a871461..f763412 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -47,4 +47,23 @@ class TestCase extends PHPUnit_Framework_TestCase $this->assertSame($data, $displayed, "$id display"); $this->assertEquals($data, $cache->fetch($id), "$id fetch"); } + + /** + * Read data file, assert that it exists and is not empty. + * As a side effect calls trim() to fight against different Editors that insert or strip final newline. + * + * @param string $filename + * @return string + */ + protected function getDataFile($filename) + { + $path = self::$test_files . '/' . $filename; + $this->assertFileExists($path); + $contents = file_get_contents($path); + $this->assertNotEmpty($contents); + $contents = trim($contents); + $this->assertNotEmpty($contents); + + return $contents; + } } \ No newline at end of file diff --git a/tests/_test_files/_included.scss b/tests/_test_files/_included.scss new file mode 100644 index 0000000..df8d371 --- /dev/null +++ b/tests/_test_files/_included.scss @@ -0,0 +1,8 @@ +/* lesstest2.scss */ + + +a.included { + color: $primary-color; + font-size: 13px; + text-decoration: none; +} diff --git a/tests/_test_files/css_uriRewriter/exp.css b/tests/_test_files/css_uriRewriter/exp.css index 3296c26..5ce7667 100644 --- a/tests/_test_files/css_uriRewriter/exp.css +++ b/tests/_test_files/css_uriRewriter/exp.css @@ -8,6 +8,7 @@ @import url("/css/foo.css"); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */ @import url(foo:bar); /* scheme, should not alter */ +@import url(); /* empty, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(/_test_files/css_uriRewriter/foo.svg#c1)} foo {mask: url(#c1)} /* should not alter */ @@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url("/_test_files/css_uriRewriter/foo bar.jpg");} +foo {background:url("");} /* empty, should not alter */ @import url('/_test_files/css_uriRewriter/foo bar.css'); @import "/_test_files/css_uriRewriter/foo bar.css"; diff --git a/tests/_test_files/css_uriRewriter/exp_prepend.css b/tests/_test_files/css_uriRewriter/exp_prepend.css index bc09c44..a56103d 100644 --- a/tests/_test_files/css_uriRewriter/exp_prepend.css +++ b/tests/_test_files/css_uriRewriter/exp_prepend.css @@ -8,6 +8,7 @@ @import url("/css/foo.css"); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */ @import url(foo:bar); /* scheme, should not alter */ +@import url(); /* empty, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(http://cnd.com/A/B/foo.svg#c1)} foo {mask: url(#c1)} /* should not alter */ @@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url("http://cnd.com/A/B/foo bar.jpg");} +foo {background:url("");} /* empty, should not alter */ @import url('http://cnd.com/A/B/foo bar.css'); @import "http://cnd.com/A/B/foo bar.css"; diff --git a/tests/_test_files/css_uriRewriter/exp_prepend2.css b/tests/_test_files/css_uriRewriter/exp_prepend2.css index c8f7c61..f9398a5 100644 --- a/tests/_test_files/css_uriRewriter/exp_prepend2.css +++ b/tests/_test_files/css_uriRewriter/exp_prepend2.css @@ -8,6 +8,7 @@ @import url("/css/foo.css"); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */ @import url(foo:bar); /* scheme, should not alter */ +@import url(); /* empty, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(//cnd.com/A/B/foo.svg#c1)} foo {mask: url(#c1)} /* should not alter */ @@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url("//cnd.com/A/B/foo bar.jpg");} +foo {background:url("");} /* empty, should not alter */ @import url('//cnd.com/A/B/foo bar.css'); @import "//cnd.com/A/B/foo bar.css"; diff --git a/tests/_test_files/css_uriRewriter/in.css b/tests/_test_files/css_uriRewriter/in.css index 0200450..e5c4858 100644 --- a/tests/_test_files/css_uriRewriter/in.css +++ b/tests/_test_files/css_uriRewriter/in.css @@ -8,6 +8,7 @@ @import url("/css/foo.css"); /* abs, should not alter */ @import url(/css2/foo.css); /* abs, should not alter */ @import url(foo:bar); /* scheme, should not alter */ +@import url(); /* empty, should not alter */ foo {clip-path:url(#c1)} /* inline clip path, should not alter */ foo {clip-path:url(foo.svg#c1)} foo {mask: url( #c1 )} /* should not alter */ @@ -17,5 +18,6 @@ foo {background:url('http://foo.com/css/foo.css');} /* scheme, should not alter foo {background:url("//foo.com/css/foo.css");} /* protocol relative, should not alter */ foo {background:url(foo:bar);} /* scheme, should not alter */ foo {background:url("foo bar.jpg");} +foo {background:url("");} /* empty, should not alter */ @import url('foo bar.css'); @import "foo bar.css"; diff --git a/tests/_test_files/htmlHelper_groupsConfig.php b/tests/_test_files/htmlHelper_groupsConfig.php index cf67275..cbf8a9d 100644 --- a/tests/_test_files/htmlHelper_groupsConfig.php +++ b/tests/_test_files/htmlHelper_groupsConfig.php @@ -9,4 +9,8 @@ return array( 'less' => array( '//_test_files/main.less', ), + + 'scss' => array( + '//_test_files/main.scss', + ), ); diff --git a/tests/_test_files/js/jscomp.polyfill.js b/tests/_test_files/js/jscomp.polyfill.js new file mode 100644 index 0000000..f1da735 --- /dev/null +++ b/tests/_test_files/js/jscomp.polyfill.js @@ -0,0 +1,7 @@ +(function() { + /** + * @type {string} + */ + var $array = jQuery.find('#div'); + print($array.find('a')); +})(); \ No newline at end of file diff --git a/tests/_test_files/js/jscomp.polyfill.min.js b/tests/_test_files/js/jscomp.polyfill.min.js new file mode 100644 index 0000000..6146151 --- /dev/null +++ b/tests/_test_files/js/jscomp.polyfill.min.js @@ -0,0 +1 @@ +(function(){var a=jQuery.find("#div");print(a.find("a"))})(); \ No newline at end of file diff --git a/tests/_test_files/js/jscomp.polyfilled.min.js b/tests/_test_files/js/jscomp.polyfilled.min.js new file mode 100644 index 0000000..faa4d04 --- /dev/null +++ b/tests/_test_files/js/jscomp.polyfilled.min.js @@ -0,0 +1,3 @@ +var $jscomp={scope:{},findInternal:function(a,c,b){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e= 0; +if (is.ua.indexOf('opera') >= 0) { + is.ie = is.ns = false; + is.opera = true; +} +if (is.ua.indexOf('gecko') >= 0) { + is.ie = is.ns = false; + is.gecko = true; +} + +/*@cc_on + /*@if (@_win32) + if (is.ie && is.win) + document.write("PASS: IE/win honored conditional comment.
"); + @else @*/ + if (is.ie && is.win) + document.write("FAIL: IE/win did not honor multi-line conditional comment.
"); + else + document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.
"); + /*@end +@*/ + +var recognizesCondComm = true; +//@cc_on/* +recognizesCondComm = false; +//@cc_on*/ + +if ((is.ie && is.win) == recognizesCondComm) + document.write("PASS: IE/win honored single-line conditional comment.
"); +else + document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.
"); + +// hello +//@cc_on/* +// world +//@cc_on*/ +//@cc_on/* +'hello'; +/*!* preserved */ +/*!* preserved */ \ No newline at end of file diff --git a/tests/_test_files/minify/lines_output.js b/tests/_test_files/lines/basic.out.js similarity index 93% rename from tests/_test_files/minify/lines_output.js rename to tests/_test_files/lines/basic.out.js index 5565867..ad0d845 100644 --- a/tests/_test_files/minify/lines_output.js +++ b/tests/_test_files/lines/basic.out.js @@ -1,20 +1,20 @@ -/* before.js */ +/* basic.in.js */ /* 1 */ /*! is.js -/* 2 *| +/* 2 *| /* 3 *| (c) 2001 Douglas Crockford /* 4 *| 2001 June 3 /* 5 *| */ -/* 6 */ +/* 6 */ /* 7 */ // is -/* 8 */ +/* 8 */ /* 9 */ // The -is- object is used to identify the browser. Every browser edition /* 10 */ // identifies itself, but there is no standard way of doing it, and some of /* 11 */ // the identification is deceptive. This is because the authors of web /* 12 */ // browsers are liars. For example, Microsoft's IE browsers claim to be /* 13 */ // Mozilla 4. Netscape 6 claims to be version 5. -/* 14 */ +/* 14 */ /* 15 */ var is = { /* 16 */ ie: navigator.appName == 'Microsoft Internet Explorer', /* 17 */ java: navigator.javaEnabled(), @@ -36,7 +36,7 @@ /* 33 */ is.ie = is.ns = false; /* 34 */ is.gecko = true; /* 35 */ } -/* 36 */ +/* 36 */ /* 37 */ /*@cc_on /* 38 *| /*@if (@_win32) /* 39 *| if (is.ie && is.win) @@ -44,24 +44,24 @@ /* 41 *| @else @*/ /* 42 */ if (is.ie && is.win) /* 43 */ document.write("FAIL: IE/win did not honor multi-line conditional comment.
"); -/* 44 */ else +/* 44 */ else /* 45 */ document.write("PASS: Non-IE/win browser ignores multi-line conditional comment.
"); /* 46 */ /*@end /* 47 *| @*/ -/* 48 */ +/* 48 */ /* 49 */ var recognizesCondComm = true; /* 50 */ //@cc_on/* -/* before.js */ +/* basic.in.js */ /* 51 */ recognizesCondComm = false; /* 52 */ //@cc_on*/ -/* 53 */ +/* 53 */ /* 54 */ if ((is.ie && is.win) == recognizesCondComm) /* 55 */ document.write("PASS: IE/win honored single-line conditional comment.
"); -/* 56 */ else +/* 56 */ else /* 57 */ document.write("FAIL: Non-IE/win browser did not ignore single-line conditional comment.
"); -/* 58 */ +/* 58 */ /* 59 */ // hello /* 60 */ //@cc_on/* /* 61 */ // world diff --git a/tests/_test_files/minify/lines_bugs.js b/tests/_test_files/lines/misc.in.js similarity index 53% rename from tests/_test_files/minify/lines_bugs.js rename to tests/_test_files/lines/misc.in.js index cf56b58..6ac5d6d 100644 --- a/tests/_test_files/minify/lines_bugs.js +++ b/tests/_test_files/lines/misc.in.js @@ -1,10 +1,14 @@ -// sections from Prototype 1.6.1 -var xpath = ".//*[local-name()='ul' or local-name()='UL']" + - "//*[local-name()='li' or local-name()='LI']"; -this.matcher = ['.//*']; -xpath = { - descendant: "//*", - child: "/*", - f: 0 -}; -document._getElementsByXPath('.//*' + cond, element); \ No newline at end of file +// sections from Prototype 1.6.1 +var xpath = ".//*[local-name()='ul' or local-name()='UL']" + + "//*[local-name()='li' or local-name()='LI']"; +this.matcher = ['.//*']; +xpath = { + descendant: "//*", + child: "/*", + f: 0 +}; +document._getElementsByXPath('.//*' + cond, element); + +// from angular 1.4.8 +var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i; + diff --git a/tests/_test_files/lines/misc.out.js b/tests/_test_files/lines/misc.out.js new file mode 100644 index 0000000..b888683 --- /dev/null +++ b/tests/_test_files/lines/misc.out.js @@ -0,0 +1,18 @@ + +/* misc.in.js */ + +/* 1 */ // sections from Prototype 1.6.1 +/* 2 */ var xpath = ".//*[local-name()='ul' or local-name()='UL']" + +/* 3 */ "//*[local-name()='li' or local-name()='LI']"; +/* 4 */ this.matcher = ['.//*']; +/* 5 */ xpath = { +/* 6 */ descendant: "//*", +/* 7 */ child: "/*", +/* 8 */ f: 0 +/* 9 */ }; +/* 10 */ document._getElementsByXPath('.//*' + cond, element); +/* 11 */ +/* 12 */ // from angular 1.4.8 +/* 13 */ var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i; +/* 14 */ +/* 15 */ diff --git a/tests/_test_files/lines/url.in.js b/tests/_test_files/lines/url.in.js new file mode 100644 index 0000000..eb5d779 --- /dev/null +++ b/tests/_test_files/lines/url.in.js @@ -0,0 +1,13 @@ +foo; /* http://example.com */ +bar; + +foo; /* + http://example.com */ +bar; + +foo = "http://example.com"; /* hello */ +bar; + +foo = "http://example.com"; /* +hello */ +bar; diff --git a/tests/_test_files/lines/url.out.js b/tests/_test_files/lines/url.out.js new file mode 100644 index 0000000..dc43a7c --- /dev/null +++ b/tests/_test_files/lines/url.out.js @@ -0,0 +1,17 @@ + +/* url.in.js */ + +/* 1 */ foo; /* http://example.com */ +/* 2 */ bar; +/* 3 */ +/* 4 */ foo; /* +/* 5 *| http://example.com */ +/* 6 */ bar; +/* 7 */ +/* 8 */ foo = "http://example.com"; /* hello */ +/* 9 */ bar; +/* 10 */ +/* 11 */ foo = "http://example.com"; /* +/* 12 *| hello */ +/* 13 */ bar; +/* 14 */ diff --git a/tests/_test_files/main.scss b/tests/_test_files/main.scss new file mode 100644 index 0000000..80bc0ea --- /dev/null +++ b/tests/_test_files/main.scss @@ -0,0 +1,29 @@ +/*! preserving comment */ + +// Variable +$primary-color: hotpink; + +// Mixin +@mixin border-radius($radius) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + border-radius: $radius; +} + +.my-element { + color: $primary-color; + width: 100%; + overflow: hidden; +} + +.my-other-element { + @include border-radius(6px); +} + +/* import include -> */ +@import "_included"; +/* <- import included */ + +/* + a normal comment. +*/ \ No newline at end of file