PluginProbe
Autoptimize / 2.8.3
Autoptimize v2.8.3
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 2.8.3, at classes/autoptimizeCriticalCSSCore.php

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