MDL-51533 minify: Update to 2.2.1
This commit is contained in:
+46
-27
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
/*!
|
||||
* cssmin.php rev ebaf67b 12/06/2013
|
||||
* cssmin.php v2.4.8-4
|
||||
* Author: Tubal Martin - http://tubalmartin.me/
|
||||
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
|
||||
*
|
||||
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
|
||||
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
|
||||
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
|
||||
* Permission is hereby granted to use the PHP version under the same
|
||||
* conditions as the YUICompressor.
|
||||
@@ -92,7 +92,7 @@ class CSSmin
|
||||
// preserve strings so their content doesn't get accidentally minified
|
||||
$css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
|
||||
|
||||
// Let's divide css code in chunks of 25.000 chars aprox.
|
||||
// Let's divide css code in chunks of 5.000 chars aprox.
|
||||
// Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
|
||||
// of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
|
||||
// long strings and a (sub)pattern matches a number of chars greater than
|
||||
@@ -101,19 +101,19 @@ class CSSmin
|
||||
$charset = '';
|
||||
$charset_regexp = '/(@charset)( [^;]+;)/i';
|
||||
$css_chunks = array();
|
||||
$css_chunk_length = 25000; // aprox size, not exact
|
||||
$css_chunk_length = 5000; // aprox size, not exact
|
||||
$start_index = 0;
|
||||
$i = $css_chunk_length; // save initial iterations
|
||||
$l = strlen($css);
|
||||
|
||||
|
||||
// if the number of characters is 25000 or less, do not chunk
|
||||
// if the number of characters is 5000 or less, do not chunk
|
||||
if ($l <= $css_chunk_length) {
|
||||
$css_chunks[] = $css;
|
||||
} else {
|
||||
// chunk css code securely
|
||||
while ($i < $l) {
|
||||
$i += 50; // save iterations. 500 checks for a closing curly brace }
|
||||
$i += 50; // save iterations
|
||||
if ($l - $start_index <= $css_chunk_length || $i >= $l) {
|
||||
$css_chunks[] = $this->str_slice($css, $start_index);
|
||||
break;
|
||||
@@ -264,6 +264,9 @@ class CSSmin
|
||||
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
||||
$css = preg_replace('/\s+/', ' ', $css);
|
||||
|
||||
// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
|
||||
$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
|
||||
|
||||
// Shorten & preserve calculations calc(...) since spaces are important
|
||||
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
|
||||
|
||||
@@ -288,8 +291,8 @@ class CSSmin
|
||||
// Remove the spaces before the things that should not have spaces before them.
|
||||
// But, be careful not to turn "p :link {...}" into "p:link{...}"
|
||||
// Swap out any pseudo-class colons with the token, and then swap back.
|
||||
$css = preg_replace_callback('/(?:^|\})(?:(?:[^\{\:])+\:)+(?:[^\{]*\{)/', array($this, 'replace_colon'), $css);
|
||||
|
||||
$css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
|
||||
|
||||
// Remove spaces before the things that should not have spaces before them.
|
||||
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
|
||||
|
||||
@@ -317,7 +320,7 @@ class CSSmin
|
||||
// lower case some common function that can be values
|
||||
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
|
||||
$css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
|
||||
|
||||
|
||||
// Put the space back in some cases, to support stuff like
|
||||
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
||||
$css = preg_replace('/\band\(/i', 'and (', $css);
|
||||
@@ -333,8 +336,13 @@ class CSSmin
|
||||
// to avoid issues on Symbian S60 3.x browsers.
|
||||
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
|
||||
|
||||
// Replace 0 length units 0(px,em,%) with 0.
|
||||
$css = preg_replace('/(^|[^0-9])(?:0?\.)?0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%|deg|g?rad|m?s|k?hz)/iS', '${1}0', $css);
|
||||
// Replace 0 <length> and 0 <percentage> values with 0.
|
||||
// <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
|
||||
// <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
|
||||
$css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
|
||||
|
||||
// 0% step in a keyframe? restore the % unit
|
||||
$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
|
||||
|
||||
// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
|
||||
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
|
||||
@@ -373,6 +381,16 @@ class CSSmin
|
||||
// Add "/" back to fix Opera -o-device-pixel-ratio query
|
||||
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
|
||||
|
||||
// Replace multiple semi-colons in a row by a single one
|
||||
// See SF bug #1980989
|
||||
$css = preg_replace('/;;+/', ';', $css);
|
||||
|
||||
// Restore new lines for /*! important comments
|
||||
$css = preg_replace('/'. self::NL .'/', "\n", $css);
|
||||
|
||||
// Lowercase all uppercase properties
|
||||
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
|
||||
|
||||
// Some source control tools don't like it when files containing lines longer
|
||||
// than, say 8000 characters, are checked in. The linebreak option is used in
|
||||
// that case to split long lines after a specific column.
|
||||
@@ -388,18 +406,8 @@ class CSSmin
|
||||
}
|
||||
}
|
||||
|
||||
// Replace multiple semi-colons in a row by a single one
|
||||
// See SF bug #1980989
|
||||
$css = preg_replace('/;;+/', ';', $css);
|
||||
|
||||
// Restore new lines for /*! important comments
|
||||
$css = preg_replace('/'. self::NL .'/', "\n", $css);
|
||||
|
||||
// Lowercase all uppercase properties
|
||||
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
|
||||
|
||||
// restore preserved comments and strings
|
||||
for ($i = 0, $max = count($this->preserved_tokens); $i < $max; $i++) {
|
||||
// restore preserved comments and strings in reverse order
|
||||
for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
|
||||
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
|
||||
}
|
||||
|
||||
@@ -582,6 +590,17 @@ class CSSmin
|
||||
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
|
||||
}
|
||||
|
||||
private function preserve_old_IE_specific_matrix_definition($matches)
|
||||
{
|
||||
$this->preserved_tokens[] = $matches[1];
|
||||
return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
|
||||
}
|
||||
|
||||
private function replace_keyframe_zero($matches)
|
||||
{
|
||||
return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
|
||||
}
|
||||
|
||||
private function rgb_to_hex($matches)
|
||||
{
|
||||
// Support for percentage values rgb(100%, 0%, 45%);
|
||||
@@ -638,22 +657,22 @@ class CSSmin
|
||||
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
|
||||
}
|
||||
|
||||
private function lowercase_directives($matches)
|
||||
private function lowercase_directives($matches)
|
||||
{
|
||||
return '@'. strtolower($matches[1]);
|
||||
}
|
||||
|
||||
private function lowercase_pseudo_elements($matches)
|
||||
private function lowercase_pseudo_elements($matches)
|
||||
{
|
||||
return ':'. strtolower($matches[1]);
|
||||
}
|
||||
|
||||
private function lowercase_common_functions($matches)
|
||||
private function lowercase_common_functions($matches)
|
||||
{
|
||||
return ':'. strtolower($matches[1]) .'(';
|
||||
}
|
||||
|
||||
private function lowercase_common_functions_values($matches)
|
||||
private function lowercase_common_functions_values($matches)
|
||||
{
|
||||
return $matches[1] . strtolower($matches[2]);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
class Minify {
|
||||
|
||||
const VERSION = '2.1.7';
|
||||
const VERSION = '2.2.0';
|
||||
const TYPE_CSS = 'text/css';
|
||||
const TYPE_HTML = 'text/html';
|
||||
// there is some debate over the ideal JS Content-Type, but this is the
|
||||
@@ -435,7 +435,7 @@ class Minify {
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo "<h1>$h1</h1>";
|
||||
echo "<p>Please see <a href='$url'>$url</a>.</p>";
|
||||
exit();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -565,7 +565,7 @@ class Minify {
|
||||
{
|
||||
$name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId);
|
||||
$name = preg_replace('/\\.+/', '.', $name);
|
||||
$name = substr($name, 0, 200 - 34 - strlen($prefix));
|
||||
$name = substr($name, 0, 100 - 34 - strlen($prefix));
|
||||
$md5 = md5(serialize(array(
|
||||
Minify_Source::getDigest(self::$_controller->sources)
|
||||
,self::$_options['minifiers']
|
||||
|
||||
@@ -70,7 +70,7 @@ class Minify_CSS_UriRewriter {
|
||||
// rewrite
|
||||
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
|
||||
return $css;
|
||||
@@ -94,7 +94,7 @@ class Minify_CSS_UriRewriter {
|
||||
// append
|
||||
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
|
||||
self::$_prependPath = null;
|
||||
@@ -282,11 +282,8 @@ class Minify_CSS_UriRewriter {
|
||||
? $m[1]
|
||||
: substr($m[1], 1, strlen($m[1]) - 2);
|
||||
}
|
||||
// analyze URI
|
||||
if ('/' !== $uri[0] // root-relative
|
||||
&& false === strpos($uri, '//') // protocol (non-data)
|
||||
&& 0 !== strpos($uri, 'data:') // data protocol
|
||||
) {
|
||||
// if not root/scheme relative and not starts with scheme
|
||||
if (!preg_match('~^(/|[a-z]+\:)~', $uri)) {
|
||||
// URI is file-relative: rewrite depending on options
|
||||
if (self::$_prependPath === null) {
|
||||
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_CSSmin
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wrapper for CSSmin
|
||||
*
|
||||
* This class uses CSSmin and Minify_CSS_UriRewriter to minify CSS and rewrite relative URIs.
|
||||
*
|
||||
* @package Minify
|
||||
* @author Stephen Clay <[email protected]>
|
||||
*/
|
||||
class Minify_CSSmin {
|
||||
|
||||
/**
|
||||
* Minify a CSS string
|
||||
*
|
||||
* @param string $css
|
||||
*
|
||||
* @param array $options available options:
|
||||
*
|
||||
* 'removeCharsets': (default true) remove all @charset at-rules
|
||||
*
|
||||
* 'prependRelativePath': (default null) if given, this string will be
|
||||
* prepended to all relative URIs in import/url declarations
|
||||
*
|
||||
* 'currentDir': (default null) if given, this is assumed to be the
|
||||
* directory of the current CSS file. Using this, minify will rewrite
|
||||
* all relative URIs in import/url declarations to correctly point to
|
||||
* the desired files. For this to work, the files *must* exist and be
|
||||
* visible by the PHP process.
|
||||
*
|
||||
* 'symlinks': (default = array()) If the CSS file is stored in
|
||||
* a symlink-ed directory, provide an array of link paths to
|
||||
* target paths, where the link paths are within the document root. Because
|
||||
* paths need to be normalized for this to work, use "//" to substitute
|
||||
* the doc root in the link paths (the array keys). E.g.:
|
||||
* <code>
|
||||
* array('//symlink' => '/real/target/path') // unix
|
||||
* array('//static' => 'D:\\staticStorage') // Windows
|
||||
* </code>
|
||||
*
|
||||
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
|
||||
* see Minify_CSS_UriRewriter::rewrite
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function minify($css, $options = array())
|
||||
{
|
||||
$options = array_merge(array(
|
||||
'compress' => true,
|
||||
'removeCharsets' => true,
|
||||
'currentDir' => null,
|
||||
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
|
||||
'prependRelativePath' => null,
|
||||
'symlinks' => array(),
|
||||
), $options);
|
||||
|
||||
if ($options['removeCharsets']) {
|
||||
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
|
||||
}
|
||||
if ($options['compress']) {
|
||||
$obj = new CSSmin();
|
||||
$css = $obj->run($css);
|
||||
}
|
||||
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
|
||||
return $css;
|
||||
}
|
||||
if ($options['currentDir']) {
|
||||
return Minify_CSS_UriRewriter::rewrite(
|
||||
$css
|
||||
,$options['currentDir']
|
||||
,$options['docRoot']
|
||||
,$options['symlinks']
|
||||
);
|
||||
} else {
|
||||
return Minify_CSS_UriRewriter::prepend(
|
||||
$css
|
||||
,$options['prependRelativePath']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +98,9 @@ class Minify_Cache_File {
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_Cache_Wincache
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* WinCache-based cache class for Minify
|
||||
*
|
||||
* <code>
|
||||
* Minify::setCache(new Minify_Cache_WinCache());
|
||||
* </code>
|
||||
*
|
||||
* @package Minify
|
||||
* @author Matthias Fax
|
||||
**/
|
||||
class Minify_Cache_WinCache
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a Minify_Cache_Wincache object, to be passed to
|
||||
* Minify::setCache().
|
||||
*
|
||||
*
|
||||
* @param int $expire seconds until expiration (default = 0
|
||||
* meaning the item will not get an expiration date)
|
||||
*/
|
||||
public function __construct($expire = 0)
|
||||
{
|
||||
if (!function_exists('wincache_ucache_info')) {
|
||||
throw new Exception("WinCache for PHP is not installed to be able to use Minify_Cache_WinCache!");
|
||||
}
|
||||
$this->_exp = $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to cache.
|
||||
*
|
||||
* @param string $id cache id
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
public function store($id, $data)
|
||||
{
|
||||
return wincache_ucache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a cache entry
|
||||
*
|
||||
* @param string $id cache id
|
||||
*
|
||||
* @return int size in bytes
|
||||
*/
|
||||
public function getSize($id)
|
||||
{
|
||||
if (!$this->_fetch($id)) {
|
||||
return false;
|
||||
}
|
||||
return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a valid cache entry exist?
|
||||
*
|
||||
* @param string $id cache id
|
||||
*
|
||||
* @param int $srcMtime mtime of the original source file(s)
|
||||
*
|
||||
* @return bool exists
|
||||
*/
|
||||
public function isValid($id, $srcMtime)
|
||||
{
|
||||
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the cached content to output
|
||||
*
|
||||
* @param string $id cache id
|
||||
*/
|
||||
public function display($id)
|
||||
{
|
||||
echo $this->_fetch($id) ? $this->_data : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the cached content
|
||||
*
|
||||
* @param string $id cache id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fetch($id)
|
||||
{
|
||||
return $this->_fetch($id) ? $this->_data : '';
|
||||
}
|
||||
|
||||
private $_exp = NULL;
|
||||
|
||||
// cache of most recently fetched id
|
||||
private $_lm = NULL;
|
||||
private $_data = NULL;
|
||||
private $_id = NULL;
|
||||
|
||||
/**
|
||||
* Fetch data and timestamp from WinCache, store in instance
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
private function _fetch($id)
|
||||
{
|
||||
if ($this->_id === $id) {
|
||||
return true;
|
||||
}
|
||||
$suc = false;
|
||||
$ret = wincache_ucache_get($id, $suc);
|
||||
if (!$suc) {
|
||||
$this->_id = NULL;
|
||||
return false;
|
||||
}
|
||||
list($this->_lm, $this->_data) = explode('|', $ret, 2);
|
||||
$this->_id = $id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@
|
||||
*/
|
||||
class Minify_ClosureCompiler {
|
||||
|
||||
const OPTION_CHARSET = 'charset';
|
||||
const OPTION_COMPILATION_LEVEL = 'compilation_level';
|
||||
|
||||
public static $isDebug = false;
|
||||
|
||||
/**
|
||||
* Filepath of the Closure Compiler jar file. This must be set before
|
||||
* calling minifyJs().
|
||||
@@ -65,18 +70,28 @@ class Minify_ClosureCompiler {
|
||||
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Minify_ClosureCompiler_Exception
|
||||
*/
|
||||
public static function minify($js, $options = array())
|
||||
{
|
||||
self::_prepare();
|
||||
if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
|
||||
throw new Exception('Minify_ClosureCompiler : could not create temp file.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : could not create temp file in "'.self::$tempDir.'".');
|
||||
}
|
||||
file_put_contents($tmpFile, $js);
|
||||
exec(self::_getCmd($options, $tmpFile), $output, $result_code);
|
||||
$cmd = self::_getCmd($options, $tmpFile);
|
||||
exec($cmd, $output, $result_code);
|
||||
unlink($tmpFile);
|
||||
if ($result_code != 0) {
|
||||
throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.');
|
||||
$message = 'Minify_ClosureCompiler : Closure Compiler execution failed.';
|
||||
if (self::$isDebug) {
|
||||
exec($cmd . ' 2>&1', $error);
|
||||
if ($error) {
|
||||
$message .= "\nReason:\n" . join("\n", $error);
|
||||
}
|
||||
}
|
||||
throw new Minify_ClosureCompiler_Exception($message);
|
||||
}
|
||||
return implode("\n", $output);
|
||||
}
|
||||
@@ -85,17 +100,18 @@ class Minify_ClosureCompiler {
|
||||
{
|
||||
$o = array_merge(
|
||||
array(
|
||||
'charset' => 'utf-8',
|
||||
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
|
||||
self::OPTION_CHARSET => 'utf-8',
|
||||
self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS',
|
||||
),
|
||||
$userOptions
|
||||
);
|
||||
$charsetOption = $o[self::OPTION_CHARSET];
|
||||
$cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
|
||||
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
|
||||
? " --charset {$o['charset']}"
|
||||
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption)
|
||||
? " --charset {$charsetOption}"
|
||||
: '');
|
||||
|
||||
foreach (array('compilation_level') as $opt) {
|
||||
foreach (array(self::OPTION_COMPILATION_LEVEL) as $opt) {
|
||||
if ($o[$opt]) {
|
||||
$cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
|
||||
}
|
||||
@@ -106,18 +122,18 @@ class Minify_ClosureCompiler {
|
||||
private static function _prepare()
|
||||
{
|
||||
if (! is_file(self::$jarFile)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
|
||||
}
|
||||
if (! is_readable(self::$jarFile)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
|
||||
}
|
||||
if (! is_dir(self::$tempDir)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
||||
}
|
||||
if (! is_writable(self::$tempDir)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* vim:ts=4:sw=4:et */
|
||||
class Minify_ClosureCompiler_Exception extends Exception {}
|
||||
|
||||
@@ -49,7 +49,6 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
||||
$this->log("Duplicate group key found.");
|
||||
return $options;
|
||||
}
|
||||
$keys = explode(',', $_GET['g']);
|
||||
foreach ($keys as $key) {
|
||||
if (! isset($cOptions['groups'][$key])) {
|
||||
$this->log("A group configuration for \"{$key}\" was not found");
|
||||
|
||||
@@ -63,8 +63,6 @@ class Minify_HTML {
|
||||
*
|
||||
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
|
||||
* unset, minify will sniff for an XHTML doctype.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function __construct($html, $options = array())
|
||||
{
|
||||
@@ -133,7 +131,7 @@ class Minify_HTML {
|
||||
|
||||
// remove ws around block/undisplayed elements
|
||||
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
|
||||
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
|
||||
.'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
|
||||
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
|
||||
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
|
||||
.'|ul)\\b[^>]*>)/i', '$1', $this->_html);
|
||||
|
||||
@@ -14,13 +14,68 @@
|
||||
* @todo can use a stream wrapper to unit test this?
|
||||
*/
|
||||
class Minify_JS_ClosureCompiler {
|
||||
const URL = 'http://closure-compiler.appspot.com/compile';
|
||||
|
||||
/**
|
||||
* Minify Javascript code via HTTP request to the Closure Compiler API
|
||||
* @var string The option key for the maximum POST byte size
|
||||
*/
|
||||
const OPTION_MAX_BYTES = 'maxBytes';
|
||||
|
||||
/**
|
||||
* @var string The option key for additional params. @see __construct
|
||||
*/
|
||||
const OPTION_ADDITIONAL_OPTIONS = 'additionalParams';
|
||||
|
||||
/**
|
||||
* @var string The option key for the fallback Minifier
|
||||
*/
|
||||
const OPTION_FALLBACK_FUNCTION = 'fallbackFunc';
|
||||
|
||||
/**
|
||||
* @var string The option key for the service URL
|
||||
*/
|
||||
const OPTION_COMPILER_URL = 'compilerUrl';
|
||||
|
||||
/**
|
||||
* @var int The default maximum POST byte size according to https://developers.google.com/closure/compiler/docs/api-ref
|
||||
*/
|
||||
const DEFAULT_MAX_BYTES = 200000;
|
||||
|
||||
/**
|
||||
* @var string[] $DEFAULT_OPTIONS The default options to pass to the compiler service
|
||||
*
|
||||
* @note This would be a constant if PHP allowed it
|
||||
*/
|
||||
private static $DEFAULT_OPTIONS = array(
|
||||
'output_format' => 'text',
|
||||
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string $url URL of compiler server. defaults to Google's
|
||||
*/
|
||||
protected $serviceUrl = 'http://closure-compiler.appspot.com/compile';
|
||||
|
||||
/**
|
||||
* @var int $maxBytes The maximum JS size that can be sent to the compiler server in bytes
|
||||
*/
|
||||
protected $maxBytes = self::DEFAULT_MAX_BYTES;
|
||||
|
||||
/**
|
||||
* @var string[] $additionalOptions Additional options to pass to the compiler service
|
||||
*/
|
||||
protected $additionalOptions = array();
|
||||
|
||||
/**
|
||||
* @var callable Function to minify JS if service fails. Default is JSMin
|
||||
*/
|
||||
protected $fallbackMinifier = array('JSMin', 'minify');
|
||||
|
||||
/**
|
||||
* Minify JavaScript code via HTTP request to a Closure Compiler API
|
||||
*
|
||||
* @param string $js input code
|
||||
* @param array $options unused at this point
|
||||
* @param array $options Options passed to __construct(). @see __construct
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function minify($js, array $options = array())
|
||||
@@ -30,53 +85,91 @@ class Minify_JS_ClosureCompiler {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options Options with keys available below:
|
||||
*
|
||||
* @param array $options
|
||||
* fallbackFunc : (callable) function to minify if service unavailable. Default is JSMin.
|
||||
*
|
||||
* fallbackFunc : default array($this, 'fallback');
|
||||
* compilerUrl : (string) URL to closure compiler server
|
||||
*
|
||||
* maxBytes : (int) The maximum amount of bytes to be sent as js_code in the POST request.
|
||||
* Defaults to 200000.
|
||||
*
|
||||
* additionalParams : (string[]) Additional parameters to pass to the compiler server. Can be anything named
|
||||
* in https://developers.google.com/closure/compiler/docs/api-ref except for js_code and
|
||||
* output_info
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->_fallbackFunc = isset($options['fallbackMinifier'])
|
||||
? $options['fallbackMinifier']
|
||||
: array($this, '_fallback');
|
||||
if (isset($options[self::OPTION_FALLBACK_FUNCTION])) {
|
||||
$this->fallbackMinifier = $options[self::OPTION_FALLBACK_FUNCTION];
|
||||
}
|
||||
if (isset($options[self::OPTION_COMPILER_URL])) {
|
||||
$this->serviceUrl = $options[self::OPTION_COMPILER_URL];
|
||||
}
|
||||
if (isset($options[self::OPTION_ADDITIONAL_OPTIONS]) && is_array($options[self::OPTION_ADDITIONAL_OPTIONS])) {
|
||||
$this->additionalOptions = $options[self::OPTION_ADDITIONAL_OPTIONS];
|
||||
}
|
||||
if (isset($options[self::OPTION_MAX_BYTES])) {
|
||||
$this->maxBytes = (int) $options[self::OPTION_MAX_BYTES];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the service to perform the minification
|
||||
*
|
||||
* @param string $js JavaScript code
|
||||
* @return string
|
||||
* @throws Minify_JS_ClosureCompiler_Exception
|
||||
*/
|
||||
public function min($js)
|
||||
{
|
||||
$postBody = $this->_buildPostBody($js);
|
||||
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
||||
? mb_strlen($postBody, '8bit')
|
||||
: strlen($postBody);
|
||||
if ($bytes > 200000) {
|
||||
throw new Minify_JS_ClosureCompiler_Exception(
|
||||
'POST content larger than 200000 bytes'
|
||||
);
|
||||
$postBody = $this->buildPostBody($js);
|
||||
|
||||
if ($this->maxBytes > 0) {
|
||||
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
||||
? mb_strlen($postBody, '8bit')
|
||||
: strlen($postBody);
|
||||
if ($bytes > $this->maxBytes) {
|
||||
throw new Minify_JS_ClosureCompiler_Exception(
|
||||
'POST content larger than ' . $this->maxBytes . ' bytes'
|
||||
);
|
||||
}
|
||||
}
|
||||
$response = $this->_getResponse($postBody);
|
||||
|
||||
$response = $this->getResponse($postBody);
|
||||
|
||||
if (preg_match('/^Error\(\d\d?\):/', $response)) {
|
||||
if (is_callable($this->_fallbackFunc)) {
|
||||
if (is_callable($this->fallbackMinifier)) {
|
||||
// use fallback
|
||||
$response = "/* Received errors from Closure Compiler API:\n$response"
|
||||
. "\n(Using fallback minifier)\n*/\n";
|
||||
$response .= call_user_func($this->_fallbackFunc, $js);
|
||||
$response .= call_user_func($this->fallbackMinifier, $js);
|
||||
} else {
|
||||
throw new Minify_JS_ClosureCompiler_Exception($response);
|
||||
}
|
||||
}
|
||||
|
||||
if ($response === '') {
|
||||
$errors = $this->_getResponse($this->_buildPostBody($js, true));
|
||||
$errors = $this->getResponse($this->buildPostBody($js, true));
|
||||
throw new Minify_JS_ClosureCompiler_Exception($errors);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected $_fallbackFunc = null;
|
||||
|
||||
protected function _getResponse($postBody)
|
||||
/**
|
||||
* Get the response for a given POST body
|
||||
*
|
||||
* @param string $postBody
|
||||
* @return string
|
||||
* @throws Minify_JS_ClosureCompiler_Exception
|
||||
*/
|
||||
protected function getResponse($postBody)
|
||||
{
|
||||
$allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
|
||||
|
||||
if ($allowUrlFopen) {
|
||||
$contents = file_get_contents(self::URL, false, stream_context_create(array(
|
||||
$contents = file_get_contents($this->serviceUrl, false, stream_context_create(array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
|
||||
@@ -86,7 +179,7 @@ class Minify_JS_ClosureCompiler {
|
||||
)
|
||||
)));
|
||||
} elseif (defined('CURLOPT_POST')) {
|
||||
$ch = curl_init(self::URL);
|
||||
$ch = curl_init($this->serviceUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
|
||||
@@ -100,32 +193,37 @@ class Minify_JS_ClosureCompiler {
|
||||
"Could not make HTTP request: allow_url_open is false and cURL not available"
|
||||
);
|
||||
}
|
||||
|
||||
if (false === $contents) {
|
||||
throw new Minify_JS_ClosureCompiler_Exception(
|
||||
"No HTTP response from server"
|
||||
);
|
||||
}
|
||||
|
||||
return trim($contents);
|
||||
}
|
||||
|
||||
protected function _buildPostBody($js, $returnErrors = false)
|
||||
{
|
||||
return http_build_query(array(
|
||||
'js_code' => $js,
|
||||
'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
|
||||
'output_format' => 'text',
|
||||
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
|
||||
), null, '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Default fallback function if CC API fails
|
||||
* @param string $js
|
||||
* Build a POST request body
|
||||
*
|
||||
* @param string $js JavaScript code
|
||||
* @param bool $returnErrors
|
||||
* @return string
|
||||
*/
|
||||
protected function _fallback($js)
|
||||
protected function buildPostBody($js, $returnErrors = false)
|
||||
{
|
||||
return JSMin::minify($js);
|
||||
return http_build_query(
|
||||
array_merge(
|
||||
self::$DEFAULT_OPTIONS,
|
||||
$this->additionalOptions,
|
||||
array(
|
||||
'js_code' => $js,
|
||||
'output_info' => ($returnErrors ? 'errors' : 'compiled_code')
|
||||
)
|
||||
),
|
||||
null,
|
||||
'&'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ class Minify_YUICompressor {
|
||||
{
|
||||
self::_prepare();
|
||||
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
|
||||
throw new Exception('Minify_YUICompressor : could not create temp file.');
|
||||
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);
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<location>minify</location>
|
||||
<name>Minify</name>
|
||||
<license>BSD</license>
|
||||
<version>2.1.7</version>
|
||||
<version>2.2.1</version>
|
||||
<licenseversion></licenseversion>
|
||||
</library>
|
||||
<library>
|
||||
|
||||
@@ -3,6 +3,7 @@ information provided here is intended especially for developers.
|
||||
|
||||
=== 3.0 ===
|
||||
|
||||
* Minify updated to 2.2.1
|
||||
* External functions x_is_allowed_from_ajax() methods have been deprecated. Define 'ajax' => true in db/services.php instead.
|
||||
* External functions can be called without a session if they define 'loginrequired' => true in db/services.php.
|
||||
* All plugins are required to declare their frankenstyle component name via
|
||||
|
||||
Reference in New Issue
Block a user