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

624 lines 27.2 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 return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
199 }
200
201 public function ao_ccss_extend_types() {
202 // Extend contidional tags
203 // Attach the conditional tags array.
204 global $ao_ccss_types;
205
206 // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
207 if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
208 $ao_ccss_types = get_ao_ccss_core_types();
209 autoptimizeCriticalCSSCore::ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
210 }
211
212 // Custom Post Types.
213 $cpts = get_post_types(
214 array(
215 'public' => true,
216 '_builtin' => false,
217 ),
218 'names',
219 'and'
220 );
221 foreach ( $cpts as $cpt ) {
222 array_unshift( $ao_ccss_types, 'custom_post_' . $cpt );
223 }
224
225 // Templates.
226 // Transient cache to avoid frequent disk reads.
227 $templates = get_transient( 'autoptimize_ccss_page_templates' );
228 if ( ! $templates ) {
229 $templates = wp_get_theme()->get_page_templates();
230 set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
231 }
232 foreach ( $templates as $tplfile => $tplname ) {
233 array_unshift( $ao_ccss_types, 'template_' . $tplfile );
234 }
235
236 // bbPress tags.
237 if ( function_exists( 'is_bbpress' ) ) {
238 $ao_ccss_types = array_merge(
239 array(
240 'bbp_is_bbpress',
241 'bbp_is_favorites',
242 'bbp_is_forum_archive',
243 'bbp_is_replies_created',
244 'bbp_is_reply_edit',
245 'bbp_is_reply_move',
246 'bbp_is_search',
247 'bbp_is_search_results',
248 'bbp_is_single_forum',
249 'bbp_is_single_reply',
250 'bbp_is_single_topic',
251 'bbp_is_single_user',
252 'bbp_is_single_user_edit',
253 'bbp_is_single_view',
254 'bbp_is_subscriptions',
255 'bbp_is_topic_archive',
256 'bbp_is_topic_edit',
257 'bbp_is_topic_merge',
258 'bbp_is_topic_split',
259 'bbp_is_topic_tag',
260 'bbp_is_topic_tag_edit',
261 'bbp_is_topics_created',
262 'bbp_is_user_home',
263 'bbp_is_user_home_edit',
264 ), $ao_ccss_types
265 );
266 }
267
268 // BuddyPress tags.
269 if ( function_exists( 'is_buddypress' ) ) {
270 $ao_ccss_types = array_merge(
271 array(
272 'bp_is_activation_page',
273 'bp_is_activity',
274 'bp_is_blogs',
275 'bp_is_buddypress',
276 'bp_is_change_avatar',
277 'bp_is_create_blog',
278 'bp_is_friend_requests',
279 'bp_is_friends',
280 'bp_is_friends_activity',
281 'bp_is_friends_screen',
282 'bp_is_group_admin_page',
283 'bp_is_group_create',
284 'bp_is_group_forum',
285 'bp_is_group_forum_topic',
286 'bp_is_group_home',
287 'bp_is_group_invites',
288 'bp_is_group_leave',
289 'bp_is_group_members',
290 'bp_is_group_single',
291 'bp_is_groups',
292 'bp_is_messages',
293 'bp_is_messages_compose_screen',
294 'bp_is_messages_conversation',
295 'bp_is_messages_inbox',
296 'bp_is_messages_sentbox',
297 'bp_is_my_activity',
298 'bp_is_my_blogs',
299 'bp_is_notices',
300 'bp_is_profile_edit',
301 'bp_is_register_page',
302 'bp_is_settings_component',
303 'bp_is_user',
304 'bp_is_user_profile',
305 'bp_is_wire',
306 ), $ao_ccss_types
307 );
308 }
309
310 // Easy Digital Downloads (EDD) tags.
311 if ( function_exists( 'edd_is_checkout' ) ) {
312 $ao_ccss_types = array_merge(
313 array(
314 'edd_is_checkout',
315 'edd_is_failed_transaction_page',
316 'edd_is_purchase_history_page',
317 'edd_is_success_page',
318 ), $ao_ccss_types
319 );
320 }
321
322 // WooCommerce tags.
323 if ( class_exists( 'WooCommerce' ) ) {
324 $ao_ccss_types = array_merge(
325 array(
326 'woo_is_account_page',
327 'woo_is_cart',
328 'woo_is_checkout',
329 'woo_is_product',
330 'woo_is_product_category',
331 'woo_is_product_tag',
332 'woo_is_shop',
333 'woo_is_wc_endpoint_url',
334 'woo_is_woocommerce',
335 ), $ao_ccss_types
336 );
337 }
338 }
339
340 public function get_ao_ccss_core_types() {
341 global $ao_ccss_types;
342 if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
343 return array(
344 'is_404',
345 'is_archive',
346 'is_author',
347 'is_category',
348 'is_front_page',
349 'is_home',
350 'is_page',
351 'is_post',
352 'is_search',
353 'is_attachment',
354 'is_single',
355 'is_sticky',
356 'is_paged',
357 );
358 } else {
359 return $ao_ccss_types;
360 }
361 }
362
363 public static function ao_ccss_key_status( $render ) {
364 // Provide key status
365 // Get key and key status.
366 global $ao_ccss_key;
367 global $ao_ccss_keyst;
368 $self = new self();
369 $key = $ao_ccss_key;
370 $key_status = $ao_ccss_keyst;
371
372 // Prepare returned variables.
373 $key_return = array();
374 $status = false;
375
376 if ( $key && 2 == $key_status ) {
377 // Key exists and its status is valid.
378 // Set valid key status.
379 $status = 'valid';
380 $status_msg = __( 'Valid' );
381 $color = '#46b450'; // Green.
382 $message = null;
383 } elseif ( $key && 1 == $key_status ) {
384 // Key exists but its validation has failed.
385 // Set invalid key status.
386 $status = 'invalid';
387 $status_msg = __( 'Invalid' );
388 $color = '#dc3232'; // Red.
389 $message = __( 'Your API key is invalid. Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> key.', 'autoptimize' );
390 } elseif ( $key && ! $key_status ) {
391 // Key exists but it has no valid status yet
392 // Perform key validation.
393 $key_check = $self->ao_ccss_key_validation( $key );
394
395 // Key is valid, set valid status.
396 if ( $key_check ) {
397 $status = 'valid';
398 $status_msg = __( 'Valid' );
399 $color = '#46b450'; // Green.
400 $message = null;
401 } else {
402 // Key is invalid, set invalid status.
403 $status = 'invalid';
404 $status_msg = __( 'Invalid' );
405 $color = '#dc3232'; // Red.
406 if ( get_option( 'autoptimize_ccss_keyst' ) == 1 ) {
407 $message = __( 'Your API key is invalid. Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> key.', 'autoptimize' );
408 } else {
409 $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' );
410 }
411 }
412 } else {
413 // No key nor status
414 // Set no key status.
415 $status = 'nokey';
416 $status_msg = __( 'None' );
417 $color = '#ffb900'; // Yellow.
418 $message = __( 'Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> API key to start.', 'autoptimize' );
419 }
420
421 // Fill returned values.
422 $key_return['status'] = $status;
423 // Provide rendering information if required.
424 if ( $render ) {
425 $key_return['stmsg'] = $status_msg;
426 $key_return['color'] = $color;
427 $key_return['msg'] = $message;
428 }
429
430 // Return key status.
431 return $key_return;
432 }
433
434 public function ao_ccss_key_validation( $key ) {
435 global $ao_ccss_noptimize;
436
437 // POST a dummy job to criticalcss.com to check for key validation
438 // Prepare home URL for the request.
439 $src_url = get_home_url();
440
441 // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
442 if ( ! empty( $ao_ccss_noptimize ) ) {
443 $src_url .= '?ao_noptirocket=1';
444 } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
445 $src_url .= '?ao_nolazy=1';
446 }
447
448 $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );
449
450 // Prepare the request.
451 $url = esc_url_raw( AO_CCSS_API . 'generate' );
452 $args = array(
453 'headers' => array(
454 'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
455 'Content-type' => 'application/json; charset=utf-8',
456 'Authorization' => 'JWT ' . $key,
457 'Connection' => 'close',
458 ),
459 // Body must be JSON.
460 'body' => json_encode(
461 apply_filters( 'autoptimize_ccss_cron_api_generate_body',
462 array(
463 'url' => $src_url,
464 'aff' => 1,
465 'aocssv' => AO_CCSS_VER,
466 )
467 )
468 ),
469 );
470
471 // Dispatch the request and store its response code.
472 $req = wp_safe_remote_post( $url, $args );
473 $code = wp_remote_retrieve_response_code( $req );
474 $body = json_decode( wp_remote_retrieve_body( $req ), true );
475
476 if ( 200 == $code ) {
477 // Response is OK.
478 // Set key status as valid and log key check.
479 update_option( 'autoptimize_ccss_keyst', 2 );
480 autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
481
482 // extract job-id from $body and put it in the queue as a P job
483 // but only if no jobs and no rules!
484 global $ao_ccss_queue;
485 global $ao_ccss_rules;
486
487 if ( 0 == count( $ao_ccss_queue ) && 0 == count( $ao_ccss_rules['types'] ) && 0 == count( $ao_ccss_rules['paths'] ) ) {
488 if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
489 $jprops['ljid'] = 'firstrun';
490 $jprops['rtarget'] = 'types|is_front_page';
491 $jprops['ptype'] = 'is_front_page';
492 $jprops['hashes'][] = 'dummyhash';
493 $jprops['hash'] = 'dummyhash';
494 $jprops['file'] = null;
495 $jprops['jid'] = $body['job']['id'];
496 $jprops['jqstat'] = $body['job']['status'];
497 $jprops['jrstat'] = null;
498 $jprops['jvstat'] = null;
499 $jprops['jctime'] = microtime( true );
500 $jprops['jftime'] = null;
501 $ao_ccss_queue['/'] = $jprops;
502 $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
503 update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
504 autoptimizeCriticalCSSCore::ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
505 }
506 }
507 return true;
508 } elseif ( 401 == $code ) {
509 // Response is unauthorized
510 // Set key status as invalid and log key check.
511 update_option( 'autoptimize_ccss_keyst', 1 );
512 autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
513 return false;
514 } else {
515 // Response unkown
516 // Log key check attempt.
517 autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
518 if ( ! empty( $body ) ) {
519 autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
520 }
521 if ( is_wp_error( $req ) ) {
522 autoptimizeCriticalCSSCore::ao_ccss_log( $req->get_error_message(), 2 );
523 }
524 return false;
525 }
526 }
527
528 public static function ao_ccss_viewport() {
529 // Get viewport size
530 // Attach viewport option.
531 global $ao_ccss_viewport;
532
533 // Prepare viewport array.
534 $viewport = array();
535
536 // Viewport Width.
537 if ( ! empty( $ao_ccss_viewport['w'] ) ) {
538 $viewport['w'] = $ao_ccss_viewport['w'];
539 } else {
540 $viewport['w'] = '';
541 }
542
543 // Viewport Height.
544 if ( ! empty( $ao_ccss_viewport['h'] ) ) {
545 $viewport['h'] = $ao_ccss_viewport['h'];
546 } else {
547 $viewport['h'] = '';
548 }
549
550 return $viewport;
551 }
552
553 public static function ao_ccss_check_contents( $ccss ) {
554 // Perform basic exploit avoidance and CSS validation.
555 if ( ! empty( $ccss ) ) {
556 // Try to avoid code injection.
557 $blocklist = array( '#!/', 'function(', '<script', '<?php' );
558 foreach ( $blocklist as $blocklisted ) {
559 if ( strpos( $ccss, $blocklisted ) !== false ) {
560 autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
561 return false;
562 }
563 }
564
565 // Check for most basics CSS structures.
566 $needlist = array( '{', '}', ':' );
567 foreach ( $needlist as $needed ) {
568 if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
569 autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
570 return false;
571 }
572 }
573 }
574
575 // Return true if file critical CSS is sane.
576 return true;
577 }
578
579 public static function ao_ccss_log( $msg, $lvl ) {
580 // Commom logging facility
581 // Attach debug option.
582 global $ao_ccss_debug;
583
584 // Prepare log levels, where accepted $lvl are:
585 // 1: II (for info)
586 // 2: EE (for error)
587 // 3: DD (for debug)
588 // Default: UU (for unkown).
589 $level = false;
590 switch ( $lvl ) {
591 case 1:
592 $level = 'II';
593 break;
594 case 2:
595 $level = 'EE';
596 break;
597 case 3:
598 // Output debug messages only if debug mode is enabled.
599 if ( $ao_ccss_debug ) {
600 $level = 'DD';
601 }
602 break;
603 default:
604 $level = 'UU';
605 }
606
607 // Prepare and write a log message if there's a valid level.
608 if ( $level ) {
609
610 // Prepare message.
611 $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>';
612
613 // Write message to log file.
614 error_log( $message, 3, AO_CCSS_LOG );
615 }
616 }
617
618 public static function ao_ccss_clear_page_tpl_cache() {
619 // Clears transient cache for page templates.
620 delete_transient( 'autoptimize_ccss_page_templates' );
621 }
622
623 }
624