mirror of
https://github.com/mrclay/minify.git
synced 2025-01-17 21:28:14 +01:00
Minify.php : moved logging to Logger.php
Controller/Base.php : + log() Changed controllers to use $this->log() HTML.php : Issue 83 (preserve some WS around scripts) CSS.php : + symlinks option to correct some rewritten URIs min/config.php : + $min_symlinks min/index.php : normalize symlinks and use Logger.php min_unit_tests/_inc.php : default FirePHP logging, start output buffering
This commit is contained in:
parent
7ca31ba2b2
commit
4c1fe68ab6
@ -100,6 +100,21 @@ $min_serveOptions['minApp']['groupsOnly'] = false;
|
||||
$min_serveOptions['minApp']['maxFiles'] = 10;
|
||||
|
||||
|
||||
/**
|
||||
* If you minify CSS files stored in symlink-ed directories, the URI rewriting
|
||||
* algorithm can fail. To prevent this, provide an array of link paths to
|
||||
* target paths, where the link paths are within the document root.
|
||||
*
|
||||
* Because paths need to be normalized for this to work, use "//" to substitute
|
||||
* the doc root in the link paths (the array keys). E.g.:
|
||||
* <code>
|
||||
* array('//symlink' => '/real/target/path') // unix
|
||||
* array('//static' => 'D:\\staticStorage') // Windows
|
||||
* </code>
|
||||
*/
|
||||
$min_symlinks = array();
|
||||
|
||||
|
||||
/**
|
||||
* If you upload files from Windows to a non-Windows server, Windows may report
|
||||
* incorrect mtimes for the files. This may cause Minify to keep serving stale
|
||||
|
@ -29,15 +29,27 @@ if ($min_documentRoot) {
|
||||
Minify::setDocRoot(); // IIS may need help
|
||||
}
|
||||
|
||||
// normalize paths in symlinks
|
||||
foreach ($min_symlinks as $link => $target) {
|
||||
$link = str_replace('//', realpath($SERVER['DOCUMENT_ROOT']), $link);
|
||||
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
|
||||
$min_serveOptions['minifierOptions']['text/css']['symlinks'][$link] = realpath($target);
|
||||
}
|
||||
|
||||
if ($min_allowDebugFlag && isset($_GET['debug'])) {
|
||||
$min_serveOptions['debug'] = true;
|
||||
}
|
||||
if (true === $min_errorLogger) {
|
||||
require_once 'FirePHP.php';
|
||||
Minify::setLogger(FirePHP::getInstance(true));
|
||||
} elseif ($min_errorLogger) {
|
||||
Minify::setLogger($min_errorLogger);
|
||||
|
||||
if ($min_errorLogger) {
|
||||
require_once 'Minify/Logger.php';
|
||||
if (true === $min_errorLogger) {
|
||||
require_once 'FirePHP.php';
|
||||
Minify_Logger::setLogger(FirePHP::getInstance(true));
|
||||
} else {
|
||||
Minify_Logger::setLogger($min_errorLogger);
|
||||
}
|
||||
}
|
||||
|
||||
// check for URI versioning
|
||||
if (preg_match('/&\\d/', $_SERVER['QUERY_STRING'])) {
|
||||
$min_serveOptions['maxAge'] = 31536000;
|
||||
|
@ -358,41 +358,11 @@ class Minify {
|
||||
if ($unsetPathInfo) {
|
||||
unset($_SERVER['PATH_INFO']);
|
||||
}
|
||||
Minify::logError("setDocRoot() set DOCUMENT_ROOT to \"{$_SERVER['DOCUMENT_ROOT']}\"");
|
||||
require_once 'Minify/Logger.php';
|
||||
Minify_Logger::log("setDocRoot() set DOCUMENT_ROOT to \"{$_SERVER['DOCUMENT_ROOT']}\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set error logger object.
|
||||
*
|
||||
* The object should have a method "log" that accepts a value as 1st argument and
|
||||
* an optional string label as the 2nd.
|
||||
*
|
||||
* @param mixed $obj or a "falsey" value to disable
|
||||
* @return null
|
||||
*/
|
||||
public static function setLogger($obj = null) {
|
||||
self::$_logger = $obj
|
||||
? $obj
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to the error log (if set)
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @return null
|
||||
*/
|
||||
public static function logError($msg) {
|
||||
if (! self::$_logger) return;
|
||||
self::$_logger->log($msg, 'Minify');
|
||||
}
|
||||
|
||||
/**
|
||||
* @var mixed logger object (like FirePHP) or null (i.e. no logging enabled)
|
||||
*/
|
||||
private static $_logger = null;
|
||||
|
||||
/**
|
||||
* @var mixed Minify_Cache_* object or null (i.e. no server cache is used)
|
||||
*/
|
||||
|
@ -163,7 +163,10 @@ class Minify_CSS {
|
||||
self::$_tempPrepend = $options['prependRelativePath'];
|
||||
$rewrite = true;
|
||||
} elseif (isset($options['currentDir'])) {
|
||||
self::$_tempCurrentDir = $options['currentDir'];
|
||||
self::$_tempCurrentDir = realpath($options['currentDir']);
|
||||
if (isset($options['symlinks'])) {
|
||||
self::$_tempSymlinks = $options['symlinks'];
|
||||
}
|
||||
$rewrite = true;
|
||||
}
|
||||
if ($rewrite) {
|
||||
@ -172,7 +175,9 @@ class Minify_CSS {
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_urlCB'), $css);
|
||||
}
|
||||
// cleanup statics
|
||||
self::$_tempPrepend = self::$_tempCurrentDir = '';
|
||||
self::$_tempSymlinks = array();
|
||||
return trim($css);
|
||||
}
|
||||
|
||||
@ -190,28 +195,34 @@ class Minify_CSS {
|
||||
}
|
||||
|
||||
/**
|
||||
* @var bool Are we "in" a hack?
|
||||
* @var bool Are we "in" a hack?
|
||||
*
|
||||
* I.e. are some browsers targetted until the next comment?
|
||||
* I.e. are some browsers targetted until the next comment?
|
||||
*/
|
||||
protected static $_inHack = false;
|
||||
|
||||
/**
|
||||
* @var string string to be prepended to relative URIs
|
||||
* @var string string to be prepended to relative URIs
|
||||
*/
|
||||
protected static $_tempPrepend = '';
|
||||
|
||||
/**
|
||||
* @var string directory of this stylesheet for rewriting purposes
|
||||
* @var string directory of this stylesheet for rewriting purposes
|
||||
*/
|
||||
protected static $_tempCurrentDir = '';
|
||||
|
||||
/**
|
||||
* @var array directory replacements to map symlink targets back to their
|
||||
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
|
||||
*/
|
||||
protected static $_tempSymlinks = array();
|
||||
|
||||
/**
|
||||
* Process a comment and return a replacement
|
||||
*
|
||||
* @param array $m regex matches
|
||||
*
|
||||
* @return string
|
||||
* @return string
|
||||
*/
|
||||
protected static function _commentCB($m)
|
||||
{
|
||||
@ -291,6 +302,14 @@ class Minify_CSS {
|
||||
// prepend path with current dir separator (OS-independent)
|
||||
$path = self::$_tempCurrentDir
|
||||
. DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
|
||||
// "unresolve" a symlink back to doc root
|
||||
foreach (self::$_tempSymlinks as $link => $target) {
|
||||
if (0 === strpos($path, $target)) {
|
||||
// replace $target with $link
|
||||
$path = $link . substr($path, strlen($target));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// strip doc root
|
||||
$path = substr($path, strlen(realpath($_SERVER['DOCUMENT_ROOT'])));
|
||||
// fix to absolute URL
|
||||
|
@ -188,4 +188,14 @@ abstract class Minify_Controller_Base {
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send message to the Minify logger
|
||||
* @param string $msg
|
||||
* @return null
|
||||
*/
|
||||
protected function log($msg) {
|
||||
require_once 'Minify/Logger.php';
|
||||
Minify_Logger::log($msg);
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class Minify_Controller_Files extends Minify_Controller_Base {
|
||||
'filepath' => $realPath
|
||||
));
|
||||
} else {
|
||||
Minify::logError("The path \"{$file}\" could not be found (or was not a file)");
|
||||
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class Minify_Controller_Groups extends Minify_Controller_Base {
|
||||
);
|
||||
if (false === $pi || ! isset($groups[$pi])) {
|
||||
// no PATH_INFO or not a valid group
|
||||
Minify::logError("Missing PATH_INFO or no group set for \"$pi\"");
|
||||
$this->log("Missing PATH_INFO or no group set for \"$pi\"");
|
||||
return $options;
|
||||
}
|
||||
$sources = array();
|
||||
@ -73,7 +73,7 @@ class Minify_Controller_Groups extends Minify_Controller_Base {
|
||||
'filepath' => $realPath
|
||||
));
|
||||
} else {
|
||||
Minify::logError("The path \"{$file}\" could not be found (or was not a file)");
|
||||
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
if (isset($_GET['g'])) {
|
||||
// try groups
|
||||
if (! isset($cOptions['groups'][$_GET['g']])) {
|
||||
Minify::logError("A group configuration for \"{$_GET['g']}\" was not set");
|
||||
$this->log("A group configuration for \"{$_GET['g']}\" was not set");
|
||||
return $options;
|
||||
}
|
||||
foreach ((array)$cOptions['groups'][$_GET['g']] as $file) {
|
||||
@ -54,7 +54,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
'filepath' => $file
|
||||
));
|
||||
} else {
|
||||
Minify::logError("The path \"{$file}\" could not be found (or was not a file)");
|
||||
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@ -72,12 +72,12 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
// no "./"
|
||||
|| preg_match('/(?:^|[^\\.])\\.\\//', $_GET['f'])
|
||||
) {
|
||||
Minify::logError("GET param 'f' invalid (see MinApp.php line 63)");
|
||||
$this->log("GET param 'f' invalid (see MinApp.php line 63)");
|
||||
return $options;
|
||||
}
|
||||
$files = explode(',', $_GET['f']);
|
||||
if (count($files) > $cOptions['maxFiles'] || $files != array_unique($files)) {
|
||||
Minify::logError("Too many or duplicate files specified");
|
||||
$this->log("Too many or duplicate files specified");
|
||||
return $options;
|
||||
}
|
||||
if (isset($_GET['b'])) {
|
||||
@ -88,7 +88,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
// valid base
|
||||
$base = "/{$_GET['b']}/";
|
||||
} else {
|
||||
Minify::logError("GET param 'b' invalid (see MinApp.php line 84)");
|
||||
$this->log("GET param 'b' invalid (see MinApp.php line 84)");
|
||||
return $options;
|
||||
}
|
||||
} else {
|
||||
@ -102,10 +102,10 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
$path = $_SERVER['DOCUMENT_ROOT'] . $base . $file;
|
||||
$file = realpath($path);
|
||||
if (false === $file) {
|
||||
Minify::logError("Path \"{$path}\" failed realpath()");
|
||||
$this->log("Path \"{$path}\" failed realpath()");
|
||||
return $options;
|
||||
} elseif (! parent::_fileIsSafe($file, $allowDirs)) {
|
||||
Minify::logError("Path \"{$path}\" failed Minify_Controller_Base::_fileIsSafe()");
|
||||
$this->log("Path \"{$path}\" failed Minify_Controller_Base::_fileIsSafe()");
|
||||
return $options;
|
||||
} else {
|
||||
$sources[] = new Minify_Source(array(
|
||||
@ -117,7 +117,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
if ($sources) {
|
||||
$this->sources = $sources;
|
||||
} else {
|
||||
Minify::logError("No sources to serve");
|
||||
$this->log("No sources to serve");
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class Minify_HTML {
|
||||
|
||||
// replace SCRIPTs (and minify) with placeholders
|
||||
$html = preg_replace_callback(
|
||||
'/\\s*(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>\\s*/i'
|
||||
'/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
|
||||
,array(self::$className, '_removeScriptCB')
|
||||
,$html);
|
||||
|
||||
@ -183,9 +183,13 @@ class Minify_HTML {
|
||||
|
||||
protected static function _removeScriptCB($m)
|
||||
{
|
||||
$openScript = $m[1];
|
||||
$js = $m[2];
|
||||
$openScript = $m[2];
|
||||
$js = $m[3];
|
||||
|
||||
// whitespace surrounding? preserve at least one space
|
||||
$ws1 = ($m[1] === '') ? '' : ' ';
|
||||
$ws2 = ($m[4] === '') ? '' : ' ';
|
||||
|
||||
// remove HTML comments (and ending "//" if present)
|
||||
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
|
||||
|
||||
@ -199,8 +203,8 @@ class Minify_HTML {
|
||||
$js = call_user_func($minifier, $js);
|
||||
|
||||
return self::_reservePlace(self::_needsCdata($js)
|
||||
? "{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>"
|
||||
: "{$openScript}{$js}</script>"
|
||||
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
|
||||
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
|
||||
);
|
||||
}
|
||||
|
||||
|
45
min/lib/Minify/Logger.php
Normal file
45
min/lib/Minify/Logger.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_Logger
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Message logging class
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
*/
|
||||
class Minify_Logger {
|
||||
|
||||
/**
|
||||
* Set logger object.
|
||||
*
|
||||
* The object should have a method "log" that accepts a value as 1st argument and
|
||||
* an optional string label as the 2nd.
|
||||
*
|
||||
* @param mixed $obj or a "falsey" value to disable
|
||||
* @return null
|
||||
*/
|
||||
public static function setLogger($obj = null) {
|
||||
self::$_logger = $obj
|
||||
? $obj
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass a message to the logger (if set)
|
||||
*
|
||||
* @param string $msg message to log
|
||||
* @return null
|
||||
*/
|
||||
public static function log($msg, $label = 'Minify') {
|
||||
if (! self::$_logger) return;
|
||||
self::$_logger->log($msg, $label);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var mixed logger object (like FirePHP) or null (i.e. no logger available)
|
||||
*/
|
||||
private static $_logger = null;
|
||||
}
|
@ -12,6 +12,15 @@ if ($min_documentRoot) {
|
||||
$_SERVER['DOCUMENT_ROOT'] = $min_documentRoot;
|
||||
}
|
||||
|
||||
// default log to FirePHP
|
||||
require_once 'Minify/Logger.php';
|
||||
if ($min_errorLogger && true !== $min_errorLogger) { // custom logger
|
||||
Minify_Logger::setLogger($min_errorLogger);
|
||||
} else {
|
||||
require_once 'FirePHP.php';
|
||||
Minify_Logger::setLogger(FirePHP::getInstance(true));
|
||||
}
|
||||
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
@ -37,3 +46,5 @@ function assertTrue($test, $message)
|
||||
|
||||
return (bool)$test;
|
||||
}
|
||||
|
||||
ob_start();
|
@ -4,10 +4,10 @@ http-equiv="content-type" content="text/html; charset=iso-8859-1" /><meta
|
||||
name="author" content="Dave Shea" /><meta
|
||||
name="keywords" content="design, css, cascading, style, sheets, xhtml, graphic design, w3c, web standards, visual, display" /><meta
|
||||
name="description" content="A demonstration of what can be accomplished visually through CSS-based design." /><meta
|
||||
name="robots" content="all" /><title>css Zen Garden: The Beauty in CSS Design</title><script type="text/javascript">var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'}
|
||||
name="robots" content="all" /><title>css Zen Garden: The Beauty in CSS Design</title> <script type="text/javascript">var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'}
|
||||
is.mac=is.ua.indexOf('mac')>=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;}</script><script type="text/javascript">/*<![CDATA[*/var i=0;while(++i<10)
|
||||
{}/*]]>*/</script><script type="text/javascript">i=1;</script><script type="text/javascript">/*<![CDATA[*/(i<1);/*]]>*/</script><!--[if IE 6]><style type="text/css">/*<![CDATA[*/
|
||||
if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;}</script> <script type="text/javascript">/*<![CDATA[*/var i=0;while(++i<10)
|
||||
{}/*]]>*/</script> <script type="text/javascript">i=1;</script> <script type="text/javascript">/*<![CDATA[*/(i<1);/*]]>*/</script> <!--[if IE 6]><style type="text/css">/*<![CDATA[*/
|
||||
/* copyright: you'll need CDATA for this < & */
|
||||
body{background:white}/*]]>*/</style><![endif]--><style type="text/css" title="currentStyle" media="screen">@import "/001/001.css";/*\*/css
|
||||
hack{}/**//*/*/css
|
||||
|
@ -89,6 +89,7 @@ Design</span></h2>
|
||||
<textarea name="comment" id="comment" rows="6" class="maxwidth" cols="80">66666
|
||||
|
||||
1234567890</textarea>
|
||||
Preserve at least 1 char of whitespace near <script type="text/javascript"></script>scripts in case of document.write().
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -4,10 +4,10 @@ http-equiv="content-type" content="text/html; charset=iso-8859-1"><meta
|
||||
name="author" content="Dave Shea"><meta
|
||||
name="keywords" content="design, css, cascading, style, sheets, xhtml, graphic design, w3c, web standards, visual, display"><meta
|
||||
name="description" content="A demonstration of what can be accomplished visually through CSS-based design."><meta
|
||||
name="robots" content="all"><title>css Zen Garden: The Beauty in CSS Design</title><script type="text/javascript">var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'}
|
||||
name="robots" content="all"><title>css Zen Garden: The Beauty in CSS Design</title> <script type="text/javascript">var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'}
|
||||
is.mac=is.ua.indexOf('mac')>=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;}</script><script type="text/javascript">var i=0;while(++i<10)
|
||||
{}</script><script type="text/javascript">i=1;</script><script type="text/javascript">(i<1);</script><!--[if IE 6]><style type="text/css">
|
||||
if(is.ua.indexOf('gecko')>=0){is.ie=is.ns=false;is.gecko=true;}</script> <script type="text/javascript">var i=0;while(++i<10)
|
||||
{}</script> <script type="text/javascript">i=1;</script> <script type="text/javascript">(i<1);</script> <!--[if IE 6]><style type="text/css">
|
||||
/* copyright: you'll need CDATA for this < & */
|
||||
body{background:white}</style><![endif]--><style type="text/css" title="currentStyle" media="screen">@import "/001/001.css";/*\*/css
|
||||
hack{}/**//*/*/css
|
||||
@ -33,4 +33,4 @@ class="p2"><span>Download the sample <a
|
||||
href="/zengarden-sample.html" title="This page's source HTML code, not to be modified.">html file</a> and <a
|
||||
href="/zengarden-sample.css" title="This page's sample CSS, the file you may modify.">css file</a></span></p></div><textarea name="comment" id="comment" rows="6" class="maxwidth" cols="80">66666
|
||||
|
||||
1234567890</textarea></div></body></html>
|
||||
1234567890</textarea>Preserve at least 1 char of whitespace near <script type="text/javascript"></script>scripts in case of document.write().</div></body></html>
|
Loading…
x
Reference in New Issue
Block a user