1
0
mirror of https://github.com/mrclay/minify.git synced 2025-03-14 09:29:38 +01:00

Clean up pull/84 additions

This commit is contained in:
Steve Clay 2014-02-04 09:57:56 -05:00
parent 5d73cd0886
commit a35b801ce9
2 changed files with 104 additions and 67 deletions

View File

@ -16,50 +16,66 @@
class Minify_JS_ClosureCompiler { class Minify_JS_ClosureCompiler {
/** /**
* @var string the option for the maximum POST byte size * @var string The option key for the maximum POST byte size
*/ */
const OPTION_MAX_BYTES = 'maxBytes'; const OPTION_MAX_BYTES = 'maxBytes';
/** /**
* @var int the default maximum POST byte size according to https://developers.google.com/closure/compiler/docs/api-ref * @var string The option key for additional params. @see __construct
*/
const MAX_BYTES_DEFAULT = 200000;
/**
* @var $url URL to compiler server. defaults to google server
*/ */
protected $url = 'http://closure-compiler.appspot.com/compile'; const OPTION_ADDITIONAL_OPTIONS = 'additionalParams';
/** /**
* @var $maxBytes The maximum JS size that can be sent to the compiler server in bytes * @var string The option key for the fallback Minifier
*/ */
protected $maxBytes = self::MAX_BYTES_DEFAULT; const OPTION_FALLBACK_FUNCTION = 'fallbackFunc';
/** /**
* @var $additionalOptions array additional options to pass to the compiler service * @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(); protected $additionalOptions = array();
/** /**
* @var $DEFAULT_OPTIONS array the default options to pass to the compiler service * @var callable Function to minify JS if service fails. Default is JSMin
*/ */
private static $DEFAULT_OPTIONS = array( protected $fallbackMinifier = array('JSMin', 'minify');
'output_format' => 'text',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS');
/** /**
* @var string the option for additional params. * Minify JavaScript code via HTTP request to a Closure Compiler API
* Read more about additional params here: https://developers.google.com/closure/compiler/docs/api-ref
* This also allows you to override the output_format or the compilation_level.
* The parameters js_code and output_info can not be set in this way
*/
const OPTION_ADDITIONAL_HTTP_PARAMS = 'additionalParams';
/**
* Minify Javascript code via HTTP request to the Closure Compiler API
* *
* @param string $js input code * @param string $js input code
* @param array $options unused at this point * @param array $options Options passed to __construct(). @see __construct
*
* @return string * @return string
*/ */
public static function minify($js, array $options = array()) public static function minify($js, array $options = array())
@ -69,35 +85,46 @@ 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 : array default array($this, '_fallback'); * compilerUrl : (string) 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. * maxBytes : (int) The maximum amount of bytes to be sent as js_code in the POST request.
* additionalParams: array The 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 * 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()) public function __construct(array $options = array())
{ {
$this->_fallbackFunc = isset($options['fallbackMinifier']) if (isset($options[self::OPTION_FALLBACK_FUNCTION])) {
? $options['fallbackMinifier'] $this->fallbackMinifier = $options[self::OPTION_FALLBACK_FUNCTION];
: array($this, '_fallback');
if (isset($options['compilerUrl'])) {
$this->url = $options['compilerUrl'];
} }
if (isset($options[self::OPTION_COMPILER_URL])) {
if (isset($options[self::OPTION_ADDITIONAL_HTTP_PARAMS]) && is_array($options[self::OPTION_ADDITIONAL_HTTP_PARAMS])) { $this->serviceUrl = $options[self::OPTION_COMPILER_URL];
$this->additionalOptions = $options[self::OPTION_ADDITIONAL_HTTP_PARAMS]; }
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])) { if (isset($options[self::OPTION_MAX_BYTES])) {
$this->maxBytes = (int) $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) public function min($js)
{ {
$postBody = $this->_buildPostBody($js); $postBody = $this->buildPostBody($js);
if ($this->maxBytes > 0) { if ($this->maxBytes > 0) {
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) $bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($postBody, '8bit') ? mb_strlen($postBody, '8bit')
@ -108,30 +135,41 @@ class Minify_JS_ClosureCompiler {
); );
} }
} }
$response = $this->_getResponse($postBody);
$response = $this->getResponse($postBody);
if (preg_match('/^Error\(\d\d?\):/', $response)) { 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" $response = "/* Received errors from Closure Compiler API:\n$response"
. "\n(Using fallback minifier)\n*/\n"; . "\n(Using fallback minifier)\n*/\n";
$response .= call_user_func($this->_fallbackFunc, $js); $response .= call_user_func($this->fallbackMinifier, $js);
} else { } else {
throw new Minify_JS_ClosureCompiler_Exception($response); throw new Minify_JS_ClosureCompiler_Exception($response);
} }
} }
if ($response === '') { if ($response === '') {
$errors = $this->_getResponse($this->_buildPostBody($js, true)); $errors = $this->getResponse($this->buildPostBody($js, true));
throw new Minify_JS_ClosureCompiler_Exception($errors); throw new Minify_JS_ClosureCompiler_Exception($errors);
} }
return $response; return $response;
} }
protected $_fallbackFunc = null; /**
* Get the response for a given POST body
protected function _getResponse($postBody) *
* @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')); $allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
if ($allowUrlFopen) { 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( 'http' => array(
'method' => 'POST', 'method' => 'POST',
'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n", 'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
@ -141,7 +179,7 @@ class Minify_JS_ClosureCompiler {
) )
))); )));
} elseif (defined('CURLOPT_POST')) { } elseif (defined('CURLOPT_POST')) {
$ch = curl_init($this->url); $ch = curl_init($this->serviceUrl);
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
@ -155,15 +193,24 @@ class Minify_JS_ClosureCompiler {
"Could not make HTTP request: allow_url_open is false and cURL not available" "Could not make HTTP request: allow_url_open is false and cURL not available"
); );
} }
if (false === $contents) { if (false === $contents) {
throw new Minify_JS_ClosureCompiler_Exception( throw new Minify_JS_ClosureCompiler_Exception(
"No HTTP response from server" "No HTTP response from server"
); );
} }
return trim($contents); return trim($contents);
} }
protected function _buildPostBody($js, $returnErrors = false) /**
* Build a POST request body
*
* @param string $js JavaScript code
* @param bool $returnErrors
* @return string
*/
protected function buildPostBody($js, $returnErrors = false)
{ {
return http_build_query( return http_build_query(
array_merge( array_merge(
@ -178,16 +225,6 @@ class Minify_JS_ClosureCompiler {
'&' '&'
); );
} }
/**
* Default fallback function if CC API fails
* @param string $js
* @return string
*/
protected function _fallback($js)
{
return JSMin::minify($js);
}
} }
class Minify_JS_ClosureCompiler_Exception extends Exception {} class Minify_JS_ClosureCompiler_Exception extends Exception {}

View File

@ -45,7 +45,7 @@ function test_Minify_JS_ClosureCompiler()
// Test maximum byte size check (default) // Test maximum byte size check (default)
$fn = "(function() {})();"; $fn = "(function() {})();";
$src = str_repeat($fn, ceil(Minify_JS_ClosureCompiler::MAX_BYTES_DEFAULT / strlen($fn))); $src = str_repeat($fn, ceil(Minify_JS_ClosureCompiler::DEFAULT_MAX_BYTES / strlen($fn)));
$exc = null; $exc = null;
try { try {
$minOutput = Minify_JS_ClosureCompiler::minify($src); $minOutput = Minify_JS_ClosureCompiler::minify($src);
@ -56,7 +56,7 @@ function test_Minify_JS_ClosureCompiler()
$exc instanceof Minify_JS_ClosureCompiler_Exception $exc instanceof Minify_JS_ClosureCompiler_Exception
, 'Minify_JS_ClosureCompiler : Throws Minify_JS_ClosureCompiler_Exception'); , 'Minify_JS_ClosureCompiler : Throws Minify_JS_ClosureCompiler_Exception');
assertTrue( assertTrue(
$exc->getMessage() === 'POST content larger than ' . Minify_JS_ClosureCompiler::MAX_BYTES_DEFAULT . ' bytes' $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'); , 'Minify_JS_ClosureCompiler : Message must tell how big maximum byte size is');
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n"; echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n";
@ -113,7 +113,7 @@ function test_Minify_JS_ClosureCompiler()
$minExpected = '1;'; $minExpected = '1;';
$minOutput = Minify_JS_ClosureCompiler::minify($ecmascript5, array( $minOutput = Minify_JS_ClosureCompiler::minify($ecmascript5, array(
Minify_JS_ClosureCompiler::OPTION_ADDITIONAL_HTTP_PARAMS => array( Minify_JS_ClosureCompiler::OPTION_ADDITIONAL_OPTIONS => array(
'language' => 'ECMASCRIPT5' 'language' => 'ECMASCRIPT5'
) )
)); ));