PluginProbe
Autoptimize / 3.1.12
Autoptimize v3.1.12
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / autoptimizeCriticalCSSCore.php

autoptimizeCriticalCSSCore.php in Autoptimize 3.1.12, at classes/autoptimizeCriticalCSSCore.php

665 lines 30.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Critical CSS Core logic:
4 * gets called by AO core, checks the rules and if a matching rule is found returns the associated CCSS.
5 */
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit;
9 }
10
11 class autoptimizeCriticalCSSCore {
12 /**
13 * Critical CSS page types.
14 *
15 * @var array
16 */
17 protected $_types = null;
18
19 /**
20 * Critical CSS object.
21 *
22 * @var object
23 */
24 protected $criticalcss;
25
26 public function __construct() {
27 $this->criticalcss = autoptimize()->criticalcss();
28 $this->run();
29 }
30
31 public function run() {
32 $css_defer = $this->criticalcss->get_option( 'css_defer' );
33 $deferjquery = $this->criticalcss->get_option( 'deferjquery' );
34 $unloadccss = $this->criticalcss->get_option( 'unloadccss' );
35
36 if ( ! $css_defer ) {
37 return;
38 }
39
40 // add all filters to do CCSS
41 // Set AO behavior: disable minification to avoid double minifying and caching.
42 add_filter( 'autoptimize_filter_css_critcss_minify', '__return_false' );
43 add_filter( 'autoptimize_filter_css_defer_inline', array( $this, 'ao_ccss_frontend' ), 10, 1 );
44
45 // Add the action to enqueue jobs for CriticalCSS cron.
46 if ( $this->criticalcss->is_api_active() ) {
47 add_action( 'autoptimize_action_css_hash', array( $this->criticalcss, 'enqueue' ), 10, 1 );
48 }
49
50 // conditionally add the filter to defer jquery and others but only if not done so in autoptimizeScripts.
51 $_native_defer = false;
52 if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_not_aggregate' ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_inline' ) ) {
53 $_native_defer = true;
54 }
55 if ( $deferjquery && ! $_native_defer ) {
56 add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
57 }
58
59 // conditionally add filter to unload the CCSS.
60 if ( $unloadccss ) {
61 add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_unloadccss' ), 12, 1 );
62 }
63
64 // Order paths by length, as longest ones have greater priority in the rules.
65 $rules = $this->criticalcss->get_option( 'rules' );
66 if ( ! empty( $rules['paths'] ) ) {
67 $keys = array_map( 'strlen', array_keys( $rules['paths'] ) );
68 array_multisort( $keys, SORT_DESC, $rules['paths'] );
69 // TODO: Not sure what we're doing here. Sorted the $keys,
70 // but they don't seem to be used anywhere.
71 }
72
73 // Add an array with default WordPress's conditional tags
74 // NOTE: these tags are sorted.
75 $this->_types = $this->get_ao_ccss_core_types();
76
77 // Extend conditional tags on plugin initalization.
78 add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );
79
80 // When autoptimize cache is cleared, also clear transient cache for page templates.
81 add_action( 'autoptimize_action_cachepurged', array( $this, 'ao_ccss_clear_page_tpl_cache' ), 10, 0 );
82 }
83
84 public function ao_ccss_frontend( $inlined ) {
85 // Apply CriticalCSS to frontend pages
86 // Attach types and settings arrays.
87 $rules = $this->criticalcss->get_option( 'rules' );
88 $additional = $this->criticalcss->get_option( 'additional' );
89 $loggedin = $this->criticalcss->get_option( 'loggedin' );
90 $debug = $this->criticalcss->get_option( 'debug' );
91 $no_ccss = '';
92 $additional = autoptimizeStyles::sanitize_css( $additional );
93
94 // Only if keystatus is OK and option to add CCSS for logged on users is on or user is not logged in.
95 if ( $loggedin || ! is_user_logged_in() ) {
96 // Check for a valid CriticalCSS based on path to return its contents.
97 $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
98 if ( ! empty( $rules['paths'] ) ) {
99 foreach ( $rules['paths'] as $path => $rule ) {
100 // explicit match OR partial match if MANUAL rule.
101 if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ( $req_path == $path || urldecode( $req_path ) == $path || ( apply_filters( 'autoptimize_filter_ccss_core_path_partial_match', true ) && false == $rule['hash'] && false != $rule['file'] && strpos( $req_path, str_replace( site_url(), '', $path ) ) !== false ) ) ) {
102 if ( file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
103 $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
104 if ( 'none' != $_ccss_contents ) {
105 if ( $debug ) {
106 $_ccss_contents = '/* PATH: ' . $path . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
107 }
108 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
109 } else {
110 if ( $debug ) {
111 $this->criticalcss->log( 'Path based rule with value "none" found.', 3 );
112 }
113 $no_ccss = 'none';
114 }
115 }
116 }
117 }
118 }
119
120 // Check for a valid CriticalCSS based on conditional tags to return its contents.
121 if ( ! empty( $rules['types'] ) && 'none' !== $no_ccss ) {
122 // order types-rules by the order of the original $ao_ccss_types array so as not to depend on the order in which rules were added.
123 $rules['types'] = array_replace( array_intersect_key( array_flip( $this->_types ), $rules['types'] ), $rules['types'] );
124 $is_front_page = is_front_page();
125
126 foreach ( $rules['types'] as $type => $rule ) {
127 if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && in_array( $type, $this->_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
128 $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
129 if ( $is_front_page && 'is_front_page' == $type ) {
130 if ( 'none' != $_ccss_contents ) {
131 if ( $debug ) {
132 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
133 }
134 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
135 } else {
136 if ( $debug ) {
137 $this->criticalcss->log( 'Conditional rule for is_front_page with value "none" found.', 3 );
138 }
139 $no_ccss = 'none';
140 }
141 } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
142 if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
143 if ( 'none' != $_ccss_contents ) {
144 if ( $debug ) {
145 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
146 }
147 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
148 } else {
149 if ( $debug ) {
150 $this->criticalcss->log( 'Conditional rule custom_post with value "none" found.', 3 );
151 }
152 $no_ccss = 'none';
153 }
154 }
155 } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
156 if ( is_page_template( substr( $type, 9 ) ) ) {
157 if ( 'none' != $_ccss_contents ) {
158 if ( $debug ) {
159 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
160 }
161 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
162 } else {
163 if ( $debug ) {
164 $this->criticalcss->log( 'Conditional rule for template with value "none" found.', 3 );
165 }
166 $no_ccss = 'none';
167 }
168 }
169 } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ! $is_front_page ) {
170 // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
171 // but we have to remove the prefix for the non-core ones for them to function.
172 $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
173 if ( function_exists( $type ) && call_user_func( $type ) ) {
174 if ( 'none' != $_ccss_contents ) {
175 if ( $debug ) {
176 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
177 }
178 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
179 } else {
180 if ( $debug ) {
181 $this->criticalcss->log( 'Conditional rule for ' . $type . ' with value "none" found.', 3 );
182 }
183 $no_ccss = 'none';
184 }
185 }
186 }
187 }
188 }
189 }
190 }
191
192 // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
193 // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
194 if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
195 if ( $debug ) {
196 $this->criticalcss->log( 'Using default "above the fold" CSS.', 3 );
197 }
198 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $additional );
199 } else {
200 if ( $debug ) {
201 $this->criticalcss->log( 'No matching CCSS found, switching to inlining full CSS.', 3 );
202 }
203 add_filter( 'autoptimize_filter_css_inline', '__return_true' );
204 return;
205 }
206 }
207
208 public function ao_ccss_defer_jquery( $in ) {
209 $loggedin = $this->criticalcss->get_option( 'loggedin' );
210
211 // defer all linked and inline JS.
212 if ( ( ! is_user_logged_in() || $loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
213 foreach ( $matches as $match ) {
214 if ( str_replace( apply_filters( 'autoptimize_filter_ccss_core_defer_exclude', array( 'data-noptimize="1"', 'data-cfasync="false"', 'data-pagespeed-no-defer' ) ), '', $match[0] ) !== $match[0] ) {
215 // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
216 continue;
217 } elseif ( '' !== $match[1] && ( ! preg_match( '/<script.* type\s?=.*>/', $match[0] ) || preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $match[0] ) ) ) {
218 // base64-encode and defer all inline JS.
219 $base64_js = '<script defer src="data:text/javascript;base64,' . base64_encode( $match[1] ) . '"></script>';
220 $in = str_replace( $match[0], $base64_js, $in );
221 } elseif ( str_replace( array( ' defer', ' async' ), '', $match[0] ) === $match[0] ) {
222 // and defer linked JS unless already deferred or asynced.
223 $new_match = str_replace( '<script ', '<script defer ', $match[0] );
224 $in = str_replace( $match[0], $new_match, $in );
225 }
226 }
227 }
228 return $in;
229 }
230
231 public function ao_ccss_unloadccss( $html_in ) {
232 // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
233 $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {var el = document.getElementById("aoatfcss"); if(el) el.media = "none";})</script>' );
234
235 if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
236 return $html_in;
237 }
238
239 return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
240 }
241
242 /**
243 * Get the types array.
244 *
245 * @return array|null
246 */
247 public function get_types() {
248 return $this->_types;
249 }
250
251 public function ao_ccss_extend_types() {
252 // Extend contidional tags
253 // Attach the conditional tags array.
254
255 // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
256 if ( empty( $this->_types ) || ! is_array( $this->_types ) ) {
257 $this->_types = $this->get_ao_ccss_core_types();
258 $this->ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
259 }
260
261 // Custom Post Types.
262 $cpts = get_post_types(
263 array(
264 'public' => true,
265 '_builtin' => false,
266 ),
267 'names',
268 'and'
269 );
270 foreach ( $cpts as $cpt ) {
271 array_unshift( $this->_types, 'custom_post_' . $cpt );
272 }
273
274 // Templates.
275 // Transient cache to avoid frequent disk reads.
276 $templates = get_transient( 'autoptimize_ccss_page_templates' );
277 if ( ! $templates ) {
278 $templates = wp_get_theme()->get_page_templates();
279 set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
280 }
281 foreach ( $templates as $tplfile => $tplname ) {
282 array_unshift( $this->_types, 'template_' . $tplfile );
283 }
284
285 // bbPress tags.
286 if ( function_exists( 'is_bbpress' ) ) {
287 $this->_types = array_merge(
288 array(
289 'bbp_is_bbpress',
290 'bbp_is_favorites',
291 'bbp_is_forum_archive',
292 'bbp_is_replies_created',
293 'bbp_is_reply_edit',
294 'bbp_is_reply_move',
295 'bbp_is_search',
296 'bbp_is_search_results',
297 'bbp_is_single_forum',
298 'bbp_is_single_reply',
299 'bbp_is_single_topic',
300 'bbp_is_single_user',
301 'bbp_is_single_user_edit',
302 'bbp_is_single_view',
303 'bbp_is_subscriptions',
304 'bbp_is_topic_archive',
305 'bbp_is_topic_edit',
306 'bbp_is_topic_merge',
307 'bbp_is_topic_split',
308 'bbp_is_topic_tag',
309 'bbp_is_topic_tag_edit',
310 'bbp_is_topics_created',
311 'bbp_is_user_home',
312 'bbp_is_user_home_edit',
313 ),
314 $this->_types
315 );
316 }
317
318 // BuddyPress tags.
319 if ( function_exists( 'is_buddypress' ) ) {
320 $this->_types = array_merge(
321 array(
322 'bp_is_activation_page',
323 'bp_is_activity',
324 'bp_is_blogs',
325 'bp_is_buddypress',
326 'bp_is_change_avatar',
327 'bp_is_create_blog',
328 'bp_is_friend_requests',
329 'bp_is_friends',
330 'bp_is_friends_activity',
331 'bp_is_friends_screen',
332 'bp_is_group_admin_page',
333 'bp_is_group_create',
334 'bp_is_group_forum',
335 'bp_is_group_forum_topic',
336 'bp_is_group_home',
337 'bp_is_group_invites',
338 'bp_is_group_leave',
339 'bp_is_group_members',
340 'bp_is_group_single',
341 'bp_is_groups',
342 'bp_is_messages',
343 'bp_is_messages_compose_screen',
344 'bp_is_messages_conversation',
345 'bp_is_messages_inbox',
346 'bp_is_messages_sentbox',
347 'bp_is_my_activity',
348 'bp_is_my_blogs',
349 'bp_is_notices',
350 'bp_is_profile_edit',
351 'bp_is_register_page',
352 'bp_is_settings_component',
353 'bp_is_user',
354 'bp_is_user_profile',
355 'bp_is_wire',
356 ),
357 $this->_types
358 );
359 }
360
361 // Easy Digital Downloads (EDD) tags.
362 if ( function_exists( 'edd_is_checkout' ) ) {
363 $this->_types = array_merge(
364 array(
365 'edd_is_checkout',
366 'edd_is_failed_transaction_page',
367 'edd_is_purchase_history_page',
368 'edd_is_success_page',
369 ),
370 $this->_types
371 );
372 }
373
374 // WooCommerce tags.
375 if ( class_exists( 'WooCommerce' ) ) {
376 $this->_types = array_merge(
377 array(
378 'woo_is_account_page',
379 'woo_is_cart',
380 'woo_is_checkout',
381 'woo_is_product',
382 'woo_is_product_category',
383 'woo_is_product_tag',
384 'woo_is_shop',
385 'woo_is_wc_endpoint_url',
386 'woo_is_woocommerce',
387 ),
388 $this->_types
389 );
390 }
391 }
392
393 public function get_ao_ccss_core_types() {
394 return array(
395 'is_404',
396 'is_front_page',
397 'is_home',
398 'is_page',
399 'is_single',
400 'is_category',
401 'is_author',
402 'is_archive',
403 'is_search',
404 'is_attachment',
405 'is_sticky',
406 'is_paged',
407 );
408 }
409
410 public function ao_ccss_key_status( $render ) {
411 // Provide key status
412 // Get key and key status.
413 $key = $this->criticalcss->get_option( 'key' );
414 $key_status = $this->criticalcss->get_option( 'keyst' );
415
416 // Prepare returned variables.
417 $key_return = array();
418 $status = false;
419
420 if ( $key && 2 == $key_status ) {
421 // Key exists and its status is valid.
422 // Set valid key status.
423 $status = 'valid';
424 $status_msg = esc_html__( 'Valid' );
425 $color = '#46b450'; // Green.
426 $message = null;
427 } elseif ( $key && 1 == $key_status ) {
428 // Key exists but its validation has failed.
429 // Set invalid key status.
430 $status = 'invalid';
431 $status_msg = esc_html__( 'Invalid' );
432 $color = '#dc3232'; // Red.
433 // Translators: link to criticalcss.com page.
434 $message = sprintf( esc_html__( 'Your API key is invalid. Please enter a valid %1$scriticalcss.com%2$s key.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
435 } elseif ( $key && ! $key_status ) {
436 // Key exists but it has no valid status yet
437 // Perform key validation.
438 $key_check = $this->ao_ccss_key_validation( $key );
439
440 // Key is valid, set valid status.
441 if ( $key_check ) {
442 $status = 'valid';
443 $status_msg = esc_html__( 'Valid' );
444 $color = '#46b450'; // Green.
445 $message = null;
446 } else {
447 // Key is invalid, set invalid status.
448 $status = 'invalid';
449 $status_msg = esc_html__( 'Invalid' );
450 $color = '#dc3232'; // Red.
451 if ( get_option( 'autoptimize_ccss_keyst' ) == 1 ) {
452 // Translators: link to criticalcss.com page.
453 $message = sprintf( esc_html__( 'Your API key is invalid. Please enter a valid %1$scriticalcss.com%2$s key.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
454 } else {
455 $message = esc_html__( 'Something went wrong when checking your API key, make sure you server can communicate with https://criticalcss.com and/ or try again later.', 'autoptimize' );
456 }
457 }
458 } else {
459 // No key nor status
460 // Set no key status.
461 $status = 'nokey';
462 $status_msg = esc_html__( 'None' );
463 $color = '#ffb900'; // Yellow.
464 // Translators: link to criticalcss.com page.
465 $message = sprintf( esc_html__( 'Please enter a valid %1$scriticalcss.com%2$s API key to start.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
466 }
467
468 // Fill returned values.
469 $key_return['status'] = $status;
470 // Provide rendering information if required.
471 if ( $render ) {
472 $key_return['stmsg'] = $status_msg;
473 $key_return['color'] = $color;
474 $key_return['msg'] = $message;
475 }
476
477 // Return key status.
478 return $key_return;
479 }
480
481 public function ao_ccss_key_validation( $key ) {
482 $noptimize = $this->criticalcss->get_option( 'noptimize' );
483
484 // POST a dummy job to criticalcss.com to check for key validation
485 // Prepare home URL for the request.
486 $src_url = get_home_url();
487
488 // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
489 if ( ! empty( $noptimize ) ) {
490 $src_url .= '/?ao_noptirocket=1';
491 } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
492 $src_url .= '/?ao_nolazy=1';
493 }
494
495 $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );
496
497 if ( true !== autoptimizeUtils::is_local_server( parse_url( $src_url, PHP_URL_HOST ) ) ) {
498 // Prepare the request.
499 $url = esc_url_raw( AO_CCSS_API . 'generate' );
500 $args = array(
501 'headers' => apply_filters(
502 'autoptimize_ccss_cron_api_generate_headers',
503 array(
504 'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
505 'Content-type' => 'application/json; charset=utf-8',
506 'Authorization' => 'JWT ' . $key,
507 'Connection' => 'close',
508 )
509 ),
510 // Body must be JSON.
511 'body' => json_encode(
512 apply_filters(
513 'autoptimize_ccss_cron_api_generate_body',
514 array(
515 'url' => $src_url,
516 'aff' => 1,
517 'aocssv' => AO_CCSS_VER,
518 )
519 ),
520 JSON_UNESCAPED_SLASHES
521 ),
522 );
523
524 // Dispatch the request and store its response code.
525 $req = wp_safe_remote_post( $url, $args );
526 $code = wp_remote_retrieve_response_code( $req );
527 $body = json_decode( wp_remote_retrieve_body( $req ), true );
528
529 if ( 200 == $code ) {
530 // Response is OK.
531 // Set key status as valid and log key check.
532 update_option( 'autoptimize_ccss_keyst', 2 );
533 $this->ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
534
535 // extract job-id from $body and put it in the queue as a P job
536 // but only if no jobs and no rules!
537 $queue = $this->criticalcss->get_option( 'queue' );
538 $rules = $this->criticalcss->get_option( 'rules' );
539
540 if ( 0 == count( $queue ) && 0 == count( $rules['types'] ) && 0 == count( $rules['paths'] ) ) {
541 if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
542 $jprops['ljid'] = 'firstrun';
543 $jprops['rtarget'] = 'types|is_front_page';
544 $jprops['ptype'] = 'is_front_page';
545 $jprops['hashes'][] = 'dummyhash';
546 $jprops['hash'] = 'dummyhash';
547 $jprops['file'] = null;
548 $jprops['jid'] = $body['job']['id'];
549 $jprops['jqstat'] = $body['job']['status'];
550 $jprops['jrstat'] = null;
551 $jprops['jvstat'] = null;
552 $jprops['jctime'] = microtime( true );
553 $jprops['jftime'] = null;
554 $queue['/'] = $jprops;
555 $queue_raw = json_encode( $queue );
556 update_option( 'autoptimize_ccss_queue', $queue_raw, false );
557 $this->ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
558 }
559 }
560 return true;
561 } elseif ( 401 == $code ) {
562 // Response is unauthorized
563 // Set key status as invalid and log key check.
564 update_option( 'autoptimize_ccss_keyst', 1 );
565 $this->ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
566 return false;
567 } else {
568 // Response unkown
569 // Log key check attempt.
570 $this->ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
571 if ( ! empty( $body ) ) {
572 $this->ao_ccss_log( print_r( $body, true ), 2 );
573 }
574 if ( is_wp_error( $req ) ) {
575 $this->ao_ccss_log( $req->get_error_message(), 2 );
576 }
577 return false;
578 }
579 } else {
580 // localhost/ private network server, no API check possible.
581 return false;
582 }
583 }
584
585 public function ao_ccss_viewport() {
586 // Get viewport size
587 // Attach viewport option.
588 $viewport = $this->criticalcss->get_option( 'viewport' );
589
590 return array(
591 'w' => ! empty( $viewport['w'] ) ? $viewport['w'] : '',
592 'h' => ! empty( $viewport['h'] ) ? $viewport['h'] : '',
593 );
594 }
595
596 public function ao_ccss_check_contents( $ccss ) {
597 // Perform basic exploit avoidance and CSS validation.
598 if ( ! empty( $ccss ) ) {
599 // Try to avoid code injection.
600 $blocklist = array( '#!/', 'function(', '<script', '<?php', '</style', ' onload=', ' onerror=', ' onmouse', ' onscroll=', ' onclick=' );
601 foreach ( $blocklist as $blocklisted ) {
602 if ( stripos( $ccss, $blocklisted ) !== false ) {
603 $this->ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
604 return false;
605 }
606 }
607
608 // Check for most basics CSS structures.
609 $needlist = array( '{', '}', ':' );
610 foreach ( $needlist as $needed ) {
611 if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
612 $this->ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
613 return false;
614 }
615 }
616 }
617
618 // Return true if file critical CSS is sane.
619 return true;
620 }
621
622 public function ao_ccss_log( $msg, $lvl ) {
623 // Commom logging facility
624 // Attach debug option.
625 $debug = $this->criticalcss->get_option( 'debug' );
626
627 // Prepare log levels, where accepted $lvl are:
628 // 1: II (for info)
629 // 2: EE (for error)
630 // 3: DD (for debug)
631 // Default: UU (for unkown).
632 $level = false;
633 if ( $debug ) {
634 switch ( $lvl ) {
635 case 1:
636 $level = 'II';
637 break;
638 case 2:
639 $level = 'EE';
640 break;
641 case 3:
642 $level = 'DD';
643 break;
644 default:
645 $level = 'UU';
646 }
647 }
648
649 // Prepare and write a log message if there's a valid level.
650 if ( $level ) {
651
652 // Prepare message.
653 $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>'; // @codingStandardsIgnoreLine
654
655 // Write message to log file.
656 error_log( $message, 3, AO_CCSS_LOG );
657 }
658 }
659
660 public function ao_ccss_clear_page_tpl_cache() {
661 // Clears transient cache for page templates.
662 delete_transient( 'autoptimize_ccss_page_templates' );
663 }
664 }
665