summaryrefslogtreecommitdiffstats
path: root/admin/survey/minify/lib/Minify/JS/ClosureCompiler.php
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-01-11 12:35:47 +0100
committerAnton Luka Šijanec <anton@sijanec.eu>2022-01-11 12:35:47 +0100
commit19985dbb8c0aa66dc4bf7905abc1148de909097d (patch)
tree2cd5a5d20d7e80fc2a51adf60d838d8a2c40999e /admin/survey/minify/lib/Minify/JS/ClosureCompiler.php
download1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.tar
1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.tar.gz
1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.tar.bz2
1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.tar.lz
1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.tar.xz
1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.tar.zst
1ka-19985dbb8c0aa66dc4bf7905abc1148de909097d.zip
Diffstat (limited to 'admin/survey/minify/lib/Minify/JS/ClosureCompiler.php')
-rw-r--r--admin/survey/minify/lib/Minify/JS/ClosureCompiler.php237
1 files changed, 237 insertions, 0 deletions
diff --git a/admin/survey/minify/lib/Minify/JS/ClosureCompiler.php b/admin/survey/minify/lib/Minify/JS/ClosureCompiler.php
new file mode 100644
index 0000000..05e2e33
--- /dev/null
+++ b/admin/survey/minify/lib/Minify/JS/ClosureCompiler.php
@@ -0,0 +1,237 @@
+<?php
+/**
+ * Class Minify_JS_ClosureCompiler
+ * @package Minify
+ */
+
+/**
+ * Minify Javascript using Google's Closure Compiler API
+ *
+ * @link http://code.google.com/closure/compiler/
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ *
+ * @todo can use a stream wrapper to unit test this?
+ */
+class Minify_JS_ClosureCompiler
+{
+
+ /**
+ * @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 = 'https://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\\JSMin', 'minify');
+
+ /**
+ * Minify JavaScript code via HTTP request to a Closure Compiler API
+ *
+ * @param string $js input code
+ * @param array $options Options passed to __construct(). @see __construct
+ *
+ * @return string
+ */
+ public static function minify($js, array $options = array())
+ {
+ $obj = new self($options);
+
+ return $obj->min($js);
+ }
+
+ /**
+ * @param array $options Options with keys available below:
+ *
+ * fallbackFunc : (callable) function to minify if service unavailable. Default is JSMin.
+ *
+ * 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())
+ {
+ 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);
+
+ 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);
+
+ if (preg_match('/^Error\(\d\d?\):/', $response)) {
+ 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->fallbackMinifier, $js);
+ } else {
+ throw new Minify_JS_ClosureCompiler_Exception($response);
+ }
+ }
+
+ if ($response === '') {
+ $errors = $this->getResponse($this->buildPostBody($js, true));
+ throw new Minify_JS_ClosureCompiler_Exception($errors);
+ }
+
+ return $response;
+ }
+
+ /**
+ * 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->serviceUrl, false, stream_context_create(array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'compilation_level' => 'SIMPLE',
+ 'output_format' => 'text',
+ 'output_info' => 'compiled_code',
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
+ 'content' => $postBody,
+ 'max_redirects' => 0,
+ 'timeout' => 15,
+ )
+ )));
+ } elseif (defined('CURLOPT_POST')) {
+ $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'));
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
+ $contents = curl_exec($ch);
+ curl_close($ch);
+ } else {
+ throw new Minify_JS_ClosureCompiler_Exception(
+ "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);
+ }
+
+ /**
+ * 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(
+ array_merge(
+ self::$DEFAULT_OPTIONS,
+ $this->additionalOptions,
+ array(
+ 'js_code' => $js,
+ 'output_info' => ($returnErrors ? 'errors' : 'compiled_code')
+ )
+ ),
+ null,
+ '&'
+ );
+ }
+}
+
+class Minify_JS_ClosureCompiler_Exception extends Exception
+{
+}