PluginProbe
Autoptimize / 3.0.3
Autoptimize v3.0.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 3.0.3, at classes/autoptimizeCriticalCSSCore.php

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