autoptimizeCriticalCSSCore::run()   B
last analyzed

Complexity

Conditions 7
Paths 9

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 9
nop 0
dl 0
loc 43
rs 8.2986
c 0
b 0
f 0
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 View Code Duplication
    public function __construct()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
13
    {
14
        // fetch all options at once and populate them individually explicitely as globals.
15
        $all_options = autoptimizeCriticalCSSBase::fetch_options();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $all_options is correct as \autoptimizeCriticalCSSBase::fetch_options() (which targets autoptimizeCriticalCSSBase::fetch_options()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
16
        foreach ( $all_options as $_option => $_value ) {
0 ignored issues
show
Bug introduced by
The expression $all_options of type null is not traversable.
Loading history...
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'] ) ) {
0 ignored issues
show
Bug introduced by
The variable $ao_ccss_rules seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
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
78
        $no_ccss = '';
79
        $ao_ccss_additional = autoptimizeStyles::sanitize_css( $ao_ccss_additional );
80
81
        // Only if keystatus is OK and option to add CCSS for logged on users is on or user is not logged in.
82
        if ( ( $ao_ccss_keyst && 2 == $ao_ccss_keyst ) && ( $ao_ccss_loggedin || ! is_user_logged_in() ) ) {
83
            // Check for a valid CriticalCSS based on path to return its contents.
84
            $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
85
            if ( ! empty( $ao_ccss_rules['paths'] ) ) {
86
                foreach ( $ao_ccss_rules['paths'] as $path => $rule ) {
87
                    // explicit match OR partial match if MANUAL rule.
88
                    if ( $req_path == $path || urldecode( $req_path ) == $path || ( false == $rule['hash'] && false != $rule['file'] && strpos( $req_path, str_replace( site_url(), '', $path ) ) !== false ) ) {
89
                        if ( file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
90
                            $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
91
                            if ( 'none' != $_ccss_contents ) {
92
                                if ( $ao_ccss_debug ) {
93
                                    $_ccss_contents = '/* PATH: ' . $path . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
94
                                }
95
                                return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
96
                            } else {
97
                                $no_ccss = 'none';
98
                            }
99
                        }
100
                    }
101
                }
102
            }
103
104
            // Check for a valid CriticalCSS based on conditional tags to return its contents.
105
            if ( ! empty( $ao_ccss_rules['types'] ) && 'none' !== $no_ccss ) {
106
                // 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.
107
                $ao_ccss_rules['types'] = array_replace( array_intersect_key( array_flip( $ao_ccss_types ), $ao_ccss_rules['types'] ), $ao_ccss_rules['types'] );
108
                $is_front_page          = is_front_page();
109
110
                foreach ( $ao_ccss_rules['types'] as $type => $rule ) {
111
                    if ( in_array( $type, $ao_ccss_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
112
                        $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
113
                        if ( $is_front_page && 'is_front_page' == $type ) {
114
                            if ( 'none' != $_ccss_contents ) {
115
                                if ( $ao_ccss_debug ) {
116
                                    $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
117
                                }
118
                                return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
119
                            } else {
120
                                $no_ccss = 'none';
121
                            }
122
                        } elseif ( strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
123
                            if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
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 ( 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
134 View Code Duplication
                            if ( is_page_template( substr( $type, 9 ) ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
                                if ( 'none' != $_ccss_contents ) {
136
                                    if ( $ao_ccss_debug ) {
137
                                        $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
138
                                    }
139
                                    return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
140
                                } else {
141
                                    $no_ccss = 'none';
142
                                }
143
                            }
144
                        } elseif ( ! $is_front_page ) {
145
                            // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
146
                            // but we have to remove the prefix for the non-core ones for them to function.
147
                            $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
148 View Code Duplication
                            if ( function_exists( $type ) && call_user_func( $type ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
                                if ( 'none' != $_ccss_contents ) {
150
                                    if ( $ao_ccss_debug ) {
151
                                        $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
152
                                    }
153
                                    return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
154
                                } else {
155
                                    $no_ccss = 'none';
156
                                }
157
                            }
158
                        }
159
                    }
160
                }
161
            }
162
        }
163
164
        // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
165
        // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
166
        if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
167
            return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $ao_ccss_additional );
168
        } else {
169
            add_filter( 'autoptimize_filter_css_inline', '__return_true' );
170
            return;
171
        }
172
    }
173
174
    public function ao_ccss_defer_jquery( $in ) {
175
        global $ao_ccss_loggedin;
176
        // defer all linked and inline JS.
177
        if ( ( ! is_user_logged_in() || $ao_ccss_loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
178
            foreach ( $matches as $match ) {
179
                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] ) {
180
                    // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
181
                    continue;
182
                } elseif ( '' !== $match[1] && ( ! preg_match( '/<script.* type\s?=.*>/', $match[0] ) || preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $match[0] ) ) ) {
183
                    // base64-encode and defer all inline JS.
184
                    $base64_js = '<script defer src="data:text/javascript;base64,' . base64_encode( $match[1] ) . '"></script>';
185
                    $in        = str_replace( $match[0], $base64_js, $in );
186
                } elseif ( str_replace( array( ' defer', ' async' ), '', $match[0] ) === $match[0] ) {
187
                    // and defer linked JS unless already deferred or asynced.
188
                    $new_match = str_replace( '<script ', '<script defer ', $match[0] );
189
                    $in        = str_replace( $match[0], $new_match, $in );
190
                }
191
            }
192
        }
193
        return $in;
194
    }
195
196
    public function ao_ccss_unloadccss( $html_in ) {
197
        // set media attrib of inline CCSS to none at onLoad to avoid it impacting full CSS (rarely needed).
198
        $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {document.getElementById("aoatfcss").media="none";})</script>' );
199
200
        if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
201
            return $html_in;
202
        }
203
204
        return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
205
    }
