mirror of
https://github.com/mrclay/minify.git
synced 2025-01-18 05:38:16 +01:00
Merge branch 'Jimdo-jimdo-compiler-service-options'
This commit is contained in:
commit
ef726e261b
@ -14,16 +14,68 @@
|
||||
* @todo can use a stream wrapper to unit test this?
|
||||
*/
|
||||
class Minify_JS_ClosureCompiler {
|
||||
/**
|
||||
* @var $url URL to compiler server. defaults to google server
|
||||
*/
|
||||
protected $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())
|
||||
@ -33,58 +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 : URL to closure compiler server
|
||||
* 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['compilerUrl'])) {
|
||||
$this->url = $options['compilerUrl'];
|
||||
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($this->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",
|
||||
@ -94,7 +179,7 @@ class Minify_JS_ClosureCompiler {
|
||||
)
|
||||
)));
|
||||
} elseif (defined('CURLOPT_POST')) {
|
||||
$ch = curl_init($this->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'));
|
||||
@ -108,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,
|
||||
'&'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ require_once '_inc.php';
|
||||
function test_Minify_JS_ClosureCompiler()
|
||||
{
|
||||
global $thisDir;
|
||||
|
||||
|
||||
$src = "
|
||||
(function (window, undefined){
|
||||
function addOne(input) {
|
||||
@ -42,6 +42,84 @@ function test_Minify_JS_ClosureCompiler()
|
||||
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n";
|
||||
}
|
||||
|
||||
// Test maximum byte size check (default)
|
||||
$fn = "(function() {})();";
|
||||
$src = str_repeat($fn, ceil(Minify_JS_ClosureCompiler::DEFAULT_MAX_BYTES / strlen($fn)));
|
||||
$exc = null;
|
||||
try {
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($src);
|
||||
} catch (Exception $e) {
|
||||
$exc = $e;
|
||||
}
|
||||
$passed = assertTrue(
|
||||
$exc instanceof Minify_JS_ClosureCompiler_Exception
|
||||
, 'Minify_JS_ClosureCompiler : Throws Minify_JS_ClosureCompiler_Exception');
|
||||
assertTrue(
|
||||
$exc->getMessage() === 'POST content larger than ' . Minify_JS_ClosureCompiler::DEFAULT_MAX_BYTES . ' bytes'
|
||||
, 'Minify_JS_ClosureCompiler : Message must tell how big maximum byte size is');
|
||||
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n";
|
||||
}
|
||||
|
||||
// Test maximum byte size check (no limit)
|
||||
$src = "(function(){})();";
|
||||
try {
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($src, array(
|
||||
Minify_JS_ClosureCompiler::OPTION_MAX_BYTES => 0
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
$exc = $e;
|
||||
}
|
||||
$passed = assertTrue(
|
||||
$src === $minOutput
|
||||
, 'Minify_JS_ClosureCompiler : With no limit set, it should compile properly');
|
||||
|
||||
// Test maximum byte size check (custom)
|
||||
$src = "(function() {})();";
|
||||
$allowedBytes = 5;
|
||||
$exc = null;
|
||||
try {
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($src, array(
|
||||
Minify_JS_ClosureCompiler::OPTION_MAX_BYTES => $allowedBytes
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
$exc = $e;
|
||||
}
|
||||
$passed = assertTrue(
|
||||
$exc instanceof Minify_JS_ClosureCompiler_Exception
|
||||
, 'Minify_JS_ClosureCompiler : Throws Minify_JS_ClosureCompiler_Exception');
|
||||
assertTrue(
|
||||
$exc->getMessage() === 'POST content larger than ' . $allowedBytes . ' bytes'
|
||||
, 'Minify_JS_ClosureCompiler : Message must tell how big maximum byte size is');
|
||||
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n";
|
||||
}
|
||||
|
||||
// Test additional options passed to HTTP request
|
||||
$ecmascript5 = "[1,].length;";
|
||||
$exc = null;
|
||||
try {
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($ecmascript5);
|
||||
} catch (Exception $e) {
|
||||
$exc = $e;
|
||||
}
|
||||
$passed = assertTrue(
|
||||
$exc instanceof Minify_JS_ClosureCompiler_Exception
|
||||
, 'Minify_JS_ClosureCompiler : Throws Minify_JS_ClosureCompiler_Exception');
|
||||
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n";
|
||||
}
|
||||
|
||||
$minExpected = '1;';
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($ecmascript5, array(
|
||||
Minify_JS_ClosureCompiler::OPTION_ADDITIONAL_OPTIONS => array(
|
||||
'language' => 'ECMASCRIPT5'
|
||||
)
|
||||
));
|
||||
$passed = assertTrue(
|
||||
$minOutput === $minExpected
|
||||
, 'Minify_JS_ClosureCompiler : Language option should make it compile');
|
||||
}
|
||||
|
||||
test_Minify_JS_ClosureCompiler();
|
||||
|
Loading…
x
Reference in New Issue
Block a user