PluginProbe
Autoptimize / 3.1.6
Autoptimize v3.1.6
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.6, at classes/autoptimizeCriticalCSSCore.php

641 lines 28.4 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 $no_ccss = 'none';
111 }
112 }
113 }
114 }
115 }
116
117 // Check for a valid CriticalCSS based on conditional tags to return its contents.
118 if ( ! empty( $rules['types'] ) && 'none' !== $no_ccss ) {
119 // 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.
120 $rules['types'] = array_replace( array_intersect_key( array_flip( $this->_types ), $rules['types'] ), $rules['types'] );
121 $is_front_page = is_front_page();
122
123 foreach ( $rules['types'] as $type => $rule ) {
124 if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && in_array( $type, $this->_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
125 $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
126 if ( $is_front_page && 'is_front_page' == $type ) {
127 if ( 'none' != $_ccss_contents ) {
128 if ( $debug ) {
129 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
130 }
131 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
132 } else {
133 $no_ccss = 'none';
134 }
135 } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
136 if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
137 if ( 'none' != $_ccss_contents ) {
138 if ( $debug ) {
139 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
140 }
141 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
142 } else {
143 $no_ccss = 'none';
144 }
145 }
146 } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
147 if ( is_page_template( substr( $type, 9 ) ) ) {
148 if ( 'none' != $_ccss_contents ) {
149 if ( $debug ) {
150 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
151 }
152 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
153 } else {
154 $no_ccss = 'none';
155 }
156 }
157 } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ! $is_front_page ) {
158 // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
159 // but we have to remove the prefix for the non-core ones for them to function.
160 $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
161 if ( function_exists( $type ) && call_user_func( $type ) ) {
162 if ( 'none' != $_ccss_contents ) {
163 if ( $debug ) {
164 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
165 }
166 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
167 } else {
168 $no_ccss = 'none';
169 }
170 }
171 }
172 }
173 }
174 }
175 }
176
177 // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
178 // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
179 if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
180 return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $additional );
181 } else {
182 add_filter( 'autoptimize_filter_css_inline', '__return_true' );
183 return;
184 }
185 }
186
187 public function ao_ccss_defer_jquery( $in ) {
188 $loggedin = $this->criticalcss->get_option( 'loggedin' );
189
190 // defer all linked and inline JS.
191 if ( ( ! is_user_logged_in() || $loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
192 foreach ( $matches as $match ) {
193 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] ) {
194 // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
195 continue;
196 } elseif ( '' !== $match[1] && ( ! preg_match( '/<script.* type\s?=.*>/', $match[0] ) || preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $match[0] ) ) ) {
197 // base64-encode and defer all inline JS.
198 $base64_js = '<script defer src="data:text/javascript;base64,' . base64_encode( $match[1] ) . '"></script>';
199 $in = str_replace( $match[0], $base64_js, $in );
200 } elseif ( str_replace( array( ' defer', ' async' ), '', $match[0] ) === $match[0] ) {
201 // and defer linked JS unless already deferred or asynced.
202 $new_match = str_replace( '<script ', '<script defer ', $match[0] );
203 $in = str_replace( $match[0], $new_match, $in );
204 }
205 }
206 }
207 return $in;
208 }
209
210 public function ao_ccss_unloadccss( $html_in ) {
211 // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
212 $_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>' );
213
214 if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
215 return $html_in;
216 }
217
218 return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
219 }
220
221 /**
222 * Get the types array.
223 *
224 * @return array|null
225 */
226 public function get_types() {
227 return $this->_types;
228 }
229
230 public function ao_ccss_extend_types() {
231 // Extend contidional tags
232 // Attach the conditional tags array.
233
234 // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
235 if ( empty( $this->_types ) || ! is_array( $this->_types ) ) {
236 $this->_types = $this->get_ao_ccss_core_types();
237 $this->ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
238 }
239
240 // Custom Post Types.
241 $cpts = get_post_types(
242 array(
243 'public' => true,
244 '_builtin' => false,
245 ),
246 'names',
247 'and'
248 );
249 foreach ( $cpts as $cpt ) {
250 array_unshift( $this->_types, 'custom_post_' . $cpt );
251 }
252
253 // Templates.
254 // Transient cache to avoid frequent disk reads.
255 $templates = get_transient( 'autoptimize_ccss_page_templates' );
256 if ( ! $templates ) {
257 $templates = wp_get_theme()->get_page_templates();
258 set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
259 }
260 foreach ( $templates as $tplfile => $tplname ) {
261 array_unshift( $this->_types, 'template_' . $tplfile );
262 }
263
264 // bbPress tags.
265 if ( function_exists( 'is_bbpress' ) ) {
266 $this->_types = array_merge(
267 array(
268 'bbp_is_bbpress',
269 'bbp_is_favorites',
270 'bbp_is_forum_archive',
271 'bbp_is_replies_created',
272 'bbp_is_reply_edit',
273 'bbp_is_reply_move',
274 'bbp_is_search',
275 'bbp_is_search_results',
276 'bbp_is_single_forum',
277 'bbp_is_single_reply',
278 'bbp_is_single_topic',
279 'bbp_is_single_user',
280 'bbp_is_single_user_edit',
281 'bbp_is_single_view',
282 'bbp_is_subscriptions',
283 'bbp_is_topic_archive',
284 'bbp_is_topic_edit',
285 'bbp_is_topic_merge',
286 'bbp_is_topic_split',
287 'bbp_is_topic_tag',
288 'bbp_is_topic_tag_edit',
289 'bbp_is_topics_created',
290 'bbp_is_user_home',
291 'bbp_is_user_home_edit',
292 ),
293 $this->_types
294 );
295 }
296
297 // BuddyPress tags.
298 if ( function_exists( 'is_buddypress' ) ) {
299 $this->_types = array_merge(
300 array(
301 'bp_is_activation_page',
302 'bp_is_activity',
303 'bp_is_blogs',
304 'bp_is_buddypress',
305 'bp_is_change_avatar',
306 'bp_is_create_blog',
307 'bp_is_friend_requests',
308 'bp_is_friends',
309 'bp_is_friends_activity',
310 'bp_is_friends_screen',
311 'bp_is_group_admin_page',
312 'bp_is_group_create',
313 'bp_is_group_forum',
314 'bp_is_group_forum_topic',
315 'bp_is_group_home',
316 'bp_is_group_invites',
317 'bp_is_group_leave',
318 'bp_is_group_members',
319 'bp_is_group_single',
320 'bp_is_groups',
321 'bp_is_messages',
322 'bp_is_messages_compose_screen',
323 'bp_is_messages_conversation',
324 'bp_is_messages_inbox',
325 'bp_is_messages_sentbox',
326 'bp_is_my_activity',
327 'bp_is_my_blogs',
328 'bp_is_notices',
329 'bp_is_profile_edit',
330 'bp_is_register_page',
331 'bp_is_settings_component',
332 'bp_is_user',
333 'bp_is_user_profile',
334 'bp_is_wire',
335 ),
336 $this->_types
337 );
338 }
339
340 // Easy Digital Downloads (EDD) tags.
341 if ( function_exists( 'edd_is_checkout' ) ) {
342 $this->_types = array_merge(
343 array(
344 'edd_is_checkout',
345 'edd_is_failed_transaction_page',
346 'edd_is_purchase_history_page',
347 'edd_is_success_page',
348 ),
349 $this->_types
350 );
351 }
352
353 // WooCommerce tags.
354 if ( class_exists( 'WooCommerce' ) ) {
355 $this->_types = array_merge(
356 array(
357 'woo_is_account_page',
358 'woo_is_cart',
359 'woo_is_checkout',
360 'woo_is_product',
361 'woo_is_product_category',
362 'woo_is_product_tag',
363 'woo_is_shop',
364 'woo_is_wc_endpoint_url',
365 'woo_is_woocommerce',
366 ),
367 $this->_types
368 );
369 }
370 }
371
372 public function get_ao_ccss_core_types() {
373 return array(
374 'is_404',
375 'is_front_page',
376 'is_home',
377 'is_page',
378 'is_single',
379 'is_category',
380 'is_author',
381 'is_archive',
382 'is_search',
383 'is_attachment',
384 'is_sticky',
385 'is_paged',
386 );
387 }
388
389 public function ao_ccss_key_status( $render ) {
390 // Provide key status
391 // Get key and key status.
392 $key = $this->criticalcss->get_option( 'key' );
393 $key_status = $this->criticalcss->get_option( 'keyst' );
394
395 // Prepare returned variables.
396 $key_return = array();
397 $status = false;
398
399 if ( $key && 2 == $key_status ) {
400 // Key exists and its status is valid.
401 // Set valid key status.
402 $status = 'valid';
403 $status_msg = __( 'Valid' );
404 $color = '#46b450'; // Green.
405 $message = null;
406 } elseif ( $key && 1 == $key_status ) {
407 // Key exists but its validation has failed.
408 // Set invalid key status.
409 $status = 'invalid';
410 $status_msg = __( 'Invalid' );
411 $color = '#dc3232'; // Red.
412 $message = __( 'Your API key is invalid. Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> key.', 'autoptimize' );
413 } elseif ( $key && ! $key_status ) {
414 // Key exists but it has no valid status yet
415 // Perform key validation.
416 $key_check = $this->ao_ccss_key_validation( $key );
417
418 // Key is valid, set valid status.
419 if ( $key_check ) {
420 $status = 'valid';
421 $status_msg = __( 'Valid' );
422 $color = '#46b450'; // Green.
423 $message = null;
424 } else {
425 // Key is invalid, set invalid status.
426 $status = 'invalid';
427 $status_msg = __( 'Invalid' );
428 $color = '#dc3232'; // Red.
429 if ( get_option( 'autoptimize_ccss_keyst' ) == 1 ) {
430 $message = __( 'Your API key is invalid. Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> key.', 'autoptimize' );
431 } else {
432 $message = __( 'Something went wrong when checking your API key, make sure you server can communicate with https://criticalcss.com and/ or try again later.', 'autoptimize' );
433 }
434 }
435 } else {
436 // No key nor status
437 // Set no key status.
438 $status = 'nokey';
439 $status_msg = __( 'None' );
440 $color = '#ffb900'; // Yellow.
441 $message = __( 'Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> API key to start.', 'autoptimize' );
442 }
443
444 // Fill returned values.
445 $key_return['status'] = $status;
446 // Provide rendering information if required.
447 if ( $render ) {
448 $key_return['stmsg'] = $status_msg;
449 $key_return['color'] = $color;
450 $key_return['msg'] = $message;
451 }
452
453 // Return key status.
454 return $key_return;
455 }
456
457 public function ao_ccss_key_validation( $key ) {
458 $noptimize = $this->criticalcss->get_option( 'noptimize' );
459
460 // POST a dummy job to criticalcss.com to check for key validation
461 // Prepare home URL for the request.
462 $src_url = get_home_url();
463
464 // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
465 if ( ! empty( $noptimize ) ) {
466 $src_url .= '/?ao_noptirocket=1';
467 } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
468 $src_url .= '/?ao_nolazy=1';
469 }
470
471 $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );
472
473 if ( true !== autoptimizeUtils::is_local_server( parse_url( $src_url, PHP_URL_HOST ) ) ) {
474 // Prepare the request.
475 $url = esc_url_raw( AO_CCSS_API . 'generate' );
476 $args = array(
477 'headers' => apply_filters(
478 'autoptimize_ccss_cron_api_generate_headers',
479 array(
480 'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
481 'Content-type' => 'application/json; charset=utf-8',
482 'Authorization' => 'JWT ' . $key,
483 'Connection' => 'close',
484 )
485 ),
486 // Body must be JSON.
487 'body' => json_encode(
488 apply_filters(
489 'autoptimize_ccss_cron_api_generate_body',
490 array(
491 'url' => $src_url,
492 'aff' => 1,
493 'aocssv' => AO_CCSS_VER,
494 )
495 ),
496 JSON_UNESCAPED_SLASHES
497 ),
498 );
499
500 // Dispatch the request and store its response code.
501 $req = wp_safe_remote_post( $url, $args );
502 $code = wp_remote_retrieve_response_code( $req );
503 $body = json_decode( wp_remote_retrieve_body( $req ), true );
504
505 if ( 200 == $code ) {
506 // Response is OK.
507 // Set key status as valid and log key check.
508 update_option( 'autoptimize_ccss_keyst', 2 );
509 $this->ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
510
511 // extract job-id from $body and put it in the queue as a P job
512 // but only if no jobs and no rules!
513 $queue = $this->criticalcss->get_option( 'queue' );
514 $rules = $this->criticalcss->get_option( 'rules' );
515
516 if ( 0 == count( $queue ) && 0 == count( $rules['types'] ) && 0 == count( $rules['paths'] ) ) {
517 if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
518 $jprops['ljid'] = 'firstrun';
519 $jprops['rtarget'] = 'types|is_front_page';
520 $jprops['ptype'] = 'is_front_page';
521 $jprops['hashes'][] = 'dummyhash';
522 $jprops['hash'] = 'dummyhash';
523 $jprops['file'] = null;
524 $jprops['jid'] = $body['job']['id'];
525 $jprops['jqstat'] = $body['job']['status'];
526 $jprops['jrstat'] = null;
527 $jprops['jvstat'] = null;
528 $jprops['jctime'] = microtime( true );
529 $jprops['jftime'] = null;
530 $queue['/'] = $jprops;
531 $queue_raw = json_encode( $queue );
532 update_option( 'autoptimize_ccss_queue', $queue_raw, false );
533 $this->ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
534 }
535 }
536 return true;
537 } elseif ( 401 == $code ) {
538 // Response is unauthorized
539 // Set key status as invalid and log key check.
540 update_option( 'autoptimize_ccss_keyst', 1 );
541 $this->ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
542 return false;
543 } else {
544 // Response unkown
545 // Log key check attempt.
546 $this->ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
547 if ( ! empty( $body ) ) {
548 $this->ao_ccss_log( print_r( $body, true ), 2 );
549 }
550 if ( is_wp_error( $req ) ) {
551 $this->ao_ccss_log( $req->get_error_message(), 2 );
552 }
553 return false;
554 }
555 } else {
556 // localhost/ private network server, no API check possible.
557 return false;
558 }
559 }
560
561 public function ao_ccss_viewport() {
562 // Get viewport size
563 // Attach viewport option.
564 $viewport = $this->criticalcss->get_option( 'viewport' );
565
566 return array(
567 'w' => ! empty( $viewport['w'] ) ? $viewport['w'] : '',
568 'h' => ! empty( $viewport['h'] ) ? $viewport['h'] : '',
569 );
570 }
571
572 public function ao_ccss_check_contents( $ccss ) {
573 // Perform basic exploit avoidance and CSS validation.
574 if ( ! empty( $ccss ) ) {
575 // Try to avoid code injection.
576 $blocklist = array( '#!/', 'function(', '<script', '<?php', '</style', ' onload=', ' onerror=', ' onmouse', ' onscroll=', ' onclick=' );
577 foreach ( $blocklist as $blocklisted ) {
578 if ( stripos( $ccss, $blocklisted ) !== false ) {
579 $this->ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
580 return false;
581 }
582 }
583
584 // Check for most basics CSS structures.
585 $needlist = array( '{', '}', ':' );
586 foreach ( $needlist as $needed ) {
587 if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
588 $this->ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
589 return false;
590 }
591 }
592 }
593
594 // Return true if file critical CSS is sane.
595 return true;
596 }
597
598 public function ao_ccss_log( $msg, $lvl ) {
599 // Commom logging facility
600 // Attach debug option.
601 $debug = $this->criticalcss->get_option( 'debug' );
602
603 // Prepare log levels, where accepted $lvl are:
604 // 1: II (for info)
605 // 2: EE (for error)
606 // 3: DD (for debug)
607 // Default: UU (for unkown).
608 $level = false;
609 if ( $debug ) {
610 switch ( $lvl ) {
611 case 1:
612 $level = 'II';
613 break;
614 case 2:
615 $level = 'EE';
616 break;
617 case 3:
618 $level = 'DD';
619 break;
620 default:
621 $level = 'UU';
622 }
623 }
624
625 // Prepare and write a log message if there's a valid level.
626 if ( $level ) {
627
628 // Prepare message.
629 $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>'; // @codingStandardsIgnoreLine
630
631 // Write message to log file.
632 error_log( $message, 3, AO_CCSS_LOG );
633 }
634 }
635
636 public function ao_ccss_clear_page_tpl_cache() {
637 // Clears transient cache for page templates.
638 delete_transient( 'autoptimize_ccss_page_templates' );
639 }
640 }
641