206
207
    public function ao_ccss_extend_types() {
208
        // Extend contidional tags
209
        // Attach the conditional tags array.
210
        global $ao_ccss_types;
211
212
        // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
213
        if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
214
            $ao_ccss_types = get_ao_ccss_core_types();
215
            autoptimizeCriticalCSSCore::ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
216
        }
217
218
        // Custom Post Types.
219
        $cpts = get_post_types(
220
            array(
221
                'public'   => true,
222
                '_builtin' => false,
223
            ),
224
            'names',
225
            'and'
226
        );
227
        foreach ( $cpts as $cpt ) {
228
            array_unshift( $ao_ccss_types, 'custom_post_' . $cpt );
229
        }
230
231
        // Templates.
232
        // Transient cache to avoid frequent disk reads.
233
        $templates = get_transient( 'autoptimize_ccss_page_templates' );
234
        if ( ! $templates ) {
235
            $templates = wp_get_theme()->get_page_templates();
236
            set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
237
        }
238
        foreach ( $templates as $tplfile => $tplname ) {
239
            array_unshift( $ao_ccss_types, 'template_' . $tplfile );
240
        }
241
242
        // bbPress tags.
243
        if ( function_exists( 'is_bbpress' ) ) {
244
            $ao_ccss_types = array_merge(
245
                array(
246
                    'bbp_is_bbpress',
247
                    'bbp_is_favorites',
248
                    'bbp_is_forum_archive',
249
                    'bbp_is_replies_created',
250
                    'bbp_is_reply_edit',
251
                    'bbp_is_reply_move',
252
                    'bbp_is_search',
253
                    'bbp_is_search_results',
254
                    'bbp_is_single_forum',
255
                    'bbp_is_single_reply',
256
                    'bbp_is_single_topic',
257
                    'bbp_is_single_user',
258
                    'bbp_is_single_user_edit',
259
                    'bbp_is_single_view',
260
                    'bbp_is_subscriptions',
261
                    'bbp_is_topic_archive',
262
                    'bbp_is_topic_edit',
263
                    'bbp_is_topic_merge',
264
                    'bbp_is_topic_split',
265
                    'bbp_is_topic_tag',
266
                    'bbp_is_topic_tag_edit',
267
                    'bbp_is_topics_created',
268
                    'bbp_is_user_home',
269
                    'bbp_is_user_home_edit',
270
                ), $ao_ccss_types
271
            );
272
        }
273
274
        // BuddyPress tags.
