PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.3
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 / minify / class-wp-optimize-minify-front-end.php

class-wp-optimize-minify-front-end.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.3, at minify/class-wp-optimize-minify-front-end.php

2,205 lines 75.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('No direct access allowed');
4
5 class WP_Optimize_Minify_Front_End {
6
7 private $collect_preload_js = array();
8
9 private $collect_preload_css = array();
10
11 private $collect_google_fonts = array();
12
13 private $minify_cache_incremented = false;
14
15 private $options = array();
16
17 /**
18 * Initialize actions and filters
19 *
20 * @return void
21 */
22 public function __construct() {
23 $this->include_dependencies();
24 $this->options = wp_optimize_minify_config()->get();
25
26 // Main process
27 add_action('wp', array($this, 'init'));
28
29 // Beaver builder
30 add_action('fl_builder_after_save_layout', array('WP_Optimize_Minify_Cache_Functions', 'reset'));
31
32 // extra_preload_headers is currently not available to users
33 // add_action('send_headers', array($this, 'extra_preload_headers'));
34 }
35
36 /**
37 * Fired on wp init
38 *
39 * @return void
40 */
41 public function init() {
42 /**
43 * Check whether Minify is run on the current page
44 */
45 if (!$this->run_on_page()) return;
46
47 if ($this->options['emoji_removal']) {
48 $this->disable_emojis();
49 }
50
51 if ($this->options['clean_header_one']) {
52 $this->remove_header_meta_info();
53 }
54
55 // Headers & Preload JS/CSS/Extra
56 if ($this->options['enabled_css_preload'] || $this->options['enabled_js_preload']) {
57 add_action('wp_footer', array($this, 'generate_preload_headers'), PHP_INT_MAX);
58 }
59
60 if ($this->options['enable_js']) {
61 $this->process_js();
62 }
63
64 if ($this->options['enable_css']) {
65 $this->process_css();
66 }
67
68 // Preload tags
69 if (trim($this->options['hpreload'])) {
70 add_action('wp_head', array($this, 'add_assets_preload'), 2);
71 }
72
73 if ($this->should_process_html()) {
74 add_action('template_redirect', array('WP_Optimize_Minify_Functions', 'html_compression_start'), PHP_INT_MAX);
75 }
76
77 if ($this->should_use_loadCSS()) {
78 add_action('wp_footer', array('WP_Optimize_Minify_Print', 'add_load_css'), PHP_INT_MAX);
79 }
80
81 $this->remove_query_string_from_static_assets();
82 }
83
84 /**
85 * Detects whether cache preloading is running or not
86 *
87 * @return bool
88 */
89 private function is_cache_preload() {
90 return isset($_SERVER['HTTP_X_WP_OPTIMIZE_CACHE_PRELOAD']) && 0 === strcmp($_SERVER['HTTP_X_WP_OPTIMIZE_CACHE_PRELOAD'], 'Yes');
91 }
92
93 /**
94 * Wether to run the feature on a page or not
95 *
96 * @param string $context - Optional, The context where the check is done
97 * @return boolean
98 */
99 public function run_on_page($context = 'default') {
100 /**
101 * Filters wether the functionality is ran on the current page.
102 *
103 * @param boolean $run_on_page
104 * @param string $context - Optional, The feature where the check is done
105 */
106 return apply_filters(
107 'wpo_minify_run_on_page',
108 !is_admin()
109 && (!defined('SCRIPT_DEBUG') || !SCRIPT_DEBUG)
110 && !is_preview()
111 && (!function_exists('is_customize_preview') || !is_customize_preview())
112 && !($this->options['disable_when_logged_in'] && is_user_logged_in())
113 && !(function_exists('is_amp_endpoint') && is_amp_endpoint())
114 && !WP_Optimize_Minify_Functions::exclude_contents(),
115 $context
116 );
117 }
118
119 /**
120 * Inline css in place, instead of inlining the large file
121 *
122 * @param String $html
123 * @param String $handle
124 * @param String $href
125 * @param String $media
126 *
127 * @return String
128 */
129 public function inline_css($html, $handle, $href, $media) {
130 $exclude_css = array_map('trim', explode("\n", trim($this->options['exclude_css'])));
131 $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css);
132 $blacklist = WP_Optimize_Minify_Functions::get_ie_blacklist();
133 $async_css = array_map('trim', explode("\n", trim($this->options['async_css'])));
134 $master_ignore = array_merge($ignore_list, $blacklist);
135
136 // make sure href is complete
137 $href = WP_Optimize_Minify_Functions::get_hurl($href);
138
139 if ($this->options['debug']) {
140 echo "<!-- wpo_min DEBUG: Inline CSS processing start $handle / $href -->\n";
141 }
142
143
144 // skip all this, if the async css option is enabled
145 if ($this->options['loadcss']) return $html;
146 // remove all css?
147 if ($this->options['remove_css']) return false;
148 // leave conditionals alone
149 if (wp_styles()->get_data($handle, 'conditional')) return $html;
150
151 // mediatype fix for some plugins + remove print mediatypes
152 if ('screen' == $media
153 || 'screen, print' == $media
154 || empty($media)
155 || is_null($media)
156 || false == $media
157 ) {
158 $media = 'all';
159 }
160 if (!empty($this->options['remove_print_mediatypes']) && 'print' == $media) {
161 return false;
162 }
163
164 // Exclude specific CSS files from PageSpeedInsights?
165 if (WP_Optimize_Minify_Functions::in_arrayi($href, $async_css)) {
166 WP_Optimize_Minify_Print::exclude_style($href);
167 return false;
168 }
169
170 // remove wpo_min from the ignore list
171 $ignore_list = array_filter($ignore_list, array($this, 'check_wpo'));
172
173 // return if in any ignore or black list
174 if (count($master_ignore) > 0 && WP_Optimize_Minify_Functions::in_arrayi($href, $master_ignore)) {
175 return $html;
176 }
177
178 // check if working with a font awesom link
179 if (WP_Optimize_Minify_Functions::is_font_awesome($href)) {
180 // font awesome processing, async css
181 if ('async' == $this->options['fawesome_method']) {
182 WP_Optimize_Minify_Print::async_style($href, $media);
183 return false;
184 } elseif ('exclude' === $this->options['fawesome_method']) {
185 // font awesome processing, async and exclude from PageSpeedIndex
186 WP_Optimize_Minify_Print::exclude_style($href);
187 return false;
188 } elseif ('inline' == $this->options['fawesome_method']) {
189 WP_Optimize_Minify_Print::inline_style($handle, $href);
190 return false;
191 }
192 }
193
194 // Check if working with google font url
195
196 if ('fonts.googleapis.com' == parse_url($href, PHP_URL_HOST)) {
197 // check if google fonts should be removed
198 if ($this->options['remove_googlefonts']) return false;
199 // check if google fonts should be merged
200 if ($this->options['merge_google_fonts']) {
201 if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) {
202 $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href);
203 $this->collect_google_fonts[$handle] = $href;
204 } else {
205 $this->collect_google_fonts[$handle] = $href;
206 }
207 return false;
208 } else {
209 if ('inline' === $this->options['gfonts_method']) {
210 if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) {
211 $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href);
212 }
213 // download, minify, cache
214 $tkey = 'css-'.hash('adler32', $handle.$href).'.css';
215 $json = false;
216 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
217 if (false === $json) {
218 $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $this->options['enable_css_minification'], 'css', $handle);
219 if ($this->options['debug']) {
220 echo "<!-- wpo_min DEBUG: Uncached file processing now for $handle / $href -->\n";
221 }
222 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json);
223 }
224
225 // decode
226 $res = json_decode($json, true);
227
228 // add font-display
229 // https://developers.google.com/web/updates/2016/02/font-display
230 $res['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $res['code']);
231
232 // inline css or fail
233 if (false != $res['status']) {
234 echo '<style class="optimize_css_1" type="text/css" media="all">'.$res['code'].'</style>' . "\n";
235 return false;
236 } else {
237 if ($this->options['debug']) {
238 echo "<!-- wpo_min DEBUG: Google fonts request failed for $href -->\n";
239 }
240 return $html;
241 }
242 } elseif ('async' === $this->options['gfonts_method']) {
243 WP_Optimize_Minify_Print::async_style($href);
244 return false;
245 } elseif ('exclude' === $this->options['gfonts_method']) {
246 WP_Optimize_Minify_Print::exclude_style($href);
247 return false;
248 }
249 }
250 }
251
252 // skip external scripts that are not specifically allowed
253 if (false === WP_Optimize_Minify_Functions::internal_url($href, site_url()) || empty($href)) {
254 if ($this->options['debug']) {
255 echo "<!-- wpo_min DEBUG: Skipped the next external enqueued CSS -->\n";
256 }
257 return $html;
258 }
259
260 $file_size = WP_Optimize_Minify_Functions::get_file_size($href);
261
262 // If we can't determine file size, then we still need to proceed with normal minify process
263 if (apply_filters('wp_optimize_skip_inlining', false === $file_size || $file_size > 20480, $file_size, $href)) return $html;
264
265 // download, minify, cache
266 $tkey = 'css-'.hash('adler32', $handle.$href).'.css';
267 $json = false;
268 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
269 if (false === $json) {
270 $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $this->options['enable_css_minification'], 'css', $handle);
271 if ($this->options['debug']) {
272 echo "<!-- wpo_min DEBUG: Uncached file processing now for $handle / $href -->" . "\n";
273 }
274 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json);
275 }
276
277 // decode
278 $res = json_decode($json, true);
279
280 // inline it + other inlined children styles
281 if (false != $res['status']) {
282 echo '<style class="optimize_css_2" type="text/css" media="'.$media.'">'.$res['code'].'</style>' . "\n";
283
284 // get inline_styles for this handle, minify and print
285 $inline_styles = array();
286 $inline_styles = wp_styles()->get_data($handle, 'after');
287 if (false != $inline_styles) {
288
289 // string type
290 if (is_string($inline_styles)) {
291 $code = WP_Optimize_Minify_Functions::get_css($href, $inline_styles, $this->options['enable_css_minification']);
292 if (!empty($code) && false != $code) {
293 echo '<style class="optimize_css_3" type="text/css" media="'.$media.'">'.$code.'</style>' . "\n";
294 }
295 }
296
297 // array type
298 if (is_array($inline_styles)) {
299 foreach ($inline_styles as $st) {
300 $code = WP_Optimize_Minify_Functions::get_css($href, $st, $this->options['enable_css_minification']);
301 if (!empty($code) && false != $code) {
302 echo '<style class="optimize_css_4" type="text/css" media="'.$media.'">'.$code.'</style>' . "\n";
303 }
304 }
305 }
306 }
307
308 // prevent default
309 return false;
310 } else {
311 if ($this->options['debug']) {
312 echo "<!-- wpo_min DEBUG: $handle / $href returned an empty from minification -->" . "\n";
313 }
314 return $html;
315 }
316 echo "<!-- ERROR: WPO-Minify couldn't catch the CSS file below. Please report this on https://wordpress.org/support/plugin/wp-optimize/ -->\n";
317 return $html;
318 }
319
320 /**
321 * Enable defer for JavaScript (WP 4.1 and above) and remove query strings for ignored files
322 *
323 * @param String $tag
324 * @param String $handle
325 * @param String $src
326 *
327 * @return String
328 */
329 public function defer_js($tag, $handle, $src) {
330 $wp_domain = trim(str_ireplace(array('http://', 'https://'), '', trim(site_url(), '/')));
331 $exclude_js = array_map('trim', explode("\n", trim($this->options['exclude_js'])));
332 $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_js);
333 // Should this defer the Poly fills for IE?
334 $blacklist = WP_Optimize_Minify_Functions::get_ie_blacklist();
335 // no query strings
336 if (false !== stripos($src, '?ver')) {
337 $srcf = stristr($src, '?ver', true); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.stristr_before_needleFound
338 $tag = str_ireplace($src, $srcf, $tag);
339 $src = $srcf;
340 }
341
342 // return if defer option is set to individual
343 if ('individual' === $this->options['enable_defer_js']) {
344 return $tag;
345 }
346
347 // return the tag if it's a polyfill
348 if (count($blacklist) > 0 && WP_Optimize_Minify_Functions::in_arrayi($src, $blacklist)) {
349 return $tag;
350 }
351
352 // Skip deferring the jQuery library option, if the defer jQuery option is disabled
353 if (!$this->options['defer_jquery']
354 && (false !== stripos($tag, '/jquery.js')
355 || false !== stripos($tag, '/jquery.min.js')
356 || (false !== stripos($tag, '/jquery-') && false !== stripos($tag, '.js')))
357 ) {
358 return $tag;
359 }
360
361 // return if external script url https://www.chromestatus.com/feature/5718547946799104
362 if (WP_Optimize_Minify_Functions::is_local_domain($src) !== true) {
363 return $tag;
364 }
365
366 // bypass if already optimized
367 if (false !== stripos($tag, 'navigator.userAgent.match')) {
368 return $tag;
369 }
370
371 // should we exclude defer on the login page?
372 if ($this->options['exclude_defer_login']
373 && false !== stripos($_SERVER["SCRIPT_NAME"], strrchr(wp_login_url(), '/'))
374 ) {
375 return $tag;
376 }
377
378 // add defer attribute, but only if not having async or defer already
379 if (stripos($tag, 'defer') === false && stripos($tag, 'async') === false) {
380
381 // add cdn for PageSpeedIndex
382 if (!empty($this->options['cdn_url'])) {
383 $cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($this->options['cdn_url'], '/'))), '/');
384 $src = str_ireplace($wp_domain, $cdn_url, $src);
385 }
386 if ('async_using_js' === $this->options['defer_js_type']) {
387 return WP_Optimize_Minify_Print::async_script($src, false);
388 }
389
390 // remove wpo_min from the ignore list
391 $ignore_list = array_filter($ignore_list, array($this, 'check_wpo'));
392 if (count($ignore_list) > 0 && WP_Optimize_Minify_Functions::in_arrayi($src, $ignore_list)) {
393 return $tag;
394 } else {
395 /**
396 * Filters whether to use the defer attribute or async.
397 *
398 * @default string 'defer' (any other value will be set to async)
399 * @param string $value - 'defer' or 'async'
400 * @param string $tag - The <script> tag
401 * @param string $handle - The WP handle for the script
402 * @param string $src - The SRC for the script
403 * @return string - 'defer' or 'async'
404 */
405 $defer_or_async = apply_filters('wpo_minify_defer_or_async', 'defer', $tag, $handle, $src);
406 if ('defer' === $defer_or_async) {
407 return str_ireplace('<script ', '<script defer ', $tag);
408 } else {
409 return str_ireplace('<script ', '<script async ', $tag);
410 }
411 }
412 }
413
414 // fallback
415 return $tag;
416 }
417
418 /**
419 * Add inline CSS code / Critical Path
420 *
421 * @return void
422 */
423 public function add_critical_path() {
424 if (is_front_page() && !empty($this->options['critical_path_css_is_front_page'])) {
425 echo '<style id="critical-path-is-front-page" type="text/css" media="all">' . "\n" . $this->options['critical_path_css_is_front_page'] . "\n" . '</style>' . "\n";
426 } elseif (!empty($this->options['critical_path_css'])) {
427 echo '<style id="critical-path-global" type="text/css" media="all">' . "\n" . $this->options['critical_path_css'] . "\n" . '</style>' . "\n";
428 }
429 }
430
431 /**
432 * Add critical assets preload
433 *
434 * @return void
435 */
436 public function add_assets_preload() {
437 $preload = json_decode($this->options['hpreload']);
438 if (is_array($preload)) {
439 foreach ($preload as $asset) {
440 if (!empty($asset)) {
441 echo '<link rel="preload" href="'.esc_url($asset->href).'" as="'.esc_attr($asset->type).'"'.($asset->crossorigin ? ' crossorigin' : '').'>';
442 }
443 }
444 }
445 }
446
447 /**
448 * Process header CSS
449 *
450 * @return boolean
451 */
452 public function process_header_css() {
453 global $wp_styles;
454 if (!is_object($wp_styles)) return false;
455
456 $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path();
457 $cache_dir = $cache_path['cachedir'];
458 $cache_dir_url = $cache_path['cachedirurl'];
459 $exclude_css = array_map('trim', explode("\n", trim($this->options['exclude_css'])));
460 $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css);
461 $async_css = array_map('trim', explode("\n", trim($this->options['async_css'])));
462
463 $minify_css = $this->options['enable_css_minification'];
464 $merge_css = $this->options['enable_merging_of_css'];
465 $merge_inline_extra_css_js = $this->options['merge_inline_extra_css_js'];
466 $process_css = $minify_css || $merge_css;
467 $styles = clone $wp_styles;
468 $styles->all_deps($styles->queue);
469 $done = $styles->done;
470 $header = array();
471 $google_fonts = array();
472 $process = array();
473 $inline_css = array();
474 $log = '';
475
476 // dequeue all styles
477 if (isset($this->options['remove_css']) && $this->options['remove_css']) {
478 foreach ($styles->to_do as $handle) {
479 $done = array_merge($done, array($handle));
480 }
481 // remove from queue
482 $wp_styles->done = $done;
483 return false;
484 }
485
486 // get list of handles to process, dequeue duplicate css urls and keep empty source handles (for dependencies)
487 $uniq = array();
488 foreach ($styles->to_do as $handle) {
489
490 // conditionals
491 $conditional = null; if (isset($wp_styles->registered[$handle]->extra["conditional"])) {
492 $conditional = $wp_styles->registered[$handle]->extra["conditional"]; // such as ie7, ie8, ie9, etc
493 }
494
495 // mediatype
496 $mt = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all';
497 if ('screen' == $mt || 'screen, print' == $mt || empty($mt) || is_null($mt) || false == $mt) {
498 $mt = 'all';
499 }
500 $mediatype = $mt;
501
502 // full url or empty
503 $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src);
504 $version = $wp_styles->registered[$handle]->ver;
505
506 // inlined scripts without file
507 if (empty($href)) continue;
508
509
510 // mark duplicates as done and remove from the queue
511 if (!empty($href)) {
512 $key = hash('adler32', $href . $version);
513 if (isset($uniq[$key])) {
514 $done = array_merge($done, array($handle));
515 continue;
516 } else {
517 $uniq[$key] = $handle;
518 }
519 }
520 // Exclude specific CSS files from PageSpeedIndex?
521 if (WP_Optimize_Minify_Functions::in_arrayi($href, $async_css)) {
522 WP_Optimize_Minify_Print::exclude_style($href);
523 $done = array_merge($done, array($handle));
524 continue;
525 }
526 // Fonts Awesome Processing
527 if (WP_Optimize_Minify_Functions::is_font_awesome($href)) {
528 if ('inline' === $this->options['fawesome_method']) {
529 WP_Optimize_Minify_Print::inline_style($handle, $href);
530 $done = array_merge($done, array($handle));
531 continue;
532 } elseif ('async' === $this->options['fawesome_method']) {
533 WP_Optimize_Minify_Print::async_style($href, $mediatype);
534 $done = array_merge($done, array($handle));
535 continue;
536 } elseif ('exclude' === $this->options['fawesome_method']) {
537 WP_Optimize_Minify_Print::exclude_style($href);
538 $done = array_merge($done, array($handle));
539 continue;
540 }
541 }
542
543 // Exclude Print mediatype
544 if (!empty($this->options['remove_print_mediatypes']) && 'print' === $mediatype) {
545 $done = array_merge($done, array($handle));
546 continue;
547 }
548
549 // array of info to save
550 $arr = array(
551 'handle' => $handle,
552 'url' => $href,
553 'conditional' => $conditional,
554 'mediatype' => $mediatype
555 );
556
557 // google fonts to the top (collect and skip process array)
558 if (WP_Optimize_Minify_Functions::is_google_font($href)) {
559 if ($this->options['remove_googlefonts']) {
560 $done = array_merge($done, array($handle));
561 continue;
562 }
563 if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) {
564 $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href);
565 $google_fonts[$handle] = $href;
566 } else {
567 $google_fonts[$handle] = $href;
568 }
569 }
570 $process[$handle] = $arr;
571 }
572
573 // Process Google fonts
574 if (count($google_fonts) > 0) {
575 // merge google fonts if force inlining is enabled?
576 $nfonts = array();
577 if ($this->options['merge_google_fonts']) {
578 $nfonts[] = WP_Optimize_Minify_Fonts::concatenate_google_fonts($google_fonts);
579 } else {
580 foreach ($google_fonts as $h => $a) {
581 if (!empty($a)) {
582 $nfonts[$h] = $a;
583 }
584 }
585 }
586
587 // foreach google font (will be one if merged is not disabled)
588 foreach ($nfonts as $handle => $href) {
589 if ('inline' === $this->options['gfonts_method']) {
590 // download, minify, cache
591 $tkey = 'css-'.hash('adler32', $href).'.css';
592 // this returns false if the cache is empty! but it doesn't check for failed
593 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
594 $json = json_decode($json, true);
595 // check if the cache is empty or if the cache has code
596 if (false === $json || empty($json['code'])) {
597 $res = WP_Optimize_Minify_Functions::download_and_minify($href, null, $minify_css, 'css', null);
598 if ($this->options['debug']) {
599 echo "<!-- wpo_min DEBUG: Uncached file processing now for $href -->\n";
600 }
601 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $res);
602 // decode
603 $json = json_decode($res, true);
604 }
605
606 // inline css or fail
607 if (!empty($json['code'])) {
608 // add font-display
609 // https://developers.google.com/web/updates/2016/02/font-display
610 $json['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $json['code']);
611 echo '<style type="text/css" media="all">'.$json['code'].'</style>' . "\n";
612 $done = array_merge($done, array($handle));
613 } else {
614 echo "<!-- GOOGLE FONTS REQUEST FAILED for $href -->" . "\n";
615 // inlining failed, so enqueue again
616 wp_enqueue_style($handle, $href, array(), null);
617 }
618 } elseif ('async' === $this->options['gfonts_method']) {
619 WP_Optimize_Minify_Print::async_style($href);
620 $done = array_merge($done, array($handle));
621 } elseif ('exclude' === $this->options['gfonts_method']) {
622 // make a stylesheet, hide from PageSpeedIndex
623 WP_Optimize_Minify_Print::exclude_style($href);
624 $done = array_merge($done, array($handle));
625 }
626 }
627 }
628
629 // get groups of handles
630 foreach ($styles->to_do as $handle) {
631 // skip already processed google fonts and empty dependencies
632 if (isset($google_fonts[$handle]) && 'inherit' !== $this->options['gfonts_method']) {
633 continue;
634 }
635 if (empty($wp_styles->registered[$handle]->src)) {
636 continue;
637 }
638 if (WP_Optimize_Minify_Functions::in_arrayi($handle, $done)) {
639 continue;
640 }
641 if (!isset($process[$handle])) {
642 continue;
643 }
644
645 // get full url
646 $href = $process[$handle]['url'];
647 $conditional = $process[$handle]['conditional'];
648 $mediatype = $process[$handle]['mediatype'];
649
650 // IE only files don't increment things
651 $ieonly = WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href);
652 if ($ieonly) {
653 continue;
654 }
655
656 $file_size = WP_Optimize_Minify_Functions::get_file_size($href);
657
658 // If we can't determine file size, then we still need to proceed with normal minify process
659 if (!apply_filters('wp_optimize_skip_inlining', false === $file_size || $file_size > 20480 || !$this->options['inline_css'], $file_size, $href)) continue;
660
661 // skip ignore list, conditional css, external css, font-awesome merge
662 if (($process_css && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($conditional) && WP_Optimize_Minify_Functions::internal_url($href, site_url()))
663 || empty($href)
664 || ($process_css && 'inherit' == $this->options['fawesome_method'] && WP_Optimize_Minify_Functions::is_font_awesome($href))
665 || ($process_css && 'inherit' == $this->options['gfonts_method'] && WP_Optimize_Minify_Functions::is_google_font($href))
666 ) {
667 // colect inline css for this handle
668 if (isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) {
669 $inline_css[$handle] = WP_Optimize_Minify_Functions::minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); // save
670 $wp_styles->registered[$handle]->extra['after'] = null; // dequeue
671 }
672
673 // process
674 if (isset($header[count($header)-1]['handle']) || count($header) == 0 || $header[count($header)-1]['media'] != $mediatype || !$merge_css) {
675 array_push($header, array('handles' => array(), 'media' => $mediatype, 'versions' => array()));
676 }
677
678 // push it to the array
679 array_push($header[count($header)-1]['handles'], $handle);
680 array_push($header[count($header)-1]['handles'], $wp_styles->registered[$handle]->ver);
681
682 // external and ignored css
683 } else {
684
685 // normal enqueuing
686 array_push($header, array('handle' => $handle));
687 }
688 }
689
690 /**
691 * Filters the array of stylesheets before processing them
692 *
693 * @param array $list - The list of items filtered
694 * @param string $location - The location of the list (footer or header)
695 * @return array
696 */
697 $header = apply_filters('wpo_minify_stylesheets', $header, 'header');
698
699 // loop through header css and merge
700 for ($i=0,$l=count($header); $i<$l; $i++) {
701 if (!isset($header[$i]['handle'])) {
702 if ($merge_css) {
703 // get hash for the inline css in this group
704 $inline_css_group = array();
705 foreach ($header[$i]['handles'] as $h) {
706 if (isset($inline_css[$h]) && !empty($inline_css[$h])) {
707 $inline_css_group[] = $inline_css[$h];
708 }
709 }
710 $inline_css_hash = md5(implode('', $inline_css_group));
711 $hash = hash('adler32', implode('', $header[$i]['handles']).$inline_css_hash . implode('', $header[$i]['versions']));
712 } else {
713 $hash = implode('', $header[$i]['handles']) . implode('', $header[$i]['versions']);
714 }
715
716 // static cache file info
717 $file_name = 'wpo-minify-header-'.$hash.($minify_css ? '.min' : '');
718
719 // create cache files and urls
720 $file = $cache_dir.'/'.$file_name.'.css';
721
722 $file_url = WP_Optimize_Minify_Functions::get_protocol("$cache_dir_url/$file_name.css");
723
724 // generate a new cache file
725 clearstatcache();
726 if (!file_exists($file)) {
727
728 // code and log initialization
729 $log = array(
730 'header' => "PROCESSED on ".date('r')." from ".home_url(add_query_arg(null, null)),
731 'files' => array()
732 );
733 $code = '';
734
735 // minify and write to file
736 foreach ($header[$i]['handles'] as $handle) {
737 if (!empty($wp_styles->registered[$handle]->src)) {
738
739 // get href per handle
740 $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src);
741 $version = $wp_styles->registered[$handle]->ver;
742
743 // inlined scripts without file
744 if (empty($href)) continue;
745
746 // download, minify, cache
747 $tkey = 'css-'.hash('adler32', $handle . $href).'.css';
748 $json = false;
749 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
750 if (false === $json) {
751 $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $this->options['enable_css_minification'], 'css', $handle, $version);
752 if ($this->options['debug']) {
753 echo "<!-- wpo_min DEBUG: Uncached file processing now for $handle / $href / $version -->" . "\n";
754 }
755 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json);
756 }
757
758 // decode
759 $res = json_decode($json, true);
760
761 if (isset($res['request']['version']) && $res['request']['version'] != $version && !$this->minify_cache_incremented) {
762 WP_Optimize_Minify_Cache_Functions::reset();
763 $this->minify_cache_incremented = true;
764 }
765
766 // response has failed
767 if (true != $res['status']) {
768 $log['files'][$handle] = $res['log'];
769 continue;
770 }
771
772 // Only add the $handle to $done if it was successfully downloaded
773 $done[] = $handle;
774 // append code to merged file
775 $code .= isset($res['code']) ? $res['code'] : '';
776 $log['files'][$handle] = $res['log'];
777
778 // append inlined styles
779 if ($merge_inline_extra_css_js && isset($inline_css[$handle]) && !empty($inline_css[$handle])) {
780 $code.= $inline_css[$handle];
781 }
782
783 // consider dependencies on handles with an empty src
784 } else {
785 wp_dequeue_script($handle);
786 wp_enqueue_script($handle);
787 }
788 };
789
790 // generate cache, write log
791 if (!empty($code)) {
792 WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log);
793 }
794 } else {
795 $log_file = $file.'.json';
796 if (file_exists($log_file)) {
797 $saved_log = json_decode(file_get_contents($log_file));
798 if (is_object($saved_log) && property_exists($saved_log, 'files')) {
799 $files = (array) $saved_log->files;
800 foreach ($header[$i]['handles'] as $handle) {
801 if (isset($files[$handle]) && $files[$handle]->success) {
802 $done[] = $handle;
803 }
804 }
805 } else {
806 // The merged file already exists, so add all files to $done.
807 $done = array_merge($done, $header[$i]['handles']);
808 }
809 } else {
810 // The merged file already exists, so add all files to $done.
811 $done = array_merge($done, $header[$i]['handles']);
812 }
813 }
814
815 // the developers tab, takes precedence
816 // Async CSS with loadCSS ?
817 if ($this->options['loadcss'] && empty($this->options['remove_css'])) {
818 $mt = $header[$i]['media'];
819 WP_Optimize_Minify_Print::async_style($file_url, $mt);
820 // enqueue file, if not empty
821 } else {
822 if (file_exists($file) && filesize($file) > 0) {
823
824 // inline CSS if mediatype is not of type "all" (such as mobile only), if the file is smaller than 20KB
825 if (filesize($file) < 20000 && 'all' != $header[$i]['media']) {
826 echo '<style id="wpo-min-header-'.$i.'" media="'.$header[$i]['media'].'">'.file_get_contents($file).'</style>' . "\n";
827 } else {
828 // enqueue it
829 wp_enqueue_style("wpo_min-header-$i", $file_url, array(), 'mycoolversion', $header[$i]['media']);
830 foreach ($header[$i]['handles'] as $h) {
831 if (!$merge_inline_extra_css_js && isset($inline_css[$h]) && !empty($inline_css[$h])) {
832 wp_add_inline_style("wpo_min-header-$i", $inline_css[$h]);
833 }
834 }
835 }
836 } else {
837 // file could not be generated, output something meaningful
838 echo "<!-- ERROR: WP-Optimize Minify was not allowed to save its cache on - ".str_replace(ABSPATH, '', $file)." -->";
839 echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->";
840 }
841 }
842 // other css need to be requeued for the order of files to be kept
843 } else {
844 wp_dequeue_style($header[$i]['handle']);
845 wp_enqueue_style($header[$i]['handle']);
846 }
847 }
848
849 // remove from queue
850 $wp_styles->done = $done;
851 return true;
852 }
853
854 /**
855 * Process JS in the footer
856 *
857 * @return void
858 */
859 public function process_footer_scripts() {
860 global $wp_scripts;
861 if (!is_object($wp_scripts)) {
862 return;
863 }
864 $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path();
865 $cache_dir = $cache_path['cachedir'];
866 $cache_dir_url = $cache_path['cachedirurl'];
867
868
869 $exclude_js = array_map('trim', explode("\n", trim($this->options['exclude_js'])));
870 $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_js);
871 $async_js = trim($this->options['async_js']) ? array_map('trim', explode("\n", trim($this->options['async_js']))) : array();
872 $scripts = clone $wp_scripts;
873 $scripts->all_deps($scripts->queue);
874 $footer = array();
875 $minify_js = $this->options['enable_js_minification'];
876 $merge_js = $this->options['enable_merging_of_js'];
877 $process_js = $minify_js || $merge_js;
878 $merge_inline_extra_css_js = $this->options['merge_inline_extra_css_js'];
879
880 // mark as done (as we go)
881 $done = $scripts->done;
882
883 // get groups of handles
884 foreach ($scripts->to_do as $handle) :
885
886 // get full url
887 $href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$handle]->src);
888
889 // inlined scripts without file
890 if (empty($href)) {
891 continue;
892 }
893
894 // Exclude JS files from PageSpeedIndex (Async) takes priority over the ignore list
895 if (false != $async_js || is_array($async_js)) {
896
897 // check for string match
898 $skipjs = false;
899 foreach ($async_js as $l) {
900 if (stripos($href, $l) !== false) {
901 // print code if there are no linebreaks, or return
902 WP_Optimize_Minify_Print::async_script($href);
903 $skipjs = true;
904 $done = array_merge($done, array($handle));
905 break;
906 }
907 }
908 if (false != $skipjs) {
909 continue;
910 }
911 }
912
913 // IE only files don't increment things
914 $ieonly = WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href);
915 if ($ieonly) {
916 continue;
917 }
918
919 // skip ignore list, scripts with conditionals, external scripts
920 if (($process_js && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && WP_Optimize_Minify_Functions::internal_url($href, site_url()))
921 || empty($href)
922 ) {
923
924 // process
925 if (isset($footer[count($footer)-1]['handle']) || !count($footer) || !$merge_js) {
926 array_push($footer, array('handles' => array(), 'versions' => array()));
927 }
928
929 if (isset($wp_scripts->registered[$handle]->extra['before'])) {
930 if (!empty($footer[count($footer)-1]['handles'])) {
931 array_push($footer, array('handles' => array(), 'versions' => array()));
932 }
933 }
934
935 // push it to the array
936 array_push($footer[count($footer)-1]['handles'], $handle);
937 array_push($footer[count($footer)-1]['versions'], $wp_scripts->registered[$handle]->ver);
938 // external and ignored scripts
939 } else {
940 array_push($footer, array('handle' => $handle));
941 }
942 endforeach;
943
944 // loop through footer scripts and merge
945 for ($i=0,$l=count($footer); $i<$l; $i++) {
946 if (!isset($footer[$i]['handle'])) {
947
948 if ($merge_js) {
949 // Change the hash based on version numbers
950 $hash = hash('adler32', implode('', $footer[$i]['handles']) . implode('', $footer[$i]['versions']));
951 } else {
952 $hash = implode('', $footer[$i]['handles']) . implode('', $footer[$i]['versions']);
953 }
954
955 // static cache file info
956 $file_name = 'wpo-minify-footer-'.$hash.($minify_js ? '.min' : '');
957
958 // create cache files and urls
959 $file = $cache_dir.'/'.$file_name.'.js';
960 $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.js');
961
962 // generate a new cache file
963 clearstatcache();
964 if (!file_exists($file)) {
965
966 // code and log initialization
967 $log = array(
968 'header' => "PROCESSED on ".date('r')." from ".home_url(add_query_arg(null, null)),
969 'files' => array()
970 );
971 $code = '';
972
973 // minify and write to file
974 foreach ($footer[$i]['handles'] as $handle) :
975 if (!empty($wp_scripts->registered[$handle]->src)) {
976 // get href per handle
977 $href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$handle]->src);
978 $version = $wp_scripts->registered[$handle]->ver;
979 // inlined scripts without file
980 if (empty($href)) {
981 continue;
982 }
983 // download, minify, cache
984 $tkey = 'js-'.hash('adler32', $handle . $href).'.js';
985 $json = false;
986 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
987 if (false === $json) {
988 $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $minify_js, 'js', $handle, $version);
989 if ($this->options['debug']) {
990 echo "<!-- wpo_min DEBUG: Uncached file processing now for $handle / $href / $version -->\n";
991 }
992 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json);
993 }
994
995 // decode
996 $res = json_decode($json, true);
997
998 if (isset($res['request']['version']) && $res['request']['version'] != $version && !$this->minify_cache_incremented) {
999 WP_Optimize_Minify_Cache_Functions::reset();
1000 $this->minify_cache_incremented = true;
1001 }
1002
1003 // response has failed
1004 if (true != $res['status']) {
1005 $log['files'][$handle] = $res['log'];
1006 continue;
1007 }
1008
1009 $done[] = $handle;
1010 // Add extra data from wp_add_inline_script before
1011 if (!empty($wp_scripts->registered[$handle]->extra)) {
1012 if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) {
1013 if ($merge_inline_extra_css_js) {
1014 $code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])), $href . ' - BEFORE');
1015 }
1016 }
1017 }
1018
1019 // Add translation
1020 if (!empty($wp_scripts->registered[$handle]->textdomain)) {
1021 $code .= "\n" . $wp_scripts->print_translations($handle, false);
1022 }
1023
1024 // append code to merged file
1025 $code .= isset($res['code']) ? WP_Optimize_Minify_Functions::prepare_merged_js($res['code'], $href) : '';
1026 $log['files'][$handle] = $res['log'];
1027
1028 // Add extra data from wp_add_inline_script after
1029 if (!empty($wp_scripts->registered[$handle]->extra)) {
1030 if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) {
1031 if ($merge_inline_extra_css_js) {
1032 $code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])), $href. ' - AFTER');
1033 }
1034 }
1035 }
1036
1037 // consider dependencies on handles with an empty src
1038 } else {
1039 wp_dequeue_script($handle);
1040 wp_enqueue_script($handle);
1041 }
1042 endforeach;
1043
1044 // generate cache, write log
1045 if (!empty($code)) {
1046 WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log);
1047 }
1048 } else {
1049 $log_file = $file.'.json';
1050 if (file_exists($log_file)) {
1051 $saved_log = json_decode(file_get_contents($log_file));
1052 if (is_object($saved_log) && property_exists($saved_log, 'files')) {
1053 $files = (array) $saved_log->files;
1054 foreach ($footer[$i]['handles'] as $handle) {
1055 if (isset($files[$handle]) && $files[$handle]->success) {
1056 $done[] = $handle;
1057 }
1058 }
1059 } else {
1060 // The merged file already exists, so add all files to $done.
1061 $done = array_merge($done, $footer[$i]['handles']);
1062 }
1063 } else {
1064 // The merged file already exists, so add all files to $done.
1065 $done = array_merge($done, $footer[$i]['handles']);
1066 }
1067 }
1068
1069 // register minified file
1070 wp_register_script("wpo_min-footer-$i", $file_url, array(), null, false);
1071
1072 // add all extra data from wp_localize_script
1073 $before_code = '';
1074 $data = array();
1075 $after_code = '';
1076
1077 foreach ($footer[$i]['handles'] as $handle) {
1078 if (isset($wp_scripts->registered[$handle]->extra['data'])) {
1079 $data[] = $wp_scripts->registered[$handle]->extra['data'];
1080 }
1081 // Add extra data from wp_add_inline_script before
1082 if (!empty($wp_scripts->registered[$handle]->extra)) {
1083 if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) {
1084 if (!$merge_inline_extra_css_js) {
1085 $before_code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])), $href.' - BEFORE');
1086 }
1087 }
1088 if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) {
1089 if (!$merge_inline_extra_css_js) {
1090 $after_code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])), $href.' - AFTER');
1091 }
1092 }
1093 }
1094 }
1095 if (count($data) > 0) {
1096 $wp_scripts->registered["wpo_min-footer-$i"]->extra['data'] = implode("\n", $data);
1097 }
1098
1099 // enqueue file, if not empty
1100 if (file_exists($file) && (filesize($file) > 0 || count($data) > 0)) {
1101 if (!empty($before_code)) {
1102 wp_add_inline_script("wpo_min-footer-$i", $before_code, 'before');
1103 }
1104 wp_enqueue_script("wpo_min-footer-$i");
1105 if (!empty($after_code)) {
1106 wp_add_inline_script("wpo_min-footer-$i", $after_code, 'after');
1107 }
1108 } else {
1109 // file could not be generated, output something meaningful
1110 echo "<!-- ERROR: WP-Optimize Minify was not allowed to save its cache on - ".str_replace(ABSPATH, '', $file)." -->";
1111 echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->";
1112 }
1113
1114 // other scripts need to be requeued for the order of files to be kept
1115 } else {
1116 wp_dequeue_script($footer[$i]['handle']);
1117 wp_enqueue_script($footer[$i]['handle']);
1118 }
1119 }
1120
1121 // remove from queue
1122 $wp_scripts->done = $done;
1123 }
1124
1125 /**
1126 * Process header JavaScript
1127 * Dependant on 'enable_js' option
1128 *
1129 * @return void
1130 */
1131 public function process_header_scripts() {
1132 global $wp_scripts;
1133 if (!is_object($wp_scripts)) return;
1134 $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path();
1135 $cache_dir = $cache_path['cachedir'];
1136 $cache_dir_url = $cache_path['cachedirurl'];
1137 $exclude_js = array_map('trim', explode("\n", trim($this->options['exclude_js'])));
1138 $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_js);
1139 $async_js = trim($this->options['async_js']) ? array_map('trim', explode("\n", trim($this->options['async_js']))) : array();
1140 $scripts = clone $wp_scripts;
1141 $scripts->all_deps($scripts->queue);
1142 $minify_js = $this->options['enable_js_minification'];
1143 $merge_js = $this->options['enable_merging_of_js'];
1144 $process_js = $minify_js || $merge_js;
1145 $merge_inline_extra_css_js = $this->options['merge_inline_extra_css_js'];
1146 $header = array();
1147 // mark as done (as we go)
1148 $done = $scripts->done;
1149 $excluded_dependencies = array();
1150
1151 // Prepare and separate assets (get groups of handles)
1152 foreach ($scripts->to_do as $handle) {
1153 // get full url
1154 $href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$handle]->src);
1155 // inlined scripts without file
1156 if (empty($href)) {
1157 wp_enqueue_script($handle, false);
1158 continue;
1159 }
1160
1161 // Only go through items without a group
1162 if (!$scripts->groups[$handle]) {
1163 // Exclude JS files from PageSpeedIndex (Async) takes priority over the ignore list
1164 // check for string match
1165 $skipjs = false;
1166 foreach ($async_js as $l) {
1167 if (stripos($href, $l) !== false) {
1168 WP_Optimize_Minify_Print::async_script($href);
1169 $skipjs = true;
1170 $done = array_merge($done, array($handle));
1171 break;
1172 }
1173 }
1174 // IE only files don't increment things
1175 if ($skipjs
1176 || WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href)
1177 ) {
1178 continue;
1179 }
1180
1181 // Skip jQuery from the merged files if defering is ENABLED and defer_jquery is DISABLED
1182 if ('all' === $this->options['enable_defer_js']
1183 && !$this->options['defer_jquery']
1184 && (false !== stripos($href, '/jquery.js')
1185 || false !== stripos($href, '/jquery.min.js')
1186 || (false !== stripos($href, '/jquery-') && false !== stripos($href, '.js')))
1187 ) {
1188 continue;
1189 }
1190
1191 // Group handles - skip ignore list, scripts with conditionals, external scripts
1192 if (($process_js && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && WP_Optimize_Minify_Functions::internal_url($href, site_url()))
1193 || empty($href)
1194 ) {
1195 // process
1196 if (isset($header[count($header)-1]['handle']) || !count($header) || !$merge_js) {
1197 array_push($header, array('handles' => array(), 'versions' => array()));
1198 }
1199
1200 // Force loading of dependencies
1201 foreach ($wp_scripts->registered[$handle]->deps as $dep) {
1202 // If the handle is not present in $done yet, or excluded, enqueue it.
1203 $dep_href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$dep]->src);
1204 if (!in_array($dep, $done) && !WP_Optimize_Minify_Functions::in_arrayi($dep_href, $ignore_list)) {
1205 // Include any dependency
1206 array_push($header[count($header)-1]['handles'], $dep);
1207 array_push($header[count($header)-1]['versions'], $wp_scripts->registered[$dep]->ver);
1208 } elseif (!in_array($dep, $done) && WP_Optimize_Minify_Functions::in_arrayi($dep_href, $ignore_list)) {
1209 // The dependency is in the exclude list
1210 array_push($header, array('handle' => $dep));
1211 // Record dependency to be added to the minified script as dependency array
1212 if (isset($excluded_dependencies[count($header)])) {
1213 $excluded_dependencies[count($header)][] = $dep;
1214 } else {
1215 $excluded_dependencies[count($header)] = array($dep);
1216 }
1217 // Adds the 'handles' record for the main script
1218 array_push($header, array('handles' => array(), 'versions' => array()));
1219 }
1220 }
1221
1222 if (isset($wp_scripts->registered[$handle]->extra['before'])) {
1223 if (!empty($header[count($header)-1]['handles'])) {
1224 array_push($header, array('handles' => array(), 'versions' => array()));
1225 }
1226 }
1227
1228 // push it to the array
1229 array_push($header[count($header)-1]['handles'], $handle);
1230 array_push($header[count($header)-1]['versions'], $scripts->registered[$handle]->ver);
1231
1232 // external and ignored scripts
1233 } else {
1234 // add the ignored assets
1235 array_push($header, array('handle' => $handle));
1236 }
1237
1238 // make sure that the scripts skipped here, show up in the footer
1239 } else {
1240 wp_enqueue_script($handle, $href, array(), null, true);
1241 }
1242 }
1243
1244 // loop through header scripts and merge
1245 for ($i=0,$l=count($header); $i < $l; $i++) {
1246 if (!isset($header[$i]['handle'])) {
1247
1248 if ($merge_js) {
1249 $hash = hash('adler32', implode('', $header[$i]['handles']) . implode('', $header[$i]['versions']));
1250 } else {
1251 $hash = implode('', $header[$i]['handles']) . implode('', $header[$i]['versions']);
1252 }
1253
1254 // static cache file info
1255 $file_name = 'wpo-minify-header-'.$hash.($minify_js ? '.min' : '');
1256 // create cache files and urls
1257 $file = $cache_dir.'/'.$file_name.'.js';
1258 $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.js');
1259
1260 // generate a new cache file
1261 clearstatcache();
1262 if (!file_exists($file)) {
1263
1264 // code and log initialization
1265 $log = array(
1266 'header' => "PROCESSED on ".date('r')." from ".home_url(add_query_arg(null, null)),
1267 'files' => array()
1268 );
1269 $code = '';
1270
1271 // minify and write to file
1272 foreach ($header[$i]['handles'] as $handle) {
1273 if (!empty($wp_scripts->registered[$handle]->src)) {
1274
1275 // get href per handle
1276 $href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$handle]->src);
1277 $version = $wp_scripts->registered[$handle]->ver;
1278 if (empty($href)) continue;
1279 // download, minify, cache
1280 $tkey = 'js-'.hash('adler32', $handle . $href).'.js';
1281 $json = false;
1282 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
1283 if (false === $json) {
1284 $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $minify_js, 'js', $handle, $version);
1285 if ($this->options['debug']) {
1286 echo "<!-- wpo_min DEBUG: Uncached file processing now for $handle / $href / $version -->" . "\n";
1287 }
1288 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json);
1289 }
1290
1291 // decode
1292 $res = json_decode($json, true);
1293
1294 if (isset($res['request']['version']) && $res['request']['version'] != $version && !$this->minify_cache_incremented) {
1295 WP_Optimize_Minify_Cache_Functions::reset();
1296 $this->minify_cache_incremented = true;
1297 }
1298
1299 // response has failed
1300 if (true != $res['status']) {
1301 $log['files'][$handle] = $res['log'];
1302 continue;
1303 }
1304
1305 // Add extra data from wp_add_inline_script before
1306 if (!empty($wp_scripts->registered[$handle]->extra)) {
1307 if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) {
1308 if ($merge_inline_extra_css_js) {
1309 $code .= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])), $href.' - BEFORE');
1310 }
1311 }
1312 }
1313
1314 // Only add the $handle to $done if it was successfully downloaded
1315 $done[] = $handle;
1316
1317 // Add translations
1318 if (!empty($wp_scripts->registered[$handle]->textdomain)) {
1319 $code .= "\n" . $wp_scripts->print_translations($handle, false);
1320 }
1321
1322 // append code to merged file
1323 $code .= isset($res['code']) ? WP_Optimize_Minify_Functions::prepare_merged_js($res['code'], $href) : '';
1324 $log['files'][$handle] = $res['log'];
1325
1326 // Add extra data from wp_add_inline_script after
1327 if (!empty($wp_scripts->registered[$handle]->extra)) {
1328 if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) {
1329 if ($merge_inline_extra_css_js) {
1330 $code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])), $href.' - AFTER');
1331 }
1332 }
1333 }
1334
1335 // consider dependencies on handles with an empty src
1336 } else {
1337 wp_dequeue_script($handle);
1338 wp_enqueue_script($handle);
1339 }
1340 }
1341
1342 // generate cache, write log
1343 if (!empty($code)) {
1344 WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log);
1345 }
1346 } else {
1347 $log_file = $file.'.json';
1348 if (file_exists($log_file)) {
1349 $saved_log = json_decode(file_get_contents($log_file));
1350 if (is_object($saved_log) && property_exists($saved_log, 'files')) {
1351 $files = (array) $saved_log->files;
1352 foreach ($header[$i]['handles'] as $handle) {
1353 if (isset($files[$handle]) && $files[$handle]->success) {
1354 $done[] = $handle;
1355 }
1356 }
1357 } else {
1358 // The merged file already exists, so add all files to $done.
1359 $done = array_merge($done, $header[$i]['handles']);
1360 }
1361 } else {
1362 // The merged file already exists, so add all files to $done.
1363 $done = array_merge($done, $header[$i]['handles']);
1364 }
1365 }
1366
1367 // register minified file
1368 $dependencies = isset($excluded_dependencies[$i]) ? $excluded_dependencies[$i] : array();
1369 wp_register_script("wpo_min-header-$i", $file_url, $dependencies, null, false);
1370
1371 // add all extra data from wp_localize_script
1372 $before_code = '';
1373 $data = array();
1374 $after_code = '';
1375
1376 foreach ($header[$i]['handles'] as $handle) {
1377 if (isset($wp_scripts->registered[$handle]->extra['data'])) {
1378 $data[] = $wp_scripts->registered[$handle]->extra['data'];
1379 }
1380
1381 // Add extra data from wp_add_inline_script before
1382 if (!empty($wp_scripts->registered[$handle]->extra)) {
1383 if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) {
1384 if (!$merge_inline_extra_css_js) {
1385 $before_code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])), $href.' - BEFORE');
1386 }
1387 }
1388 if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) {
1389 if (!$merge_inline_extra_css_js) {
1390 $after_code.= "\n" . WP_Optimize_Minify_Functions::prepare_merged_js(implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])), $href.' - AFTER');
1391 }
1392 }
1393 }
1394 }
1395 if (count($data) > 0) {
1396 $wp_scripts->registered["wpo_min-header-$i"]->extra['data'] = implode("\n", $data);
1397 }
1398
1399 // enqueue file, if not empty
1400 if (file_exists($file) && (filesize($file) > 0 || count($data) > 0)) {
1401 if (!empty($before_code)) {
1402 wp_add_inline_script("wpo_min-header-$i", $before_code, 'before');
1403 }
1404 wp_enqueue_script("wpo_min-header-$i");
1405 if (!empty($after_code)) {
1406 wp_add_inline_script("wpo_min-header-$i", $after_code, 'after');
1407 }
1408 } else {
1409 // file could not be generated, output something meaningful
1410 echo "<!-- ERROR: WP-Optimize minify was not allowed to save its cache on - ".str_replace(ABSPATH, '', $file)." -->";
1411 echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->";
1412 echo "<!-- If you found a bug, please report this on https://wordpress.org/support/plugin/wp-optimize/ -->";
1413 }
1414
1415 // other scripts need to be requeued for the order of files to be kept
1416 } else {
1417 wp_dequeue_script($header[$i]['handle']);
1418 wp_enqueue_script($header[$i]['handle']);
1419 }
1420 }
1421
1422 // remove from queue
1423 $wp_scripts->done = $done;
1424 }
1425
1426 /**
1427 * Process CSS in the footer
1428 *
1429 * @return void
1430 */
1431 public function process_footer_css() {
1432 global $wp_styles;
1433 if (!is_object($wp_styles)) return;
1434 $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path();
1435 $cache_dir = $cache_path['cachedir'];
1436 $cache_dir_url = $cache_path['cachedirurl'];
1437 $exclude_css = array_map('trim', explode("\n", trim($this->options['exclude_css'])));
1438 $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css);
1439 $async_css = array_map('trim', explode("\n", trim($this->options['async_css'])));
1440 $minify_css = $this->options['enable_css_minification'];
1441 $merge_css = $this->options['enable_merging_of_css'];
1442 $process_css = $minify_css || $merge_css;
1443 $log = "";
1444 $code = "";
1445 $styles = clone $wp_styles;
1446 $styles->all_deps($styles->queue);
1447 $done = $styles->done;
1448 $footer = array();
1449 $google_fonts = array();
1450 $inline_css = array();
1451
1452 // dequeue all styles
1453 if (isset($this->options['remove_css']) && $this->options['remove_css']) {
1454 foreach ($styles->to_do as $handle) :
1455 $done = array_merge($done, array($handle));
1456 endforeach;
1457
1458 // remove from queue
1459 $wp_styles->done = $done;
1460 return;
1461 }
1462
1463 // dequeue and get a list of google fonts, or requeue external
1464 foreach ($styles->to_do as $handle) {
1465 $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src);
1466 // inlined scripts without file
1467 if (empty($href)) continue;
1468
1469 if (WP_Optimize_Minify_Functions::is_google_font($href)) {
1470 wp_dequeue_style($handle);
1471 if ($this->options['remove_googlefonts']) {
1472 $done = array_merge($done, array($handle));
1473 continue;
1474 }
1475 // mark as done if to be removed
1476 if ($this->options['merge_google_fonts']
1477 || 'inline' === $this->options['gfonts_method']
1478 ) {
1479 if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) {
1480 $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href);
1481 $google_fonts[$handle] = $href;
1482 } else {
1483 $google_fonts[$handle] = $href;
1484 }
1485 } else {
1486 // skip google fonts optimization?
1487 wp_enqueue_style($handle);
1488 }
1489 } else {
1490 // failsafe
1491 wp_dequeue_style($handle);
1492 wp_enqueue_style($handle);
1493 }
1494
1495 }
1496
1497 // concat google fonts, if enabled
1498 if ($this->options['merge_google_fonts']
1499 && count($google_fonts) > 0
1500 || ('inline' === $this->options['gfonts_method'] && count($google_fonts) > 0)
1501 ) {
1502
1503 // merge google fonts if force inlining is enabled?
1504 $nfonts = array();
1505 if ($this->options['merge_google_fonts']) {
1506 $nfonts[] = WP_Optimize_Minify_Fonts::concatenate_google_fonts($google_fonts);
1507 } else {
1508 foreach ($google_fonts as $h => $a) {
1509 if (!empty($a)) {
1510 $nfonts[$h] = $a;
1511 }
1512 }
1513 }
1514
1515 // foreach google font (will be one if merged is not disabled)
1516 if (count($nfonts) > 0) {
1517 foreach ($nfonts as $handle => $href) {
1518 // hide from PageSpeedIndex, async, inline, or default
1519 if ('exclude' === $this->options['gfonts_method']) {
1520 WP_Optimize_Minify_Print::exclude_style($href);
1521 $done = array_merge($done, array($handle));
1522 } elseif ('async' === $this->options['gfonts_method']) {
1523 // async CSS
1524 WP_Optimize_Minify_Print::async_style($href);
1525 $done = array_merge($done, array($handle));
1526 } elseif ('inline' === $this->options['gfonts_method']) {
1527 // inline css
1528 WP_Optimize_Minify_Print::inline_style($handle, $href);
1529 $done = array_merge($done, array($handle));
1530 } else {
1531 // fallback, enqueue google fonts
1532 wp_enqueue_style($handle, $href, array(), null, 'all');
1533 }
1534 }
1535 }
1536 }
1537
1538
1539 // get groups of handles
1540 $uniq = array();
1541 foreach ($styles->to_do as $handle) {
1542
1543 // skip already processed google fonts
1544 if (isset($google_fonts[$handle])) {
1545 continue;
1546 }
1547
1548 // conditionals
1549 $conditional = null; if (isset($wp_styles->registered[$handle]->extra["conditional"])) {
1550 $conditional = $wp_styles->registered[$handle]->extra["conditional"]; // such as ie7, ie8, ie9, etc
1551 }
1552
1553 // mediatype
1554 $mt = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all';
1555 if ('screen' == $mt || 'screen, print' == $mt || empty($mt) || is_null($mt) || false == $mt) {
1556 $mt = 'all';
1557 }
1558 $mediatype = $mt;
1559
1560 // get full url
1561 $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src);
1562 $version = $wp_styles->registered[$handle]->ver;
1563
1564 // inlined scripts without file
1565 if (empty($href)) {
1566 continue;
1567 }
1568
1569 // mark duplicates as done and remove from the queue
1570 if (!empty($href)) {
1571 $key = hash('adler32', $href . $version);
1572 if (isset($uniq[$key])) {
1573 $done = array_merge($done, array($handle));
1574 continue;
1575 } else {
1576 $uniq[$key] = $handle;
1577 }
1578 }
1579
1580 // IE only files don't increment things
1581 $ieonly = WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href);
1582 if ($ieonly) {
1583 continue;
1584 }
1585
1586 // Exclude specific CSS files from PageSpeedIndex?
1587 if (false != $async_css && is_array($async_css) && WP_Optimize_Minify_Functions::in_arrayi($href, $async_css)) {
1588 WP_Optimize_Minify_Print::exclude_style($href);
1589 $done = array_merge($done, array($handle));
1590 continue;
1591 }
1592
1593 if (WP_Optimize_Minify_Functions::is_font_awesome($href)) {
1594 if ('inline' === $this->options['fawesome_method']) {
1595 WP_Optimize_Minify_Print::inline_style($handle, $href);
1596 $done = array_merge($done, array($handle));
1597 continue;
1598 } elseif ('async' === $this->options['fawesome_method']) {
1599 WP_Optimize_Minify_Print::async_style($href, $mediatype);
1600 $done = array_merge($done, array($handle));
1601 continue;
1602 } elseif ('exclude' === $this->options['fawesome_method']) {
1603 WP_Optimize_Minify_Print::exclude_style($href);
1604 $done = array_merge($done, array($handle));
1605 continue;
1606 }
1607 }
1608
1609 // Exclude Print mediatype
1610 if ($this->options['remove_print_mediatypes'] && 'print' === $mediatype) {
1611 $done = array_merge($done, array($handle));
1612 continue;
1613 }
1614
1615 // skip ignore list, conditional css, external css, font-awesome merge
1616 if (($process_css && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($conditional) && WP_Optimize_Minify_Functions::internal_url($href, site_url()))
1617 || empty($href)
1618 ) {
1619
1620 // colect inline css for this handle
1621 if (isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) {
1622 $inline_css[$handle] = WP_Optimize_Minify_Functions::minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); // save
1623 $wp_styles->registered[$handle]->extra['after'] = null; // dequeue
1624 }
1625
1626 // process
1627 if (isset($footer[count($footer)-1]['handle']) || !count($footer) || $footer[count($footer)-1]['media'] != $wp_styles->registered[$handle]->args || !$merge_css) {
1628 array_push($footer, array('handles' => array(), 'media' => $mediatype, 'versions' => array()));
1629 }
1630
1631 // push it to the array get latest modified time
1632 array_push($footer[count($footer)-1]['handles'], $handle);
1633 array_push($footer[count($footer)-1]['handles'], $version);
1634
1635 // external and ignored css
1636 } else {
1637
1638 // normal enqueueing
1639 array_push($footer, array('handle' => $handle));
1640 }
1641 }
1642
1643 /**
1644 * Filters the array of stylesheets before processing them
1645 *
1646 * @param array $list - The list of items filtered
1647 * @param string $location - The location of the list (footer or header)
1648 * @return array
1649 */
1650 $footer = apply_filters('wpo_minify_stylesheets', $footer, 'footer');
1651
1652 // loop through footer css and merge
1653 for ($i=0,$l=count($footer); $i<$l; $i++) {
1654 if (!isset($footer[$i]['handle'])) {
1655 if ($merge_css) {
1656 // get hash for the inline css in this group
1657 $inline_css_group = array();
1658 foreach ($footer[$i]['handles'] as $h) {
1659 if (isset($inline_css[$h]) && !empty($inline_css[$h])) {
1660 $inline_css_group[] = $inline_css[$h];
1661 }
1662 }
1663 $inline_css_hash = md5(implode('', $inline_css_group));
1664 $hash = hash('adler32', implode('', $footer[$i]['handles']) . $inline_css_hash . implode('', $footer[$i]['versions']));
1665 } else {
1666 $hash = implode('', $footer[$i]['handles']) . implode('', $footer[$i]['versions']);
1667 }
1668
1669 // static cache file info
1670 $file_name = 'wpo-minify-footer-'.$hash.($minify_css ? '.min' : '');
1671
1672 // create cache files and urls
1673 $file = $cache_dir.'/'.$file_name.'.css';
1674 $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.css');
1675
1676 // generate a new cache file
1677 clearstatcache();
1678 if (!file_exists($file)) {
1679
1680 // code and log initialization
1681 $log = array(
1682 'header' => "PROCESSED on ".date('r')." from ".home_url(add_query_arg(null, null)),
1683 'files' => array()
1684 );
1685 $code = '';
1686
1687 // minify and write to file
1688 foreach ($footer[$i]['handles'] as $handle) {
1689 if (!empty($wp_styles->registered[$handle]->src)) {
1690
1691 // get href per handle
1692 $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src);
1693 $version = $wp_styles->registered[$handle]->ver;
1694 // inlined scripts without hreffile
1695 if (empty($href)) continue;
1696
1697 // download, minify, cache
1698 $tkey = 'css-'.hash('adler32', $handle . $href).'.css';
1699 $json = false;
1700 $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey);
1701 if (false === $json) {
1702 $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $minify_css, 'css', $handle, $version);
1703 if ($this->options['debug']) {
1704 echo "<!-- wpo_min DEBUG: Uncached file processing now for $handle / $href / $version -->" . "\n";
1705 }
1706 WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json);
1707 }
1708
1709 // decode
1710 $res = json_decode($json, true);
1711
1712 if (isset($res['request']['version']) && $res['request']['version'] != $version && !$this->minify_cache_incremented) {
1713 WP_Optimize_Minify_Cache_Functions::reset();
1714 $this->minify_cache_incremented = true;
1715 }
1716
1717 // response has failed
1718 if (true != $res['status']) {
1719 $log['files'][$handle] = $res['log'];
1720 continue;
1721 }
1722
1723 $done[] = $handle;
1724
1725 // append code to merged file
1726 $code .= isset($res['code']) ? $res['code'] : '';
1727 $log['files'][$handle] = $res['log'];
1728
1729 // append inlined styles
1730 if (isset($inline_css[$handle]) && !empty($inline_css[$handle])) {
1731 $code.= $inline_css[$handle];
1732 }
1733
1734 // consider dependencies on handles with an empty src
1735 } else {
1736 wp_dequeue_script($handle);
1737 wp_enqueue_script($handle);
1738 }
1739 };
1740
1741 // generate cache, add inline css, write log
1742 if (!empty($code)) {
1743 WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log);
1744 }
1745 } else {
1746 $log_file = $file.'.json';
1747 if (file_exists($log_file)) {
1748 $saved_log = json_decode(file_get_contents($log_file));
1749 if (is_object($saved_log) && property_exists($saved_log, 'files')) {
1750 $files = (array) $saved_log->files;
1751 foreach ($footer[$i]['handles'] as $handle) {
1752 if (isset($files[$handle]) && $files[$handle]->success) {
1753 $done[] = $handle;
1754 }
1755 }
1756 } else {
1757 // The merged file already exists, so add all files to $done.
1758 $done = array_merge($done, $footer[$i]['handles']);
1759 }
1760 } else {
1761 // The merged file already exists, so add all files to $done.
1762 $done = array_merge($done, $footer[$i]['handles']);
1763 }
1764 }
1765
1766 // Async CSS with loadCSS ?
1767 if ($this->options['loadcss'] && !$this->options['remove_css']) {
1768 $mt = $footer[$i]['media'];
1769 WP_Optimize_Minify_Print::async_style($file_url, $mt);
1770 // enqueue file, if not empty
1771 } else {
1772 if (file_exists($file) && filesize($file) > 0) {
1773
1774 // inline if the file is smaller than 20KB or option has been enabled
1775 if (filesize($file) < 20000 && $this->options['inline_css']) {
1776 $this->inline_css(file_get_contents($file), $handle, $file_url, $footer[$i]['media']);
1777 } else {
1778 // enqueue it
1779 wp_enqueue_style("wpo_min-footer-$i", $file_url, array(), null, $footer[$i]['media']);
1780 }
1781 } else {
1782 // file could not be generated, output something meaningful
1783 echo "<!-- ERROR: WP-Optimize Minify was not allowed to save its cache on - ".str_replace(ABSPATH, '', $file)." -->";
1784 echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->";
1785 }
1786 }
1787
1788 // other css need to be requeued for the order of files to be kept
1789 } else {
1790 wp_dequeue_style($footer[$i]['handle']);
1791 wp_enqueue_style($footer[$i]['handle']);
1792 }
1793 }
1794
1795 // remove from queue
1796 $wp_styles->done = $done;
1797 }
1798
1799 /**
1800 * Orders the CSS per media type
1801 *
1802 * @param array $list - The list of assets
1803 * @return array
1804 */
1805 public function order_stylesheets_per_media_type($list) {
1806 // get unique mediatypes
1807 $allmedia = array();
1808 foreach ($list as $array) {
1809 if (isset($array['media'])) {
1810 $allmedia[$array['media']] = '';
1811 }
1812 }
1813
1814 // extract handles by mediatype
1815 $grouphandles = array();
1816 foreach ($allmedia as $md => $var) {
1817 foreach ($list as $array) {
1818 if (isset($array['media']) && $array['media'] === $md) {
1819 foreach ($array['handles'] as $h) {
1820 $grouphandles[$md][] = $h;
1821 }
1822 }
1823 }
1824 }
1825
1826 // reset and reorder list by mediatypes
1827 $newlist = array();
1828 foreach ($allmedia as $md => $var) {
1829 $newlist[] = array('handles' => $grouphandles[$md], 'media' => $md);
1830 }
1831 if (count($newlist) > 0) {
1832 $list = $newlist;
1833 }
1834
1835 return $list;
1836 }
1837
1838 /**
1839 * Merged google font
1840 *
1841 * @return void;
1842 */
1843 public function add_google_fonts_merged() {
1844 // must have something to do
1845 if (count($this->collect_google_fonts) == 0) return;
1846
1847 // merge google fonts
1848 $href = WP_Optimize_Minify_Fonts::concatenate_google_fonts($this->collect_google_fonts);
1849 if (empty($href)) {
1850 return;
1851 }
1852
1853 // hide google fonts from PageSpeedIndex
1854 if ('exclude' === $this->options['gfonts_method']) {
1855 // make a stylesheet, hide from PageSpeedIndex
1856 WP_Optimize_Minify_Print::exclude_style($href);
1857 } elseif ('async' === $this->options['gfonts_method']) {
1858 // load google fonts async
1859 WP_Optimize_Minify_Print::async_style($href);
1860 } else {
1861 // fallback to normal inline
1862 WP_Optimize_Minify_Print::style($href);
1863 }
1864
1865 // unset per hook
1866 foreach ($this->collect_google_fonts as $k => $v) {
1867 unset($this->collect_google_fonts[$k]);
1868 }
1869 }
1870
1871 /**
1872 * Check if the string given contains 'wpo-minify'
1873 *
1874 * @param string $var
1875 *
1876 * @return boolean
1877 */
1878 public function check_wpo($var) {
1879 return (false === strpos($var, 'assets/wpo-minify'));
1880 }
1881
1882 /**
1883 * Generate preload headers file
1884 *
1885 * @return boolean
1886 */
1887 public function generate_preload_headers() {
1888
1889 // always false for admin pages
1890 if (is_admin()) return false;
1891
1892 // get host with multisite support and query strings
1893 $host = htmlentities($_SERVER['SERVER_NAME']);
1894 if (empty($host)) {
1895 $host = htmlentities($_SERVER['HTTP_HOST']);
1896 }
1897 $request_query = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
1898 $request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
1899
1900 // initialize headers
1901 $headers = array();
1902
1903 // css headers
1904 if ($this->options['enabled_css_preload']
1905 && count($this->collect_preload_css) > 0
1906 ) {
1907 foreach ($this->collect_preload_css as $u) {
1908 // filter out footer footer files, because they are not in the critical path
1909 if (strpos($u, '/assets/wpo-minify-footer-') !== false) {
1910 continue;
1911 }
1912
1913 // add headers
1914 $headers[] = "Link: <$u>; rel=preload; as=style";
1915 }
1916 }
1917
1918 // js headers
1919 if ($this->options['enabled_js_preload']
1920 && count($this->collect_preload_js) > 0
1921 ) {
1922 foreach ($this->collect_preload_js as $u) {
1923
1924 // filter out footer footer files, because they are not in the critical path
1925 if (false !== strpos($u, '/assets/wpo-minify-footer-')) {
1926 continue;
1927 }
1928
1929 // add headers
1930 $headers[] = "Link: <$u>; rel=preload; as=script";
1931 }
1932 }
1933
1934 // must have something
1935 if (count($headers) == 0) {
1936 return false;
1937 } else {
1938 $headers = implode("\n", $headers);
1939 }
1940
1941 // get cache path
1942 $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path();
1943 $header_dir = $cache_path['headerdir'];
1944
1945 // possible cache file locations
1946 $b = $header_dir . '/' . md5($host.'-'.$request_uri) . '.header';
1947 $a = $header_dir . '/' . md5($host.'-'.$request_uri . $request_query).'.header';
1948
1949 // reset file cache
1950 clearstatcache();
1951
1952 // if there are no query strings
1953 if ($b == $a) {
1954 if (!file_exists($a)) {
1955 WP_Optimize_Minify_Print::write_header($a, $headers);
1956 }
1957 return false;
1958 } elseif (!file_exists($b)) {
1959 WP_Optimize_Minify_Print::write_header($b, $headers);
1960 }
1961
1962 return false;
1963 }
1964
1965 /**
1966 * Collect all wpo_min JS files and save them to a headers file
1967 *
1968 * @param String $html
1969 * @param String $handle
1970 * @param String $src
1971 *
1972 * @return String
1973 */
1974 public function collect_js_preload_headers($html, $handle, $src) {
1975 if (false !== strpos($src, 'assets/wpo-minify')) {
1976 if (!in_array($src, $this->collect_preload_js)) {
1977 $this->collect_preload_js[] = $src;
1978 }
1979 }
1980 return $html . "\n";
1981 }
1982
1983 /**
1984 * Collect all wpo_min CSS files and save them to a headers file
1985 * this function intercepts the styles that are enqueued
1986 * take note that if the css is loaded ASYNC, it's not enqueued
1987 *
1988 * @param string $html - Html string
1989 * @param string $handle - Handle
1990 * @param string $src - The CSS file path
1991 *
1992 * @return string
1993 */
1994 public function collect_css_preload_headers($html, $handle, $src) {
1995 if (false !== strpos($src, 'assets/wpo-minify')) {
1996 if (!in_array($src, $this->collect_preload_css)) {
1997 $this->collect_preload_css[] = $src;
1998 }
1999 }
2000 return $html . "\n";
2001 }
2002
2003 /**
2004 * Get current headers file for the url
2005 *
2006 * @return bool|string
2007 */
2008 private function get_preload_headers() {
2009
2010 // always false for admin pages
2011 if (is_admin()) return false;
2012
2013 $enabled_css_preload = $this->options['enabled_css_preload'];
2014 $enabled_js_preload = $this->options['enabled_js_preload'];
2015
2016 if (!$enabled_css_preload && !$enabled_js_preload) {
2017 return false;
2018 }
2019
2020 // get host with multisite support and query strings
2021 $host = htmlentities($_SERVER['SERVER_NAME']);
2022 if (empty($host)) {
2023 $host = htmlentities($_SERVER['HTTP_HOST']);
2024 }
2025 $request_query = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
2026 $request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
2027
2028 // get cache path
2029 $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path();
2030 $header_dir = $cache_path['headerdir'];
2031 $cache_file_base = $header_dir . '/';
2032
2033 // possible cache file locations
2034 $b = $cache_file_base . md5("$host-$request_uri").'.header';
2035 $a = $cache_file_base . md5("$host-$request_uri$request_query").'.header';
2036
2037 // reset file cache
2038 clearstatcache();
2039
2040 // return header files or fallback
2041 if ($b == $a && file_exists($a)) {
2042 return file_get_contents($a);
2043 }
2044 if ($b != $a && file_exists($b)) {
2045 return file_get_contents($b);
2046 }
2047
2048 return false;
2049 }
2050
2051 /**
2052 * Add pre-connect and pre-load headers
2053 *
2054 * @return void
2055 */
2056 public function extra_preload_headers() {
2057 if (!$this->run_on_page('extra_preload_headers')) return;
2058
2059 // fetch headers
2060 $pre_connect = array_map('trim', explode("\n", trim($this->options['hpreconnect'])));
2061
2062 // preload
2063 if (is_array($pre_connect) && count($pre_connect) > 0) {
2064 foreach ($pre_connect as $url) {
2065 if (!empty($url) && filter_var($url, FILTER_VALIDATE_URL)) {
2066 header("Link: <$url>; rel=preconnect", false);
2067 }
2068 }
2069 }
2070
2071 $headers = $this->get_preload_headers();
2072 if (false != $headers) {
2073 $nh = array_map('trim', explode("\n", $headers));
2074 foreach ($nh as $h) {
2075 if (!empty($h)) {
2076 header($h, false);
2077 }
2078 }
2079 }
2080 }
2081
2082 /**
2083 * Includes dependency files
2084 */
2085 private function include_dependencies() {
2086 if (!class_exists('WP_Optimize_Minify_Functions')) {
2087 include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-functions.php';
2088 }
2089
2090 if (!class_exists('WP_Optimize_Minify_Print')) {
2091 include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-print.php';
2092 }
2093
2094 if (!class_exists('WP_Optimize_Minify_Fonts')) {
2095 include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-fonts.php';
2096 }
2097 }
2098
2099 /**
2100 * Handles emoji removal
2101 */
2102 private function disable_emojis() {
2103 WP_Optimize_Minify_Functions::disable_wp_emojicons();
2104 add_filter('tiny_mce_plugins', array('WP_Optimize_Minify_Functions', 'disable_emojis_tinymce' ));
2105 }
2106
2107 /**
2108 * Handles header meta information removal
2109 */
2110 private function remove_header_meta_info() {
2111 // no resource hints, generator tag, shortlinks, manifest link, etc
2112 remove_action('wp_head', 'wp_resource_hints', 2);
2113 remove_action('wp_head', 'wp_generator');
2114 remove_action('template_redirect', 'wp_shortlink_header', 11);
2115 remove_action('wp_head', 'wlwmanifest_link');
2116 remove_action('wp_head', 'rsd_link');
2117 remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0);
2118 remove_action('wp_head', 'feed_links', 2);
2119 remove_action('wp_head', 'feed_links_extra', 3);
2120 WP_Optimize_Minify_Functions::remove_redundant_shortlink();
2121 }
2122
2123 /**
2124 * Handles javascript processing
2125 */
2126 private function process_js() {
2127 add_action('wp_print_scripts', array($this, 'process_header_scripts'), PHP_INT_MAX);
2128 add_action('wp_print_footer_scripts', array($this, 'process_footer_scripts'), 9);
2129
2130 // Defer JS
2131 add_filter('script_loader_tag', array($this, 'defer_js'), 10, 3);
2132
2133 // Preloading
2134 if ($this->options['enabled_js_preload']) {
2135 add_filter('script_loader_tag', array($this, 'collect_js_preload_headers'), PHP_INT_MAX, 3);
2136 }
2137
2138 // add the LoadAsync JavaScript function
2139 $async_js = trim($this->options['async_js']) ? array_map('trim', explode("\n", trim($this->options['async_js']))) : array();
2140 if (count($async_js) > 0
2141 || ( 'all' === $this->options['enable_defer_js'] && 'async_using_js' === $this->options['defer_js_type'] )
2142 ) {
2143 add_action('wp_head', array('WP_Optimize_Minify_Print', 'add_load_async'), 0);
2144 }
2145 }
2146
2147 /**
2148 * Handles css processing
2149 */
2150 private function process_css() {
2151 add_action('wp_head', array($this, 'add_critical_path'), 2);
2152
2153 // merge, if inline is selected but prevent optimization for these locations
2154 if ($this->options['inline_css']) {
2155 // this prints the styles (not fonts) and checks the 'colllect google fonts'
2156 add_filter('style_loader_tag', array($this, 'inline_css'), PHP_INT_MAX, 4);
2157 // this prints the google fonts
2158 add_action('wp_print_styles', array($this, 'add_google_fonts_merged'), PHP_INT_MAX);
2159 add_action('wp_print_footer_scripts', array($this, 'add_google_fonts_merged'), PHP_INT_MAX);
2160 }
2161 // Preloading
2162 if ($this->options['enabled_css_preload']) {
2163 add_filter('style_loader_tag', array($this, 'collect_css_preload_headers'), PHP_INT_MAX, 3);
2164 }
2165 // Optimize the css and collect the google fonts for merging
2166 add_action('wp_print_styles', array($this, 'process_header_css'), PHP_INT_MAX);
2167 add_action('wp_print_footer_scripts', array($this, 'process_footer_css'), 9);
2168
2169 /**
2170 * Filters whether or not to ignore the order of the CSS files, and group them by media type
2171 *
2172 * @param boolean $maintain_css_order
2173 * @return boolean
2174 * @default true
2175 */
2176 if (!apply_filters('wpo_minify_maintain_css_order', true)) {
2177 // Reorder stylesheets
2178 add_filter('wpo_minify_stylesheets', array($this, 'order_stylesheets_per_media_type'), 10);
2179 }
2180 }
2181
2182 /**
2183 * Determines whether we should minify html or not
2184 */
2185 private function should_process_html() {
2186 return $this->options['html_minification'] && !is_admin() && $this->is_cache_preload();
2187 }
2188
2189 /**
2190 * Determines whether to use loadCSS polyfill or not
2191 */
2192 private function should_use_loadCSS() {
2193 return $this->options['loadcss']
2194 || 'async' === $this->options['fawesome_method']
2195 || 'async' === $this->options['gfonts_method'];
2196 }
2197
2198 /**
2199 * Removes query string from static assets
2200 */
2201 private function remove_query_string_from_static_assets() {
2202 add_filter('style_loader_src', array('WP_Optimize_Minify_Functions', 'remove_cssjs_ver'), 10, 2);
2203 }
2204 }
2205