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

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