275
        if ( function_exists( 'is_buddypress' ) ) {
276
            $ao_ccss_types = array_merge(
277
                array(
278
                    'bp_is_activation_page',
279
                    'bp_is_activity',
280
                    'bp_is_blogs',
281
                    'bp_is_buddypress',
282
                    'bp_is_change_avatar',
283
                    'bp_is_create_blog',
284
                    'bp_is_friend_requests',
285
                    'bp_is_friends',
286
                    'bp_is_friends_activity',
287
                    'bp_is_friends_screen',
288
                    'bp_is_group_admin_page',
289
                    'bp_is_group_create',
290
                    'bp_is_group_forum',
291
                    'bp_is_group_forum_topic',
292
                    'bp_is_group_home',
293
                    'bp_is_group_invites',
294
                    'bp_is_group_leave',
295
                    'bp_is_group_members',
296
                    'bp_is_group_single',
297
                    'bp_is_groups',
298
                    'bp_is_messages',
299
                    'bp_is_messages_compose_screen',
300
                    'bp_is_messages_conversation',
301
                    'bp_is_messages_inbox',
302
                    'bp_is_messages_sentbox',
303
                    'bp_is_my_activity',
304
                    'bp_is_my_blogs',
305
                    'bp_is_notices',
306
                    'bp_is_profile_edit',
307
                    'bp_is_register_page',
308
                    'bp_is_settings_component',
309
                    'bp_is_user',
310
                    'bp_is_user_profile',
311
                    'bp_is_wire',
312
                ), $ao_ccss_types
313
            );
314
        }
315
316
        // Easy Digital Downloads (EDD) tags.
317
        if ( function_exists( 'edd_is_checkout' ) ) {
318
            $ao_ccss_types = array_merge(
319
                array(
320
                    'edd_is_checkout',
321
                    'edd_is_failed_transaction_page',
322
                    'edd_is_purchase_history_page',
323
                    'edd_is_success_page',
324
                ), $ao_ccss_types
325
            );
326
        }
327
328
        // WooCommerce tags.
329
        if ( class_exists( 'WooCommerce' ) ) {
330
            $ao_ccss_types = array_merge(
331
                array(
332
                    'woo_is_account_page',
333
                    'woo_is_cart',
334
                    'woo_is_checkout',
335
                    'woo_is_product',
336
                    'woo_is_product_category',
337
                    'woo_is_product_tag',
338
                    'woo_is_shop',
339
                    'woo_is_wc_endpoint_url',
340
                    'woo_is_woocommerce',
341
                ), $ao_ccss_types
342
            );
343
        }
344
    }
345
346
    public function get_ao_ccss_core_types() {
347
        global $ao_ccss_types;
348
        if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
349
            return array(
350
                'is_404',
351
                'is_archive',
352
                'is_author',
353
                'is_category',
354
                'is_front_page',
355
                'is_home',
356
                'is_page',
357
                'is_post',
358
                'is_search',
359
                'is_attachment',
360
                'is_single',
361
                'is_sticky',
362
                'is_paged',
363
            );
364
        } else {
365
            return $ao_ccss_types;
366
        }
367
    }
