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

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