diff --git a/min/lib/Minify.php b/min/lib/Minify.php index 9af0517..167e766 100644 --- a/min/lib/Minify.php +++ b/min/lib/Minify.php @@ -56,7 +56,7 @@ class Minify { * * @var string $importWarning */ - public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */"; + public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */\n"; /** * Specify a cache object (with identical interface as Minify_Cache_File) or diff --git a/min/lib/Minify/CSS/UriRewriter.php b/min/lib/Minify/CSS/UriRewriter.php index f1cebbc..ca3ee38 100644 --- a/min/lib/Minify/CSS/UriRewriter.php +++ b/min/lib/Minify/CSS/UriRewriter.php @@ -19,6 +19,12 @@ class Minify_CSS_UriRewriter { */ protected static $className = 'Minify_CSS_UriRewriter'; + /** + * rewrite() and rewriteRelative() append debugging information here + * @var string + */ + public static $debugText = ''; + /** * Rewrite file relative URIs as root relative in CSS files * @@ -58,6 +64,13 @@ class Minify_CSS_UriRewriter { self::$_symlinks[$link] = self::_realpath($target); } + self::$debugText .= "docRoot : " . self::$_docRoot . "\n" + . "currentDir : " . self::$_currentDir . "\n"; + if (self::$_symlinks) { + self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n"; + } + self::$debugText .= "\n"; + $css = self::_trimUrls($css); // rewrite @@ -202,16 +215,26 @@ class Minify_CSS_UriRewriter { // prepend path with current dir separator (OS-independent) $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); + + self::$debugText .= "file-relative URI : {$uri}\n" + . "path prepended : {$path}\n"; + // "unresolve" a symlink back to doc root foreach ($symlinks as $link => $target) { if (0 === strpos($path, $target)) { // replace $target with $link - $path = $link . substr($path, strlen($target)); + $path = $link . substr($path, strlen($target)); + + self::$debugText .= "symlink unresolved : {$path}\n"; + break; } } // strip doc root $path = substr($path, strlen($realDocRoot)); + + self::$debugText .= "docroot stripped : {$path}\n"; + // fix to root-relative URI $uri = strtr($path, DIRECTORY_SEPARATOR, '/'); // remove /./ and /../ where possible @@ -220,9 +243,14 @@ class Minify_CSS_UriRewriter { do { $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, -1, $changed); } while ($changed); + + self::$debugText .= "traversals removed : {$uri}\n\n"; + return $uri; } + + /** * Get realpath with any trailing slash removed * diff --git a/min/lib/Minify/Lines.php b/min/lib/Minify/Lines.php index f10d318..6f94fb6 100644 --- a/min/lib/Minify/Lines.php +++ b/min/lib/Minify/Lines.php @@ -18,13 +18,19 @@ class Minify_Lines { * * This uses a very basic parser easily fooled by comment tokens inside * strings or regexes, but, otherwise, generally clean code will not be - * mangled. - * + * mangled. URI rewriting can also be performed. + * * @param string $content * * @param array $options available options: * * 'id': (optional) string to identify file. E.g. file name/path + * + * 'currentDir': (default null) if given, this is assumed to be the + * directory of the current CSS file. Using this, minify will rewrite + * all relative URIs in import/url declarations to correctly point to + * the desired files, and prepend a comment with debugging information about + * this process. * * @return string */ @@ -49,7 +55,24 @@ class Minify_Lines { $newLines[] = self::_addNote($line, $i, $inComment, $padTo); $inComment = self::_eolInComment($line, $inComment); } - return implode("\n", $newLines) . "\n"; + $content = implode("\n", $newLines) . "\n"; + + // check for desired URI rewriting + if (isset($options['currentDir'])) { + require_once 'Minify/CSS/UriRewriter.php'; + 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() + ); + $content = "/* Minify_CSS_UriRewriter::\$debugText\n\n" + . Minify_CSS_UriRewriter::$debugText . "*/\n" + . $content; + } + + return $content; } /** diff --git a/min/lib/MyMin.php b/min/lib/MyMin.php deleted file mode 100644 index 15e688a..0000000 --- a/min/lib/MyMin.php +++ /dev/null @@ -1,289 +0,0 @@ - - * @copyright 2002 Douglas Crockford (jsmin.c) - * @copyright 2007 Ryan Grove (PHP port) - * @copyright 2007 Andrea Giammarchi (improvements + MyMinCompressor + MyMinCSS) - * @license http://opensource.org/licenses/mit-license.php MIT License - * @version 1.0.1 (2007-10-05) - updated 2008-02-17 - */ - -// -- Class MyMin -------------------------------------------------------------- -class MyMin { - - const /* char */ LF = "\n", - SPACE = ' ', - EOS = "\x00"; - - protected /* boolean */ $cc_on; - - protected /* char */ $a, - $ahead, - $b; - - protected /* int */ $index = 0, - $length; - - protected /* string */ $input, - $output = ""; - - // -- Public Static Methods ---------------------------------------------------- - static public final function /* string */ parse(/* string */ $input, /* boolean */ $cc_on = true){ - return "".(new MyMin($input, $cc_on)); - } - - // -- Public Instance Methods -------------------------------------------------- - public final function /* object */ __construct(/* string */ $input, /* boolean */ $cc_on = true){ - $this->input = preg_replace("/(\r\n|\n\r|\r|\n)+/", self::LF, trim($input)); - $this->length = strlen($this->input); - $this->cc_on = $cc_on; - $this->b = $this->ahead = self::SPACE; - $this->a = self::LF; - $this->action(3); - while($this->a !== self::EOS){ - switch($this->a){ - case self::SPACE: - $this->action($this->isAlNum($this->b) ? 1 : 2); - break; - case self::LF: - switch($this->b){ - case '{': - case '[': - case '(': - case '+': - case '-': - $this->action(1); - break; - case self::SPACE: - $this->action(3); - break; - default: - $this->action($this->isAlNum($this->b) ? 1 : 2); - break; - } - break; - default: - switch($this->b){ - case self::SPACE: - $this->action($this->isAlNum($this->a) ? 1 : 3); - break; - case self::LF: - switch($this->a){ - case '}': - case ']': - case ')': - case '+': - case '-': - case '"': - case '\'': - $this->action(1); - break; - default: - $this->action($this->isAlNum($this->a) ? 1 : 3); - break; - } - break; - default: - $this->action(1); - break; - } - break; - } - } - } - - public final function /* string */ __toString(/* void */){ - return str_replace("\n\n", "\n", ltrim($this->output)); - } - - // -- Protected Instance Methods ----------------------------------------------- - protected function /* void */ action(/* int */ $i){ - switch($i){ - case 1: - $this->output .= $this->a; - case 2: - $this->a = $this->b; - if($this->a === '\'' || $this->a === '"'){ - while(true){ - $this->output .= $this->a; - if(!$this->nextCharNoSlash($this->b, "Unterminated string literal.")) - break; - } - } - case 3: - $this->b = $this->next(); - if($this->b === '/'){ - switch($this->a){ - case self::LF: - case self::SPACE: - if(!$this->spaceBeforeRegExp($this->output)) - break; - - case '{': - case ';': - - case '(': - case ',': - case '=': - case ':': - case '[': - case '!': - case '&': - case '|': - case '?': - $this->output .= $this->a.$this->b; - while($this->nextCharNoSlash('/', "Unterminated regular expression literal.")) - $this->output .= $this->a; - $this->b = $this->next(); - break; - } - } - break; - } - } - - protected function /* void */ appendComment(/* int */ $pos, /* string */ $open, /* string */ $close) { - $this->output .= $this->a.$open.(new MyMin(substr($this->input, $this->index, $pos - $this->index), $this->cc_on)).$close; - $this->index = $pos; - $this->a = self::LF; - } - - protected function /* void */ conditionalComment(/* char */ $find) { - $single = $find === self::LF; - $pos = strpos($this->input, $find, $this->index); - if($pos === false){ - if($single) - $pos = $this->length; - else - throw new MyMinException("Unterminated comment."); - } - $this->appendComment($pos, $single ? "//" : "/*", $find); - } - - protected function /* char */ get(/* void */) { - $c = $this->ahead; - $this->ahead = self::EOS; - if($c === self::EOS && $this->index < $this->length) - $c = $this->input{$this->index++}; - return ($c === self::EOS || $c === self::LF || $c >= self::SPACE) ? $c : self::SPACE; - } - - protected function /* boolean */ isAlNum(/* char */ $c) { - return $c > 126 || $c === '\\' || preg_match('/^(\w|\$)$/', $c); - } - - protected function /* char */ next(/* void */) { - $c = $this->get(); - $loop = true; - if($c === '/'){ - switch($this->ahead = $this->get()){ - case '/': - if($this->cc_on && $this->input{$this->index} === '@') - $this->conditionalComment(self::LF); - while($loop){ - $c = $this->get(); - if($c <= self::LF) - $loop = false; - } - break; - case '*': - $this->get(); - if($this->cc_on && $this->input{$this->index} === '@') - $this->conditionalComment("*/"); - while($loop){ - switch($this->get()){ - case '*': - if(($this->ahead = $this->get()) === '/'){ - $this->get(); - $c = self::SPACE; - $loop = false; - } - break; - case self::EOS: - throw new MyMinException("Unterminated comment."); - } - } - break; - } - } - return $c; - } - - protected function /* boolean */ nextCharNoSlash(/* char */ $c, /* string */ $message) { - $loop = true; - $this->a = $this->get(); - if($this->a === $c) - $loop = false; - else{ - if($this->a === '\\'){ - $this->output .= $this->a; - $this->a = $this->get(); - } - if($this->a <= self::LF) - throw new MyMinException($message); - } - return $loop; - } - - protected function /* boolean */ spaceBeforeRegExp(/* string */ $output){ - for( - $i = 0, - $length = strlen($output), - $reserved = array("case", "else", "in", "return", "typeof"), - $result = false, - $tmp = ""; - $i < 5 && !$result; - $i++ - ){ - if($length === strlen($reserved[$i])) - $result = $reserved[$i] === $output; - else if($length > strlen($reserved[$i])){ - $tmp = substr($output, $length - strlen($reserved[$i]) - 1); - $result = substr($tmp, 1) === $reserved[$i] && !$this->isAlNum($tmp{0}); - } - }; - return $length < 2 ? true : $result; - } -} - -// -- MyMin Exceptions --------------------------------------------------------- -class MyMinException extends Exception {} diff --git a/min_unit_tests/test_Minify_CSS_UriRewriter.php b/min_unit_tests/test_Minify_CSS_UriRewriter.php index 704ab01..6970d4f 100644 --- a/min_unit_tests/test_Minify_CSS_UriRewriter.php +++ b/min_unit_tests/test_Minify_CSS_UriRewriter.php @@ -11,6 +11,8 @@ function test_Minify_CSS_UriRewriter() $in = file_get_contents($thisDir . '/_test_files/css_uriRewriter/in.css'); $expected = file_get_contents($thisDir . '/_test_files/css_uriRewriter/exp.css'); + Minify_CSS_UriRewriter::$debugText = ''; + $actual = Minify_CSS_UriRewriter::rewrite( $in ,$thisDir . '/_test_files/css_uriRewriter' // currentDir @@ -24,6 +26,10 @@ function test_Minify_CSS_UriRewriter() if (!$passed) { echo "---Expected: " .strlen($expected). " bytes\n\n{$expected}\n\n\n"; } + + // show debugging only when test run directly + echo "--- Minify_CSS_UriRewriter::\$debugText\n\n" + , Minify_CSS_UriRewriter::$debugText; } }