368
369
    public static function ao_ccss_key_status( $render ) {
370
        // Provide key status
371
        // Get key and key status.
372
        global $ao_ccss_key;
373
        global $ao_ccss_keyst;
374
        $self       = new self();
375
        $key        = $ao_ccss_key;
376
        $key_status = $ao_ccss_keyst;
377
378
        // Prepare returned variables.
379
        $key_return = array();
380
        $status     = false;
0 ignored issues
show
Unused Code introduced by
$status is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
381
382
        if ( $key && 2 == $key_status ) {
383
            // Key exists and its status is valid.
384
            // Set valid key status.
385
            $status     = 'valid';
386
            $status_msg = __( 'Valid' );
387
            $color      = '#46b450'; // Green.
388
            $message    = null;
389 View Code Duplication
        } elseif ( $key && 1 == $key_status ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
390
            // Key exists but its validation has failed.
391
            // Set invalid key status.
392
            $status     = 'invalid';
393
            $status_msg = __( 'Invalid' );
394
            $color      = '#dc3232'; // Red.
395
            $message    = __( 'Your API key is invalid. Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> key.', 'autoptimize' );
396
        } elseif ( $key && ! $key_status ) {
397
            // Key exists but it has no valid status yet
398
            // Perform key validation.
399
            $key_check = $self->ao_ccss_key_validation( $key );
400
401
            // Key is valid, set valid status.
402
            if ( $key_check ) {
403
                $status     = 'valid';
404
                $status_msg = __( 'Valid' );
405
                $color      = '#46b450'; // Green.
406
                $message    = null;
407
            } else {
408
                // Key is invalid, set invalid status.
409
                $status     = 'invalid';
410
                $status_msg = __( 'Invalid' );
411
                $color      = '#dc3232'; // Red.
412
                if ( get_option( 'autoptimize_ccss_keyst' ) == 1 ) {
413
                    $message = __( 'Your API key is invalid. Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> key.', 'autoptimize' );
414
                } else {
415
                    $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' );
416
                }
417
            }
418 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
419
            // No key nor status
420
            // Set no key status.
421
            $status     = 'nokey';
422
            $status_msg = __( 'None' );
423
            $color      = '#ffb900'; // Yellow.
424
            $message    = __( 'Please enter a valid <a href="https://criticalcss.com/?aff=1" target="_blank">criticalcss.com</a> API key to start.', 'autoptimize' );
425
        }
426
427
        // Fill returned values.
428
        $key_return['status'] = $status;
429
        // Provide rendering information if required.
430
        if ( $render ) {
431
            $key_return['stmsg'] = $status_msg;
432
            $key_return['color'] = $color;
433
            $key_return['msg']   = $message;
434
        }
435
436
        // Return key status.
437
        return $key_return;
438
    }
439
440
    public function ao_ccss_key_validation( $key ) {
441
        global $ao_ccss_noptimize;
442
443
        // POST a dummy job to criticalcss.com to check for key validation
444
        // Prepare home URL for the request.
445
        $src_url = get_home_url();
446
447
        // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
448 View Code Duplication
        if ( ! empty( $ao_ccss_noptimize ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
449
            $src_url .= '?ao_noptirocket=1';
450
        } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
451
            $src_url .= '?ao_nolazy=1';
452
        }
453
454
        $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );
455
456
        // Prepare the request.
457
        $url  = esc_url_raw( AO_CCSS_API . 'generate' );
458
        $args = array(
459
            'headers' => array(
460
                'User-Agent'    => 'Autoptimize v' . AO_CCSS_VER,
461
                'Content-type'  => 'application/json; charset=utf-8',
462
                'Authorization' => 'JWT ' . $key,
463
                'Connection'    => 'close',
464
            ),
465
            // Body must be JSON.
466
            'body'    => json_encode(
467
                apply_filters( 'autoptimize_ccss_cron_api_generate_body',
468
                    array(
469
                        'url'    => $src_url,
470
                        'aff'    => 1,
471
                        'aocssv' => AO_CCSS_VER,
472
                    )
473
                )
474
            ),
475
        );
476
477
        // Dispatch the request and store its response code.
478
        $req  = wp_safe_remote_post( $url, $args );
479
        $code = wp_remote_retrieve_response_code( $req );
480
        $body = json_decode( wp_remote_retrieve_body( $req ), true );
481
482
        if ( 200 == $code ) {
483
            // Response is OK.
484
            // Set key status as valid and log key check.
485
            update_option( 'autoptimize_ccss_keyst', 2 );
486
            autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
487
488
            // extract job-id from $body and put it in the queue as a P job
489
            // but only if no jobs and no rules!
490
            global $ao_ccss_queue;
491
            global $ao_ccss_rules;
492
493
            if ( 0 == count( $ao_ccss_queue ) && 0 == count( $ao_ccss_rules['types'] ) && 0 == count( $ao_ccss_rules['paths'] ) ) {
494
                if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
495
                    $jprops['ljid']     = 'firstrun';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$jprops was never initialized. Although not strictly required by PHP, it is generally a good practice to add $jprops = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
496
                    $jprops['rtarget']  = 'types|is_front_page';
497
                    $jprops['ptype']    = 'is_front_page';
498
                    $jprops['hashes'][] = 'dummyhash';
499
                    $jprops['hash']     = 'dummyhash';
500
                    $jprops['file']     = null;
501
                    $jprops['jid']      = $body['job']['id'];
502
                    $jprops['jqstat']   = $body['job']['status'];
503
                    $jprops['jrstat']   = null;
504
                    $jprops['jvstat']   = null;
505
                    $jprops['jctime']   = microtime( true );
506
                    $jprops['jftime']   = null;
507
                    $ao_ccss_queue['/'] = $jprops;
508
                    $ao_ccss_queue_raw  = json_encode( $ao_ccss_queue );
509
                    update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
510
                    autoptimizeCriticalCSSCore::ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
511
                }
512
            }
