PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 2.1.2
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v2.1.2
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / classes / Optimizer / JS.php

JS.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score 2.1.2, at includes/classes/Optimizer/JS.php

340 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * JS Optimizer
4 * Credits: https://github.com/Automattic/nginx-http-concat/blob/master/jsconcat.php
5 *
6 * @package PoweredCache\Optimizer
7 */
8
9 namespace PoweredCache\Optimizer;
10
11 use \WP_Scripts as WP_Scripts;
12
13 //phpcs:disable Squiz.Commenting.VariableComment.Missing
14 //phpcs:disable Generic.Commenting.DocComment.MissingShort
15 //phpcs:disable Squiz.Commenting.FunctionComment.MissingParamName
16 //phpcs:disable Squiz.Commenting.FunctionComment.MissingParamTag
17 //phpcs:disable Squiz.Commenting.FunctionComment.MissingParamComment
18
19
20 /**
21 * Class JS
22 */
23 class JS extends WP_Scripts {
24 private $old_scripts;
25 public $allow_gzip_compression;
26 public $do_minify;
27 public $excluded_files = [];
28
29 /**
30 * JS constructor.
31 *
32 * @param object $scripts \WP_Scripts
33 */
34 public function __construct( $scripts ) {
35 if ( empty( $scripts ) || ! ( $scripts instanceof WP_Scripts ) ) {
36 $this->old_scripts = new WP_Scripts();
37 } else {
38 $this->old_scripts = $scripts;
39 }
40
41 // Unset all the object properties except our private copy of the scripts object.
42 // We have to unset everything so that the overload methods talk to $this->old_scripts->whatever
43 // instead of $this->whatever.
44 foreach ( array_keys( get_object_vars( $this ) ) as $key ) {
45 if ( 'old_scripts' === $key ) {
46 continue;
47 }
48 unset( $this->$key );
49 }
50 }
51
52 /**
53 * Check given handle has inline JS
54 *
55 * @param string $handle JS handle
56 *
57 * @return bool
58 */
59 protected function has_inline_content( $handle ) {
60 $before_output = $this->get_data( $handle, 'before' );
61 if ( ! empty( $before_output ) ) {
62 return true;
63 }
64
65 $after_output = $this->get_data( $handle, 'after' );
66 if ( ! empty( $after_output ) ) {
67 return true;
68 }
69
70 // JavaScript translations
71 $has_translations = ! empty( $this->registered[ $handle ]->textdomain );
72 if ( $has_translations ) {
73 return true;
74 }
75
76 return false;
77 }
78
79 /**
80 * Prep items for concat
81 *
82 * @param bool $handles
83 * @param bool $group
84 *
85 * @return array|string[]
86 */
87 public function do_items( $handles = false, $group = false ) {
88 $handles = false === $handles ? $this->queue : (array) $handles;
89 $javascripts = array();
90 $siteurl = apply_filters( 'powered_cache_fo_site_url', $this->base_url );
91
92 $this->all_deps( $handles );
93 $level = 0;
94
95 foreach ( $this->to_do as $key => $handle ) {
96 if ( in_array( $handle, $this->done, true ) || ! isset( $this->registered[ $handle ] ) ) {
97 continue;
98 }
99
100 if ( 0 === $group && $this->groups[ $handle ] > 0 ) {
101 $this->in_footer[] = $handle;
102 unset( $this->to_do[ $key ] );
103 continue;
104 }
105
106 if ( ! $this->registered[ $handle ]->src ) { // Defines a group.
107 // if there are localized items, echo them
108 $this->print_extra_script( $handle );
109 $this->done[] = $handle;
110 continue;
111 }
112
113 if ( false === $group && in_array( $handle, $this->in_footer, true ) ) {
114 $this->in_footer = array_diff( $this->in_footer, (array) $handle );
115 }
116
117 $obj = $this->registered[ $handle ];
118 $js_url = $obj->src;
119 $js_url_parsed = wp_parse_url( $js_url );
120 $extra = $obj->extra;
121
122 // Don't concat by default
123 $do_concat = false;
124
125 // Only try to concat static js files
126 if ( false !== strpos( $js_url_parsed['path'], '.js' ) ) {
127 $do_concat = true;
128 }
129
130 // Don't try to concat externally hosted scripts
131 $is_internal_url = Helper::is_internal_url( $js_url, $siteurl );
132 if ( ! $is_internal_url ) {
133 $do_concat = false;
134 }
135
136 // Concat and canonicalize the paths only for
137 // existing scripts that aren't outside ABSPATH
138 $js_realpath = Helper::realpath( $js_url, $siteurl );
139 if ( ! $js_realpath || 0 !== strpos( $js_realpath, ABSPATH ) ) {
140 $do_concat = false;
141 } else {
142 $js_url_parsed['path'] = substr( $js_realpath, strlen( ABSPATH ) - 1 );
143 }
144
145 if ( $this->has_inline_content( $handle ) ) {
146 $do_concat = false;
147 }
148
149 // Skip core scripts that use Strict Mode
150 if ( 'react' === $handle || 'react-dom' === $handle ) {
151 $do_concat = false;
152 }
153
154 if ( Helper::is_excluded_js( $js_url ) ) {
155 $do_concat = false;
156 }
157
158 /**
159 * Allow plugins to disable concatenation of certain scripts.
160 *
161 * @hook powered_cache_fo_js_do_concat
162 *
163 * @param {boolean} $do_concat Contact status.
164 * @param {string} $handle Handle of script.
165 *
166 * @return {boolean} New value.
167 * @since 2.0
168 */
169 $do_concat = apply_filters( 'powered_cache_fo_js_do_concat', $do_concat, $handle );
170
171 if ( true === $do_concat ) {
172 if ( ! isset( $javascripts[ $level ] ) ) {
173 $javascripts[ $level ]['type'] = 'concat';
174 }
175
176 $javascripts[ $level ]['paths'][] = $js_url_parsed['path'];
177 $javascripts[ $level ]['handles'][] = $handle;
178
179 } else {
180 $level ++;
181 $javascripts[ $level ]['type'] = 'do_item';
182 $javascripts[ $level ]['handle'] = $handle;
183 $level ++;
184 }
185 unset( $this->to_do[ $key ] );
186 }
187
188 if ( empty( $javascripts ) ) {
189 return $this->done;
190 }
191
192 foreach ( $javascripts as $js_array ) {
193 if ( 'do_item' === $js_array['type'] ) {
194 if ( $this->do_item( $js_array['handle'], $group ) ) {
195 $this->done[] = $js_array['handle'];
196 }
197 } elseif ( 'concat' === $js_array['type'] ) {
198 array_map( array( $this, 'print_extra_script' ), $js_array['handles'] );
199
200 if ( isset( $js_array['paths'] ) && count( $js_array['paths'] ) > 1 ) {
201 $paths = array_map(
202 function ( $url ) {
203 return ABSPATH . $url;
204 },
205 $js_array['paths']
206 );
207 $mtime = max( array_map( 'filemtime', $paths ) );
208 $path_str = implode( ',', $js_array['paths'] ) . "?m=${mtime}j";
209
210 if ( $this->allow_gzip_compression ) {
211 $path_64 = base64_encode( gzcompress( $path_str ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
212 if ( strlen( $path_str ) > ( strlen( $path_64 ) + 1 ) ) {
213 $path_str = '-' . $path_64;
214 }
215 }
216
217 $href = Helper::get_optimized_url( $path_str, $this->do_minify );
218 } elseif ( isset( $js_array['paths'] ) && is_array( $js_array['paths'] ) ) {
219 $href = $this->cache_bust_mtime( $siteurl . $js_array['paths'][0], $siteurl );
220 }
221
222 $this->done = array_merge( $this->done, $js_array['handles'] );
223
224 // Print before/after scripts from wp_inline_scripts() and concatenated script tag
225 if ( isset( $js_array['extras']['before'] ) ) {
226 foreach ( $js_array['extras']['before'] as $inline_before ) {
227 echo $inline_before; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
228 }
229 }
230 if ( isset( $href ) ) {
231 /**
232 * Filters combined script loader tags.
233 *
234 * @hook powered_cache_fo_script_loader_tag
235 *
236 * @param {string} $js_tag JS tag.
237 *
238 * @return {string} New value.
239 * @since 2.0
240 */
241 echo apply_filters( 'powered_cache_fo_script_loader_tag', "<script type='text/javascript' src='$href'></script>", $href ); // phpcs:ignore
242 echo PHP_EOL;
243 }
244 if ( isset( $js_array['extras']['after'] ) ) {
245 foreach ( $js_array['extras']['after'] as $inline_after ) {
246 echo $inline_after; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
247 }
248 }
249 }
250 }
251
252 /**
253 * Fires after combine JS
254 *
255 * @hook powered_cache_fo_js_concat_did_items
256 *
257 * @param {array} $javascripts JS resources.
258 *
259 * @since 2.0
260 */
261 do_action( 'powered_cache_fo_js_concat_did_items', $javascripts );
262
263 return $this->done;
264 }
265
266 /**
267 * Bust cache
268 *
269 * @param string $url URL
270 * @param string $siteurl Site URL
271 *
272 * @return string
273 */
274 public function cache_bust_mtime( $url, $siteurl ) {
275 if ( strpos( $url, '?m=' ) ) {
276 return $url;
277 }
278
279 $parts = wp_parse_url( $url );
280 if ( ! isset( $parts['path'] ) || empty( $parts['path'] ) ) {
281 return $url;
282 }
283
284 $file = Helper::realpath( $url, $siteurl );
285
286 $mtime = false;
287 if ( file_exists( $file ) ) {
288 $mtime = filemtime( $file );
289 }
290
291 if ( ! $mtime ) {
292 return $url;
293 }
294
295 if ( false === strpos( $url, '?' ) ) {
296 $q = '';
297 } else {
298 list( $url, $q ) = explode( '?', $url, 2 );
299 if ( strlen( $q ) ) {
300 $q = '&amp;' . $q;
301 }
302 }
303
304 return "$url?m={$mtime}g{$q}";
305 }
306
307 /**
308 * @param $key
309 *
310 * @return bool
311 */
312 public function __isset( $key ) {
313 return isset( $this->old_scripts->$key );
314 }
315
316 /**
317 * @param $key
318 */
319 public function __unset( $key ) {
320 unset( $this->old_scripts->$key );
321 }
322
323 /**
324 * @param $key
325 *
326 * @return mixed
327 */
328 public function &__get( $key ) {
329 return $this->old_scripts->$key;
330 }
331
332 /**
333 * @param $key
334 * @param $value
335 */
336 public function __set( $key, $value ) {
337 $this->old_scripts->$key = $value;
338 }
339 }
340