Completed
Push — master ( 5b581f...5da188 )
by frank
04:46
created

autoptimizeCriticalCSSCore::ao_ccss_frontend()   F

Complexity

Conditions 41
Paths 28

Size

Total Lines 103

Duplication

Lines 20
Ratio 19.42 %

Importance

Changes 0
Metric Value
cc 41
nc 28
nop 1
dl 20
loc 103
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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