PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / mrclay / minify / lib / Minify / JS / ClosureCompiler.php

ClosureCompiler.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.7.0, at vendor/mrclay/minify/lib/Minify/JS/ClosureCompiler.php

238 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Minify_JS_ClosureCompiler
4 * @package Minify
5 */
6
7 /**
8 * Minify Javascript using Google's Closure Compiler API
9 *
10 * @link http://code.google.com/closure/compiler/
11 * @package Minify
12 * @author Stephen Clay <[email protected]>
13 *
14 * @todo can use a stream wrapper to unit test this?
15 */
16 class Minify_JS_ClosureCompiler
17 {
18
19 /**
20 * @var string The option key for the maximum POST byte size
21 */
22 const OPTION_MAX_BYTES = 'maxBytes';
23
24 /**
25 * @var string The option key for additional params. @see __construct
26 */
27 const OPTION_ADDITIONAL_OPTIONS = 'additionalParams';
28
29 /**
30 * @var string The option key for the fallback Minifier
31 */
32 const OPTION_FALLBACK_FUNCTION = 'fallbackFunc';
33
34 /**
35 * @var string The option key for the service URL
36 */
37 const OPTION_COMPILER_URL = 'compilerUrl';
38
39 /**
40 * @var int The default maximum POST byte size according to https://developers.google.com/closure/compiler/docs/api-ref
41 */
42 const DEFAULT_MAX_BYTES = 200000;
43
44 /**
45 * @var string[] $DEFAULT_OPTIONS The default options to pass to the compiler service
46 *
47 * @note This would be a constant if PHP allowed it
48 */
49 private static $DEFAULT_OPTIONS = array(
50 'output_format' => 'text',
51 'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
52 );
53
54 /**
55 * @var string $url URL of compiler server. defaults to Google's
56 */
57 protected $serviceUrl = 'https://closure-compiler.appspot.com/compile';
58
59 /**
60 * @var int $maxBytes The maximum JS size that can be sent to the compiler server in bytes
61 */
62 protected $maxBytes = self::DEFAULT_MAX_BYTES;
63
64 /**
65 * @var string[] $additionalOptions Additional options to pass to the compiler service
66 */
67 protected $additionalOptions = array();
68
69 /**
70 * @var callable Function to minify JS if service fails. Default is JSMin
71 */
72 protected $fallbackMinifier = array('JSMin\\JSMin', 'minify');
73
74 /**
75 * Minify JavaScript code via HTTP request to a Closure Compiler API
76 *
77 * @param string $js input code
78 * @param array $options Options passed to __construct(). @see __construct
79 *
80 * @return string
81 */
82 public static function minify($js, array $options = array())
83 {
84 $obj = new self($options);
85
86 return $obj->min($js);
87 }
88
89 /**
90 * @param array $options Options with keys available below:
91 *
92 * fallbackFunc : (callable) function to minify if service unavailable. Default is JSMin.
93 *
94 * compilerUrl : (string) URL to closure compiler server
95 *
96 * maxBytes : (int) The maximum amount of bytes to be sent as js_code in the POST request.
97 * Defaults to 200000.
98 *
99 * additionalParams : (string[]) Additional parameters to pass to the compiler server. Can be anything named
100 * in https://developers.google.com/closure/compiler/docs/api-ref except for js_code and
101 * output_info
102 */
103 public function __construct(array $options = array())
104 {
105 if (isset($options[self::OPTION_FALLBACK_FUNCTION])) {
106 $this->fallbackMinifier = $options[self::OPTION_FALLBACK_FUNCTION];
107 }
108 if (isset($options[self::OPTION_COMPILER_URL])) {
109 $this->serviceUrl = $options[self::OPTION_COMPILER_URL];
110 }
111 if (isset($options[self::OPTION_ADDITIONAL_OPTIONS]) && is_array($options[self::OPTION_ADDITIONAL_OPTIONS])) {
112 $this->additionalOptions = $options[self::OPTION_ADDITIONAL_OPTIONS];
113 }
114 if (isset($options[self::OPTION_MAX_BYTES])) {
115 $this->maxBytes = (int) $options[self::OPTION_MAX_BYTES];
116 }
117 }
118
119 /**
120 * Call the service to perform the minification
121 *
122 * @param string $js JavaScript code
123 * @return string
124 * @throws Minify_JS_ClosureCompiler_Exception
125 */
126 public function min($js)
127 {
128 $postBody = $this->buildPostBody($js);
129
130 if ($this->maxBytes > 0) {
131 $bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
132 ? mb_strlen($postBody, '8bit')
133 : strlen($postBody);
134 if ($bytes > $this->maxBytes) {
135 throw new Minify_JS_ClosureCompiler_Exception(
136 'POST content larger than ' . $this->maxBytes . ' bytes'
137 );
138 }
139 }
140
141 $response = $this->getResponse($postBody);
142
143 if (preg_match('/^Error\(\d\d?\):/', $response)) {
144 if (is_callable($this->fallbackMinifier)) {
145 // use fallback
146 $response = "/* Received errors from Closure Compiler API:\n$response"
147 . "\n(Using fallback minifier)\n*/\n";
148 $response .= call_user_func($this->fallbackMinifier, $js);
149 } else {
150 throw new Minify_JS_ClosureCompiler_Exception($response);
151 }
152 }
153
154 if ($response === '') {
155 $errors = $this->getResponse($this->buildPostBody($js, true));
156 throw new Minify_JS_ClosureCompiler_Exception($errors);
157 }
158
159 return $response;
160 }
161
162 /**
163 * Get the response for a given POST body
164 *
165 * @param string $postBody
166 * @return string
167 * @throws Minify_JS_ClosureCompiler_Exception
168 */
169 protected function getResponse($postBody)
170 {
171 $allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
172
173 if ($allowUrlFopen) {
174 $contents = file_get_contents($this->serviceUrl, false, stream_context_create(array(
175 'http' => array(
176 'method' => 'POST',
177 'compilation_level' => 'SIMPLE',
178 'output_format' => 'text',
179 'output_info' => 'compiled_code',
180 'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
181 'content' => $postBody,
182 'max_redirects' => 0,
183 'timeout' => 15,
184 )
185 )));
186 } elseif (defined('CURLOPT_POST')) {
187 $ch = curl_init($this->serviceUrl);
188 curl_setopt($ch, CURLOPT_POST, true);
189 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
190 curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
191 curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
192 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
193 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
194 $contents = curl_exec($ch);
195 curl_close($ch);
196 } else {
197 throw new Minify_JS_ClosureCompiler_Exception(
198 "Could not make HTTP request: allow_url_open is false and cURL not available"
199 );
200 }
201
202 if (false === $contents) {
203 throw new Minify_JS_ClosureCompiler_Exception(
204 "No HTTP response from server"
205 );
206 }
207
208 return trim($contents);
209 }
210
211 /**
212 * Build a POST request body
213 *
214 * @param string $js JavaScript code
215 * @param bool $returnErrors
216 * @return string
217 */
218 protected function buildPostBody($js, $returnErrors = false)
219 {
220 return http_build_query(
221 array_merge(
222 self::$DEFAULT_OPTIONS,
223 $this->additionalOptions,
224 array(
225 'js_code' => $js,
226 'output_info' => ($returnErrors ? 'errors' : 'compiled_code')
227 )
228 ),
229 null,
230 '&'
231 );
232 }
233 }
234
235 class Minify_JS_ClosureCompiler_Exception extends Exception
236 {
237 }
238