This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Class for JS optimization. |
||
4 | */ |
||
5 | |||
6 | if ( ! defined( 'ABSPATH' ) ) { |
||
7 | exit; |
||
8 | } |
||
9 | |||
10 | class autoptimizeScripts extends autoptimizeBase |
||
11 | { |
||
12 | |||
13 | /** |
||
14 | * Stores founds scripts. |
||
15 | * |
||
16 | * @var array |
||
17 | */ |
||
18 | private $scripts = array(); |
||
19 | |||
20 | /** |
||
21 | * Stores to be moved JS. |
||
22 | * |
||
23 | * @var array |
||
24 | */ |
||
25 | private $move = array( |
||
26 | 'first' => array(), |
||
27 | 'last' => array(), |
||
28 | ); |
||
29 | |||
30 | /** |
||
31 | * List of not to be moved JS. |
||
32 | * |
||
33 | * @var array |
||
34 | */ |
||
35 | private $dontmove = array( |
||
36 | 'document.write', |
||
37 | 'html5.js', |
||
38 | 'show_ads.js', |
||
39 | 'google_ad', |
||
40 | 'histats.com/js', |
||
41 | 'statcounter.com/counter/counter.js', |
||
42 | 'ws.amazon.com/widgets', |
||
43 | 'media.fastclick.net', |
||
44 | '/ads/', |
||
45 | 'comment-form-quicktags/quicktags.php', |
||
46 | 'edToolbar', |
||
47 | 'intensedebate.com', |
||
48 | 'scripts.chitika.net/', |
||
49 | '_gaq.push', |
||
50 | 'jotform.com/', |
||
51 | 'admin-bar.min.js', |
||
52 | 'GoogleAnalyticsObject', |
||
53 | 'plupload.full.min.js', |
||
54 | 'syntaxhighlighter', |
||
55 | 'adsbygoogle', |
||
56 | 'gist.github.com', |
||
57 | '_stq', |
||
58 | 'nonce', |
||
59 | 'post_id', |
||
60 | 'data-noptimize', |
||
61 | 'logHuman', |
||
62 | 'amp-mobile-version-switcher', |
||
63 | 'data-rocketlazyloadscript', |
||
64 | 'rocket-browser-checker-js-after', |
||
65 | ); |
||
66 | |||
67 | /** |
||
68 | * List of to be moved JS. |
||
69 | * |
||
70 | * @var array |
||
71 | */ |
||
72 | private $domove = array( |
||
73 | 'gaJsHost', |
||
74 | 'load_cmc', |
||
75 | 'jd.gallery.transitions.js', |
||
76 | 'swfobject.embedSWF(', |
||
77 | 'tiny_mce.js', |
||
78 | 'tinyMCEPreInit.go', |
||
79 | ); |
||
80 | |||
81 | /** |
||
82 | * List of JS that can be moved last (not used any more). |
||
83 | * |
||
84 | * @var array |
||
85 | */ |
||
86 | private $domovelast = array( |
||
87 | 'addthis.com', |
||
88 | '/afsonline/show_afs_search.js', |
||
89 | 'disqus.js', |
||
90 | 'networkedblogs.com/getnetworkwidget', |
||
91 | 'infolinks.com/js/', |
||
92 | 'jd.gallery.js.php', |
||
93 | 'jd.gallery.transitions.js', |
||
94 | 'swfobject.embedSWF(', |
||
95 | 'linkwithin.com/widget.js', |
||
96 | 'tiny_mce.js', |
||
97 | 'tinyMCEPreInit.go', |
||
98 | ); |
||
99 | |||
100 | /** |
||
101 | * Setting CDN base URL. |
||
102 | * |
||
103 | * @var string |
||
104 | */ |
||
105 | public $cdn_url = ''; |
||
106 | |||
107 | /** |
||
108 | * Setting; aggregate or not. |
||
109 | * |
||
110 | * @var bool |
||
111 | */ |
||
112 | private $aggregate = true; |
||
113 | |||
114 | /** |
||
115 | * Setting; if not aggregated, should we defer? |
||
116 | * |
||
117 | * @var bool |
||
118 | */ |
||
119 | private $defer_not_aggregate = false; |
||
120 | |||
121 | /** |
||
122 | * Setting; try/catch wrapping or not. |
||
123 | * |
||
124 | * @var bool |
||
125 | */ |
||
126 | private $trycatch = false; |
||
127 | |||
128 | /** |
||
129 | * State; is JS already minified. |
||
130 | * |
||
131 | * @var bool |
||
132 | */ |
||
133 | private $alreadyminified = false; |
||
134 | |||
135 | /** |
||
136 | * Setting; force JS in head or not. |
||
137 | * |
||
138 | * @var bool |
||
139 | */ |
||
140 | private $forcehead = true; |
||
141 | |||
142 | /** |
||
143 | * Setting; aggregate inline JS or not. |
||
144 | * |
||
145 | * @var bool |
||
146 | */ |
||
147 | private $include_inline = false; |
||
148 | |||
149 | /** |
||
150 | * State; holds JS code. |
||
151 | * |
||
152 | * @var string |
||
153 | */ |
||
154 | private $jscode = ''; |
||
155 | |||
156 | /** |
||
157 | * State; holds URL of JS-file. |
||
158 | * |
||
159 | * @var string |
||
160 | */ |
||
161 | private $url = ''; |
||
162 | |||
163 | /** |
||
164 | * State; stores rest of HTML if (old) option "only in head" is on. |
||
165 | * |
||
166 | * @var string |
||
167 | */ |
||
168 | private $restofcontent = ''; |
||
169 | |||
170 | /** |
||
171 | * State; holds md5-hash. |
||
172 | * |
||
173 | * @var string |
||
174 | */ |
||
175 | private $md5hash = ''; |
||
176 | |||
177 | /** |
||
178 | * Setting (filter); allowlist of to be aggregated JS. |
||
179 | * |
||
180 | * @var string |
||
181 | */ |
||
182 | private $allowlist = ''; |
||
183 | |||
184 | /** |
||
185 | * Setting (filter); holds JS that should be removed. |
||
186 | * |
||
187 | * @var array |
||
188 | */ |
||
189 | private $jsremovables = array(); |
||
190 | |||
191 | /** |
||
192 | * Setting (filter); can we inject already minified files after the |
||
193 | * unminified aggregate JS has been minified. |
||
194 | * |
||
195 | * @var bool |
||
196 | */ |
||
197 | private $inject_min_late = true; |
||
198 | |||
199 | /** |
||
200 | * Setting; should excluded JS be minified (if not already). |
||
201 | * |
||
202 | * @var bool |
||
203 | */ |
||
204 | private $minify_excluded = true; |
||
205 | |||
206 | /** |
||
207 | * Reads the page and collects script tags. |
||
208 | * |
||
209 | * @param array $options all options. |
||
210 | */ |
||
211 | public function read( $options ) |
||
212 | { |
||
213 | $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content ); |
||
214 | if ( $noptimize_js ) { |
||
215 | return false; |
||
216 | } |
||
217 | |||
218 | // only optimize known good JS? |
||
219 | $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content ); |
||
220 | $allowlist_js = apply_filters( 'autoptimize_filter_js_whitelist', $allowlist_js, $this->content ); // fixme: to be removed in next version. |
||
221 | View Code Duplication | if ( ! empty( $allowlist_js ) ) { |
|
0 ignored issues
–
show
|
|||
222 | $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) ); |
||
0 ignored issues
–
show
It seems like
array_filter(array_map('...e(',', $allowlist_js))) of type array is incompatible with the declared type string of property $allowlist .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
223 | } |
||
224 | |||
225 | // is there JS we should simply remove? |
||
226 | $removable_js = apply_filters( 'autoptimize_filter_js_removables', '', $this->content ); |
||
227 | View Code Duplication | if ( ! empty( $removable_js ) ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
228 | $this->jsremovables = array_filter( array_map( 'trim', explode( ',', $removable_js ) ) ); |
||
229 | } |
||
230 | |||
231 | // only header? |
||
232 | View Code Duplication | if ( apply_filters( 'autoptimize_filter_js_justhead', $options['justhead'] ) ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
233 | $content = explode( '</head>', $this->content, 2 ); |
||
234 | $this->content = $content[0] . '</head>'; |
||
235 | $this->restofcontent = $content[1]; |
||
236 | } |
||
237 | |||
238 | // Determine whether we're doing JS-files aggregation or not. |
||
239 | if ( ! $options['aggregate'] ) { |
||
240 | $this->aggregate = false; |
||
241 | } |
||
242 | // Returning true for "dontaggregate" turns off aggregation. |
||
243 | if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) { |
||
244 | $this->aggregate = false; |
||
245 | } |
||
246 | |||
247 | // Defer when not aggregating. |
||
248 | if ( false === $this->aggregate && apply_filters( 'autoptimize_js_filter_defer_not_aggregate', $options['defer_not_aggregate'] ) ) { |
||
249 | $this->defer_not_aggregate = true; |
||
250 | } |
||
251 | |||
252 | // include inline? |
||
253 | if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) { |
||
254 | $this->include_inline = true; |
||
255 | } |
||
256 | |||
257 | // filter to "late inject minified JS", default to true for now (it is faster). |
||
258 | $this->inject_min_late = apply_filters( 'autoptimize_filter_js_inject_min_late', true ); |
||
259 | |||
260 | // filters to override hardcoded do(nt)move(last) array contents (array in, array out!). |
||
261 | $this->dontmove = apply_filters( 'autoptimize_filter_js_dontmove', $this->dontmove ); |
||
262 | $this->domovelast = apply_filters( 'autoptimize_filter_js_movelast', $this->domovelast ); |
||
263 | $this->domove = apply_filters( 'autoptimize_filter_js_domove', $this->domove ); |
||
264 | |||
265 | // Determine whether excluded files should be minified if not yet so. |
||
266 | if ( ! $options['minify_excluded'] && $options['aggregate'] ) { |
||
267 | $this->minify_excluded = false; |
||
268 | } |
||
269 | $this->minify_excluded = apply_filters( 'autoptimize_filter_js_minify_excluded', $this->minify_excluded, '' ); |
||
270 | |||
271 | // get extra exclusions settings or filter. |
||
272 | $exclude_js = $options['js_exclude']; |
||
273 | $exclude_js = apply_filters( 'autoptimize_filter_js_exclude', $exclude_js, $this->content ); |
||
274 | |||
275 | if ( '' !== $exclude_js ) { |
||
276 | if ( is_array( $exclude_js ) ) { |
||
277 | $remove_keys = array_keys( $exclude_js, 'remove' ); |
||
278 | if ( false !== $remove_keys ) { |
||
279 | foreach ( $remove_keys as $remove_key ) { |
||
280 | unset( $exclude_js[ $remove_key ] ); |
||
281 | $this->jsremovables[] = $remove_key; |
||
282 | } |
||
283 | } |
||
284 | $excl_js_arr = array_keys( $exclude_js ); |
||
285 | } else { |
||
286 | $excl_js_arr = array_filter( array_map( 'trim', explode( ',', $exclude_js ) ) ); |
||
287 | } |
||
288 | $this->dontmove = array_merge( $excl_js_arr, $this->dontmove ); |
||
289 | } |
||
290 | |||
291 | // Should we add try-catch? |
||
292 | if ( $options['trycatch'] ) { |
||
293 | $this->trycatch = true; |
||
294 | } |
||
295 | |||
296 | // force js in head? |
||
297 | if ( $options['forcehead'] ) { |
||
298 | $this->forcehead = true; |
||
299 | } else { |
||
300 | $this->forcehead = false; |
||
301 | } |
||
302 | |||
303 | $this->forcehead = apply_filters( 'autoptimize_filter_js_forcehead', $this->forcehead ); |
||
304 | |||
305 | // get cdn url. |
||
306 | $this->cdn_url = $options['cdn_url']; |
||
307 | |||
308 | // noptimize me. |
||
309 | $this->content = $this->hide_noptimize( $this->content ); |
||
310 | |||
311 | // Save IE hacks. |
||
312 | $this->content = $this->hide_iehacks( $this->content ); |
||
313 | |||
314 | // comments. |
||
315 | $this->content = $this->hide_comments( $this->content ); |
||
316 | |||
317 | // Get script files. |
||
318 | if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) { |
||
319 | foreach ( $matches[0] as $tag ) { |
||
320 | // only consider script aggregation for types allowlisted in should_aggregate-function. |
||
321 | $should_aggregate = $this->should_aggregate( $tag ); |
||
322 | if ( ! $should_aggregate ) { |
||
323 | $tag = ''; |
||
324 | continue; |
||
325 | } |
||
326 | |||
327 | if ( preg_match( '#<script[^>]*src=("|\')([^>]*)("|\')#Usmi', $tag, $source ) ) { |
||
328 | // non-inline script. |
||
329 | View Code Duplication | if ( $this->isremovable( $tag, $this->jsremovables ) ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
330 | $this->content = str_replace( $tag, '', $this->content ); |
||
331 | continue; |
||
332 | } |
||
333 | |||
334 | $orig_tag = null; |
||
0 ignored issues
–
show
$orig_tag is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
335 | $url = current( explode( '?', $source[2], 2 ) ); |
||
336 | $path = $this->getpath( $url ); |
||
337 | if ( false !== $path && preg_match( '#\.js$#', $path ) && $this->ismergeable( $tag ) ) { |
||
338 | // ok to optimize, add to array. |
||
339 | $this->scripts[] = $path; |
||
340 | } else { |
||
341 | $orig_tag = $tag; |
||
342 | $new_tag = $tag; |
||
343 | |||
344 | // non-mergeable script (excluded or dynamic or external). |
||
345 | if ( is_array( $exclude_js ) ) { |
||
346 | // should we add flags? |
||
347 | foreach ( $exclude_js as $excl_tag => $excl_flags ) { |
||
348 | if ( false !== strpos( $orig_tag, $excl_tag ) && in_array( $excl_flags, array( 'async', 'defer' ) ) ) { |
||
349 | $new_tag = str_replace( '<script ', '<script ' . $excl_flags . ' ', $new_tag ); |
||
350 | } |
||
351 | } |
||
352 | } |
||
353 | |||
354 | // not aggregating but deferring? |
||
355 | if ( $this->defer_not_aggregate && false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path && strpos( $new_tag, ' defer' ) === false ) { |
||
356 | $new_tag = str_replace( '<script ', '<script defer ', $new_tag ); |
||
357 | // and remove async as async+defer=async while we explicitly want defer. |
||
358 | if ( strpos( $new_tag, ' async' ) !== false && apply_filters( 'autoptimize_filter_js_defer_remove_async', true ) ) { |
||
359 | $new_tag = str_replace( array( ' async', ' async="async"', " async='async'" ), '', $new_tag ); |
||
360 | } |
||
361 | } |
||
362 | |||
363 | // Should we minify the non-aggregated script? |
||
364 | // -> if aggregate is on and exclude minify is on |
||
365 | // -> if aggregate is off and the file is not in dontmove. |
||
366 | View Code Duplication | if ( $path && $this->minify_excluded ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() The expression
$path of type false|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
367 | $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false ); |
||
368 | if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) { |
||
369 | $minified_url = $this->minify_single( $path ); |
||
370 | if ( ! empty( $minified_url ) ) { |
||
371 | // Replace original URL with minified URL from cache. |
||
372 | $new_tag = str_replace( $url, $minified_url, $new_tag ); |
||
373 | } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) { |
||
374 | // Remove the original script tag, because cache content is empty but only if filter |
||
375 | // is trued because $minified_url is also false if original JS is minified already. |
||
376 | $new_tag = ''; |
||
377 | } |
||
378 | } |
||
379 | } |
||
380 | |||
381 | if ( $this->ismovable( $new_tag ) ) { |
||
382 | // can be moved, flags and all. |
||
383 | if ( $this->movetolast( $new_tag ) ) { |
||
384 | $this->move['last'][] = $new_tag; |
||
385 | } else { |
||
386 | $this->move['first'][] = $new_tag; |
||
387 | } |
||
388 | View Code Duplication | } else { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
389 | // cannot be moved, so if flag was added re-inject altered tag immediately. |
||
390 | if ( ( '' !== $new_tag && $orig_tag !== $new_tag ) || ( '' === $new_tag && apply_filters( 'autoptimize_filter_js_remove_empty_files', false ) ) ) { |
||
391 | $this->content = str_replace( $orig_tag, $new_tag, $this->content ); |
||
392 | $orig_tag = ''; |
||
0 ignored issues
–
show
$orig_tag is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
393 | } |
||
394 | // and forget about the $tag (not to be touched any more). |
||
395 | $tag = ''; |
||
396 | } |
||
397 | } |
||
398 | } else { |
||
399 | // Inline script. |
||
400 | View Code Duplication | if ( $this->isremovable( $tag, $this->jsremovables ) ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
401 | $this->content = str_replace( $tag, '', $this->content ); |
||
402 | continue; |
||
403 | } |
||
404 | |||
405 | // unhide comments, as javascript may be wrapped in comment-tags for old times' sake. |
||
406 | $tag = $this->restore_comments( $tag ); |
||
407 | if ( $this->ismergeable( $tag ) && $this->include_inline ) { |
||
408 | preg_match( '#<script.*>(.*)</script>#Usmi', $tag, $code ); |
||
409 | $code = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] ); |
||
410 | $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code ); |
||
411 | $this->scripts[] = 'INLINE;' . $code; |
||
412 | } else { |
||
413 | // Can we move this? |
||
414 | $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag ); |
||
415 | if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) { |
||
416 | if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) { |
||
417 | $this->move['last'][] = $tag; |
||
418 | } else { |
||
419 | $this->move['first'][] = $tag; |
||
420 | } |
||
421 | } else { |
||
422 | // We shouldn't touch this. |
||
423 | $tag = ''; |
||
424 | } |
||
425 | } |
||
426 | // Re-hide comments to be able to do the removal based on tag from $this->content. |
||
427 | $tag = $this->hide_comments( $tag ); |
||
428 | } |
||
429 | |||
430 | // Remove the original script tag. |
||
431 | $this->content = str_replace( $tag, '', $this->content ); |
||
432 | } |
||
433 | |||
434 | return true; |
||
435 | } |
||
436 | |||
437 | // No script files, great ;-) . |
||
438 | return false; |
||
439 | } |
||
440 | |||
441 | /** |
||
442 | * Determines wheter a certain `<script>` $tag should be aggregated or not. |
||
443 | * |
||
444 | * We consider these as "aggregation-safe" currently: |
||
445 | * - script tags without a `type` attribute |
||
446 | * - script tags with these `type` attribute values: `text/javascript`, `text/ecmascript`, `application/javascript`, |
||
447 | * and `application/ecmascript` |
||
448 | * |
||
449 | * Everything else should return false. |
||
450 | * |
||
451 | * @link https://developer.mozilla.org/en/docs/Web/HTML/Element/script#attr-type |
||
452 | * |
||
453 | * @param string $tag Script node & child(ren). |
||
454 | * @return bool |
||
455 | */ |
||
456 | public function should_aggregate( $tag ) |
||
457 | { |
||
458 | if ( empty( $tag ) ) { |
||
459 | return false; |
||
460 | } |
||
461 | |||
462 | // We're only interested in the type attribute of the <script> tag itself, not any possible |
||
463 | // inline code that might just contain the 'type=' string... |
||
464 | $tag_parts = array(); |
||
465 | preg_match( '#<(script[^>]*)>#i', $tag, $tag_parts ); |
||
466 | $tag_without_contents = null; |
||
467 | if ( ! empty( $tag_parts[1] ) ) { |
||
468 | $tag_without_contents = $tag_parts[1]; |
||
469 | } |
||
470 | |||
471 | $has_type = ( strpos( $tag_without_contents, 'type' ) !== false ); |
||
472 | |||
473 | $type_valid = false; |
||
474 | if ( $has_type ) { |
||
475 | $type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents ); |
||
476 | } |
||
477 | |||
478 | $should_aggregate = false; |
||
479 | if ( ! $has_type || $type_valid ) { |
||
480 | $should_aggregate = true; |
||
481 | } |
||
482 | |||
483 | return $should_aggregate; |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * Joins and optimizes JS. |
||
488 | */ |
||
489 | public function minify() |
||
490 | { |
||
491 | foreach ( $this->scripts as $script ) { |
||
492 | if ( empty( $script ) ) { |
||
493 | continue; |
||
494 | } |
||
495 | |||
496 | // TODO/FIXME: some duplicate code here, can be reduced/simplified. |
||
497 | if ( preg_match( '#^INLINE;#', $script ) ) { |
||
498 | // Inline script. |
||
499 | $script = preg_replace( '#^INLINE;#', '', $script ); |
||
500 | $script = rtrim( $script, ";\n\t\r" ) . ';'; |
||
501 | // Add try-catch? |
||
502 | if ( $this->trycatch ) { |
||
503 | $script = 'try{' . $script . '}catch(e){}'; |
||
504 | } |
||
505 | $tmpscript = apply_filters( 'autoptimize_js_individual_script', $script, '' ); |
||
506 | if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscript ) ) { |
||
507 | $script = $tmpscript; |
||
508 | $this->alreadyminified = true; |
||
509 | } |
||
510 | $this->jscode .= "\n" . $script; |
||
511 | } else { |
||
512 | // External script. |
||
513 | if ( false !== $script && file_exists( $script ) && is_readable( $script ) ) { |
||
514 | $scriptsrc = file_get_contents( $script ); |
||
515 | $scriptsrc = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $scriptsrc ); |
||
516 | $scriptsrc = rtrim( $scriptsrc, ";\n\t\r" ) . ';'; |
||
517 | // Add try-catch? |
||
518 | if ( $this->trycatch ) { |
||
519 | $scriptsrc = 'try{' . $scriptsrc . '}catch(e){}'; |
||
520 | } |
||
521 | $tmpscriptsrc = apply_filters( 'autoptimize_js_individual_script', $scriptsrc, $script ); |
||
522 | View Code Duplication | if ( has_filter( 'autoptimize_js_individual_script' ) && ! empty( $tmpscriptsrc ) ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
523 | $scriptsrc = $tmpscriptsrc; |
||
524 | $this->alreadyminified = true; |
||
525 | } elseif ( $this->can_inject_late( $script ) ) { |
||
526 | $scriptsrc = self::build_injectlater_marker( $script, md5( $scriptsrc ) ); |
||
527 | } |
||
528 | $this->jscode .= "\n" . $scriptsrc; |
||
529 | } |
||
530 | } |
||
531 | } |
||
532 | |||
533 | // Check for already-minified code. |
||
534 | $this->md5hash = md5( $this->jscode ); |
||
535 | $ccheck = new autoptimizeCache( $this->md5hash, 'js' ); |
||
536 | if ( $ccheck->check() ) { |
||
537 | $this->jscode = $ccheck->retrieve(); |
||
0 ignored issues
–
show
It seems like
$ccheck->retrieve() can also be of type false . However, the property $jscode is declared as type string . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
538 | return true; |
||
539 | } |
||
540 | unset( $ccheck ); |
||
541 | |||
542 | // $this->jscode has all the uncompressed code now. |
||
543 | if ( true !== $this->alreadyminified ) { |
||
544 | if ( apply_filters( 'autoptimize_js_do_minify', true ) ) { |
||
545 | $tmp_jscode = trim( JSMin::minify( $this->jscode ) ); |
||
546 | if ( ! empty( $tmp_jscode ) ) { |
||
547 | $this->jscode = $tmp_jscode; |
||
548 | unset( $tmp_jscode ); |
||
549 | } |
||
550 | $this->jscode = $this->inject_minified( $this->jscode ); |
||
551 | $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode ); |
||
552 | return true; |
||
553 | } else { |
||
554 | $this->jscode = $this->inject_minified( $this->jscode ); |
||
555 | return false; |
||
556 | } |
||
557 | } |
||
558 | |||
559 | $this->jscode = apply_filters( 'autoptimize_js_after_minify', $this->jscode ); |
||
560 | return true; |
||
561 | } |
||
562 | |||
563 | /** |
||
564 | * Caches the JS in uncompressed, deflated and gzipped form. |
||
565 | */ |
||
566 | public function cache() |
||
567 | { |
||
568 | $cache = new autoptimizeCache( $this->md5hash, 'js' ); |
||
569 | if ( ! $cache->check() ) { |
||
570 | // Cache our code. |
||
571 | $cache->cache( $this->jscode, 'text/javascript' ); |
||
572 | } |
||
573 | $this->url = AUTOPTIMIZE_CACHE_URL . $cache->getname(); |
||
574 | $this->url = $this->url_replace_cdn( $this->url ); |
||
575 | } |
||
576 | |||
577 | /** |
||
578 | * Returns the content. |
||
579 | */ |
||
580 | public function getcontent() |
||
581 | { |
||
582 | // Restore the full content. |
||
583 | if ( ! empty( $this->restofcontent ) ) { |
||
584 | $this->content .= $this->restofcontent; |
||
585 | $this->restofcontent = ''; |
||
586 | } |
||
587 | |||
588 | // Add the scripts taking forcehead/ deferred (default) into account. |
||
589 | if ( $this->forcehead ) { |
||
590 | $replace_tag = array( '</head>', 'before' ); |
||
591 | $defer = ''; |
||
592 | } else { |
||
593 | $replace_tag = array( '</body>', 'before' ); |
||
594 | $defer = 'defer '; |
||
595 | } |
||
596 | |||
597 | $defer = apply_filters( 'autoptimize_filter_js_defer', $defer ); |
||
598 | $type_js = ''; |
||
599 | if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) { |
||
600 | $type_js = 'type="text/javascript" '; |
||
601 | } |
||
602 | |||
603 | $bodyreplacementpayload = '<script ' . $type_js . $defer . 'src="' . $this->url . '"></script>'; |
||
604 | $bodyreplacementpayload = apply_filters( 'autoptimize_filter_js_bodyreplacementpayload', $bodyreplacementpayload ); |
||
605 | |||
606 | $bodyreplacement = implode( '', $this->move['first'] ); |
||
607 | $bodyreplacement .= $bodyreplacementpayload; |
||
608 | $bodyreplacement .= implode( '', $this->move['last'] ); |
||
609 | |||
610 | $replace_tag = apply_filters( 'autoptimize_filter_js_replacetag', $replace_tag ); |
||
611 | |||
612 | if ( strlen( $this->jscode ) > 0 ) { |
||
613 | $this->inject_in_html( $bodyreplacement, $replace_tag ); |
||
614 | } |
||
615 | |||
616 | // Restore comments. |
||
617 | $this->content = $this->restore_comments( $this->content ); |
||
618 | |||
619 | // Restore IE hacks. |
||
620 | $this->content = $this->restore_iehacks( $this->content ); |
||
621 | |||
622 | // Restore noptimize. |
||
623 | $this->content = $this->restore_noptimize( $this->content ); |
||
624 | |||
625 | // Return the modified HTML. |
||
626 | return $this->content; |
||
627 | } |
||
628 | |||
629 | /** |
||
630 | * Checks against the allow- and blocklists. |
||
631 | * |
||
632 | * @param string $tag JS tag. |
||
633 | */ |
||
634 | private function ismergeable( $tag ) |
||
635 | { |
||
636 | if ( empty( $tag ) || ! $this->aggregate ) { |
||
637 | return false; |
||
638 | } |
||
639 | |||
640 | if ( ! empty( $this->allowlist ) ) { |
||
641 | foreach ( $this->allowlist as $match ) { |
||
0 ignored issues
–
show
|
|||
642 | if ( false !== strpos( $tag, $match ) ) { |
||
643 | return true; |
||
644 | } |
||
645 | } |
||
646 | // No match with allowlist. |
||
647 | return false; |
||
648 | } else { |
||
649 | foreach ( $this->domove as $match ) { |
||
650 | if ( false !== strpos( $tag, $match ) ) { |
||
651 | // Matched something. |
||
652 | return false; |
||
653 | } |
||
654 | } |
||
655 | |||
656 | if ( $this->movetolast( $tag ) ) { |
||
657 | return false; |
||
658 | } |
||
659 | |||
660 | foreach ( $this->dontmove as $match ) { |
||
661 | if ( false !== strpos( $tag, $match ) ) { |
||
662 | // Matched something. |
||
663 | return false; |
||
664 | } |
||
665 | } |
||
666 | |||
667 | // If we're here it's safe to merge. |
||
668 | return true; |
||
669 | } |
||
670 | } |
||
671 | |||
672 | /** |
||
673 | * Checks agains the blocklist. |
||
674 | * |
||
675 | * @param string $tag tag to check for blocklist (exclusions). |
||
676 | */ |
||
677 | private function ismovable( $tag ) |
||
678 | { |
||
679 | if ( empty( $tag ) || true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) { |
||
680 | return false; |
||
681 | } |
||
682 | |||
683 | foreach ( $this->domove as $match ) { |
||
684 | if ( false !== strpos( $tag, $match ) ) { |
||
685 | // Matched something. |
||
686 | return true; |
||
687 | } |
||
688 | } |
||
689 | |||
690 | if ( $this->movetolast( $tag ) ) { |
||
691 | return true; |
||
692 | } |
||
693 | |||
694 | foreach ( $this->dontmove as $match ) { |
||
695 | if ( false !== strpos( $tag, $match ) ) { |
||
696 | // Matched something. |
||
697 | return false; |
||
698 | } |
||
699 | } |
||
700 | |||
701 | // If we're here it's safe to move. |
||
702 | return true; |
||
703 | } |
||
704 | |||
705 | private function movetolast( $tag ) |
||
706 | { |
||
707 | if ( empty( $tag ) ) { |
||
708 | return false; |
||
709 | } |
||
710 | |||
711 | foreach ( $this->domovelast as $match ) { |
||
712 | if ( false !== strpos( $tag, $match ) ) { |
||
713 | // Matched, return true. |
||
714 | return true; |
||
715 | } |
||
716 | } |
||
717 | |||
718 | // Should be in 'first'. |
||
719 | return false; |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on: |
||
724 | * - inject_min_late being active |
||
725 | * - filename ending in `min.js` |
||
726 | * - filename matching `js/jquery/jquery.js` (WordPress core jquery, is minified) |
||
727 | * - filename matching one passed in the consider minified filter |
||
728 | * |
||
729 | * @param string $js_path Path to JS file. |
||
730 | * @return bool |
||
731 | */ |
||
732 | private function can_inject_late( $js_path ) { |
||
733 | $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false ); |
||
734 | if ( true !== $this->inject_min_late ) { |
||
735 | // late-inject turned off. |
||
736 | return false; |
||
737 | } elseif ( ( false === strpos( $js_path, 'min.js' ) ) && ( false === strpos( $js_path, 'wp-includes/js/jquery/jquery.js' ) ) && ( str_replace( $consider_minified_array, '', $js_path ) === $js_path ) ) { |
||
738 | // file not minified based on filename & filter. |
||
739 | return false; |
||
740 | } else { |
||
741 | // phew, all is safe, we can late-inject. |
||
742 | return true; |
||
743 | } |
||
744 | } |
||
745 | |||
746 | /** |
||
747 | * Returns whether we're doing aggregation or not. |
||
748 | * |
||
749 | * @return bool |
||
750 | */ |
||
751 | public function aggregating() |
||
752 | { |
||
753 | return $this->aggregate; |
||
754 | } |
||
755 | |||
756 | /** |
||
757 | * Minifies a single local js file and returns its (cached) url. |
||
758 | * |
||
759 | * @param string $filepath Filepath. |
||
760 | * @param bool $cache_miss Optional. Force a cache miss. Default false. |
||
761 | * |
||
762 | * @return bool|string Url pointing to the minified js file or false. |
||
763 | */ |
||
764 | public function minify_single( $filepath, $cache_miss = false ) |
||
765 | { |
||
766 | $contents = $this->prepare_minify_single( $filepath ); |
||
767 | |||
768 | if ( empty( $contents ) ) { |
||
769 | return false; |
||
770 | } |
||
771 | |||
772 | // Check cache. |
||
773 | $hash = 'single_' . md5( $contents ); |
||
774 | $cache = new autoptimizeCache( $hash, 'js' ); |
||
775 | |||
776 | // If not in cache already, minify... |
||
777 | if ( ! $cache->check() || $cache_miss ) { |
||
778 | $contents = trim( JSMin::minify( $contents ) ); |
||
779 | |||
780 | // Check if minified cache content is empty. |
||
781 | if ( empty( $contents ) ) { |
||
782 | return false; |
||
783 | } |
||
784 | |||
785 | // Filter contents of excluded minified CSS. |
||
786 | $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents ); |
||
787 | |||
788 | // Store in cache. |
||
789 | $cache->cache( $contents, 'text/javascript' ); |
||
790 | } |
||
791 | |||
792 | $url = $this->build_minify_single_url( $cache ); |
||
793 | |||
794 | return $url; |
||
795 | } |
||
796 | } |
||
797 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.