513
            return true;
514
        } elseif ( 401 == $code ) {
515
            // Response is unauthorized
516
            // Set key status as invalid and log key check.
517
            update_option( 'autoptimize_ccss_keyst', 1 );
518
            autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
519
            return false;
520
        } else {
521
            // Response unkown
522
            // Log key check attempt.
523
            autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
524
            if ( ! empty( $body ) ) {
525
                autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
526
            }
527
            if ( is_wp_error( $req ) ) {
528
                autoptimizeCriticalCSSCore::ao_ccss_log( $req->get_error_message(), 2 );
529
            }
530
            return false;
531
        }
532
    }
533
534
    public static function ao_ccss_viewport() {
535
        // Get viewport size
536
        // Attach viewport option.
537
        global $ao_ccss_viewport;
538
539
        // Prepare viewport array.
540
        $viewport = array();
541
542
        // Viewport Width.
543
        if ( ! empty( $ao_ccss_viewport['w'] ) ) {
544
            $viewport['w'] = $ao_ccss_viewport['w'];
545
        } else {
546
            $viewport['w'] = '';
547
        }
548
549
        // Viewport Height.
550
        if ( ! empty( $ao_ccss_viewport['h'] ) ) {
551
            $viewport['h'] = $ao_ccss_viewport['h'];
552
        } else {
553
            $viewport['h'] = '';
554
        }
555
556
        return $viewport;
557
    }
558
559
    public static function ao_ccss_check_contents( $ccss ) {
560
        // Perform basic exploit avoidance and CSS validation.
561
        if ( ! empty( $ccss ) ) {
562
            // Try to avoid code injection.
563
            $blocklist = array( '#!/', 'function(', '<script', '<?php' );
564
            foreach ( $blocklist as $blocklisted ) {
565
                if ( strpos( $ccss, $blocklisted ) !== false ) {
566
                    autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
567
                    return false;
568
                }
569
            }
570
571
            // Check for most basics CSS structures.
572
            $needlist = array( '{', '}', ':' );
573
            foreach ( $needlist as $needed ) {
574
                if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
575
                    autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
576
                    return false;
577
                }
578
            }
579
        }
580
581
        // Return true if file critical CSS is sane.
582
        return true;
583
    }
584
585
    public static function ao_ccss_log( $msg, $lvl ) {
586
        // Commom logging facility
587
        // Attach debug option.
588
        global $ao_ccss_debug;
589
590
        // Prepare log levels, where accepted $lvl are:
591
        // 1: II (for info)
592
        // 2: EE (for error)
593
        // 3: DD (for debug)
594
        // Default: UU (for unkown).
595
        $level = false;
596
        switch ( $lvl ) {
597
            case 1:
598
                $level = 'II';
599
                break;
600
            case 2:
601
                $level = 'EE';
602
                break;
603
            case 3:
604
                // Output debug messages only if debug mode is enabled.
605
                if ( $ao_ccss_debug ) {
606
                    $level = 'DD';
607
                }
608
                break;
609
            default:
610
                $level = 'UU';
611
        }
612
613
        // Prepare and write a log message if there's a valid level.
614
        if ( $level ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $level of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
615
616
            // Prepare message.
617
            $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>';
618
619
            // Write message to log file.
620
            error_log( $message, 3, AO_CCSS_LOG );
621
        }
622
    }
623
624
    public static function ao_ccss_clear_page_tpl_cache() {
625
        // Clears transient cache for page templates.
626
        delete_transient( 'autoptimize_ccss_page_templates' );
627
    }
628
629
}
630