PluginProbe
Autoptimize / 3.1.16
Autoptimize v3.1.16
3.1.16 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 All 108 releases
← All changes | classes/autoptimizeCriticalCSSCore.php +297 -238 2.7.5 → 3.1.16 View file →
@@ -8,91 +8,109 @@
8 8 exit;
9 9 }
10 10
11 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 - }
12 + /**
13 + * Critical CSS page types.
14 + *
15 + * @var array
16 + */
17 + protected $_types = null;
18 +
19 + /**
20 + * Critical CSS object.
21 + *
22 + * @var object
23 + */
24 + protected $criticalcss;
20 25
26 + public function __construct() {
27 + $this->criticalcss = autoptimize()->criticalcss();
21 28 $this->run();
22 29 }
23 30
24 31 public function run() {
25 - global $ao_css_defer;
26 - global $ao_ccss_deferjquery;
27 - global $ao_ccss_key;
28 - global $ao_ccss_unloadccss;
32 + $css_defer = $this->criticalcss->get_option( 'css_defer' );
33 + $deferjquery = $this->criticalcss->get_option( 'deferjquery' );
34 + $unloadccss = $this->criticalcss->get_option( 'unloadccss' );
29 35
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 );
36 + if ( ! $css_defer ) {
37 + return;
38 + }
35 39
36 - // Add the action to enqueue jobs for CriticalCSS cron.
37 - add_action( 'autoptimize_action_css_hash', array( 'autoptimizeCriticalCSSEnqueue', 'ao_ccss_enqueue' ), 10, 1 );
40 + // add all filters to do CCSS
41 + // Set AO behavior: disable minification to avoid double minifying and caching.
42 + add_filter( 'autoptimize_filter_css_critcss_minify', '__return_false' );
43 + add_filter( 'autoptimize_filter_css_defer_inline', array( $this, 'ao_ccss_frontend' ), 10, 1 );
38 44
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 - }
45 + // Add the action to enqueue jobs for CriticalCSS cron.
46 + if ( $this->criticalcss->is_api_active() ) {
47 + add_action( 'autoptimize_action_css_hash', array( $this->criticalcss, 'enqueue' ), 10, 1 );
48 + }
43 49
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 - }
50 + // conditionally add the filter to defer jquery and others but only if not done so in autoptimizeScripts.
51 + $_native_defer = false;
52 + if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_not_aggregate' ) && 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_js_defer_inline' ) ) {
53 + $_native_defer = true;
54 + }
55 + if ( $deferjquery && ! $_native_defer ) {
56 + add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_defer_jquery' ), 11, 1 );
57 + }
48 58
49 - // Order paths by length, as longest ones have greater priority in the rules.
50 - if ( ! empty( $ao_ccss_rules['paths'] ) ) {
51 - $keys = array_map( 'strlen', array_keys( $ao_ccss_rules['paths'] ) );
52 - array_multisort( $keys, SORT_DESC, $ao_ccss_rules['paths'] );
53 - }
59 + // conditionally add filter to unload the CCSS.
60 + if ( $unloadccss ) {
61 + add_filter( 'autoptimize_html_after_minify', array( $this, 'ao_ccss_unloadccss' ), 12, 1 );
62 + }
54 63
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();
64 + // Order paths by length, as longest ones have greater priority in the rules.
65 + $rules = $this->criticalcss->get_option( 'rules' );
66 + if ( ! empty( $rules['paths'] ) ) {
67 + $keys = array_map( 'strlen', array_keys( $rules['paths'] ) );
68 + array_multisort( $keys, SORT_DESC, $rules['paths'] );
69 + // TODO: Not sure what we're doing here. Sorted the $keys,
70 + // but they don't seem to be used anywhere.
71 + }
59 72
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' ) );
73 + // Add an array with default WordPress's conditional tags
74 + // NOTE: these tags are sorted.
75 + $this->_types = $this->get_ao_ccss_core_types();
62 76
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 - }
77 + // Extend conditional tags on plugin initalization.
78 + add_action( apply_filters( 'autoptimize_filter_ccss_extend_types_hook', 'init' ), array( $this, 'ao_ccss_extend_types' ) );
79 +
80 + // When autoptimize cache is cleared, also clear transient cache for page templates.
81 + add_action( 'autoptimize_action_cachepurged', array( $this, 'ao_ccss_clear_page_tpl_cache' ), 10, 0 );
66 82 }
67 83
68 84 public function ao_ccss_frontend( $inlined ) {
69 85 // Apply CriticalCSS to frontend pages
70 86 // 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 = '';
87 + $rules = $this->criticalcss->get_option( 'rules' );
88 + $additional = $this->criticalcss->get_option( 'additional' );
89 + $loggedin = $this->criticalcss->get_option( 'loggedin' );
90 + $debug = $this->criticalcss->get_option( 'debug' );
91 + $no_ccss = '';
92 + $additional = autoptimizeStyles::sanitize_css( $additional );
78 93
79 94 // 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() ) ) {
95 + if ( $loggedin || ! is_user_logged_in() ) {
81 96 // Check for a valid CriticalCSS based on path to return its contents.
82 97 $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
83 - if ( ! empty( $ao_ccss_rules['paths'] ) ) {
84 - foreach ( $ao_ccss_rules['paths'] as $path => $rule ) {
98 + if ( ! empty( $rules['paths'] ) ) {
99 + foreach ( $rules['paths'] as $path => $rule ) {
85 100 // 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 ) ) {
101 + if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ( $req_path == $path || urldecode( $req_path ) == $path || ( apply_filters( 'autoptimize_filter_ccss_core_path_partial_match', true ) && false == $rule['hash'] && false != $rule['file'] && strpos( $req_path, str_replace( site_url(), '', $path ) ) !== false ) ) ) {
87 102 if ( file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
88 - $_ccss_contents = file_get_contents( AO_CCSS_DIR . $rule['file'] );
103 + $_ccss_contents = $this->ao_ccss_file_get_contents( AO_CCSS_DIR . $rule['file'] );
89 104 if ( 'none' != $_ccss_contents ) {
90 - if ( $ao_ccss_debug ) {
105 + if ( $debug ) {
91 106 $_ccss_contents = '/* PATH: ' . $path . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
92 107 }
93 - return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
108 + return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
94 109 } else {
110 + if ( $debug ) {
111 + $this->criticalcss->log( 'Path based rule with value "none" found.', 3 );
112 + }
95 113 $no_ccss = 'none';
96 114 }
97 115 }
98 116 }
@@ -99,58 +117,70 @@
99 117 }
100 118 }
101 119
102 120 // Check for a valid CriticalCSS based on conditional tags to return its contents.
103 - if ( ! empty( $ao_ccss_rules['types'] ) && 'none' !== $no_ccss ) {
121 + if ( ! empty( $rules['types'] ) && 'none' !== $no_ccss ) {
104 122 // 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();
123 + $rules['types'] = array_replace( array_intersect_key( array_flip( $this->_types ), $rules['types'] ), $rules['types'] );
124 + $is_front_page = is_front_page();
107 125
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'] );
126 + foreach ( $rules['types'] as $type => $rule ) {
127 + if ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && in_array( $type, $this->_types ) && file_exists( AO_CCSS_DIR . $rule['file'] ) ) {
128 + $_ccss_contents = $this->ao_ccss_file_get_contents( AO_CCSS_DIR . $rule['file'] );
111 129 if ( $is_front_page && 'is_front_page' == $type ) {
112 130 if ( 'none' != $_ccss_contents ) {
113 - if ( $ao_ccss_debug ) {
131 + if ( $debug ) {
114 132 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
115 133 }
116 - return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
134 + return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
117 135 } else {
136 + if ( $debug ) {
137 + $this->criticalcss->log( 'Conditional rule for is_front_page with value "none" found.', 3 );
138 + }
118 139 $no_ccss = 'none';
119 140 }
120 - } elseif ( strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
141 + } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && strpos( $type, 'custom_post_' ) === 0 && ! $is_front_page ) {
121 142 if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
122 143 if ( 'none' != $_ccss_contents ) {
123 - if ( $ao_ccss_debug ) {
144 + if ( $debug ) {
124 145 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
125 146 }
126 - return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
147 + return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
127 148 } else {
149 + if ( $debug ) {
150 + $this->criticalcss->log( 'Conditional rule custom_post with value "none" found.', 3 );
151 + }
128 152 $no_ccss = 'none';
129 153 }
130 154 }
131 - } elseif ( 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
155 + } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && 0 === strpos( $type, 'template_' ) && ! $is_front_page ) {
132 156 if ( is_page_template( substr( $type, 9 ) ) ) {
133 157 if ( 'none' != $_ccss_contents ) {
134 - if ( $ao_ccss_debug ) {
158 + if ( $debug ) {
135 159 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
136 160 }
137 - return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
161 + return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
138 162 } else {
163 + if ( $debug ) {
164 + $this->criticalcss->log( 'Conditional rule for template with value "none" found.', 3 );
165 + }
139 166 $no_ccss = 'none';
140 167 }
141 168 }
142 - } elseif ( ! $is_front_page ) {
169 + } elseif ( ( $this->criticalcss->is_api_active() || $this->criticalcss->is_rule_manual( $rule ) ) && ! $is_front_page ) {
143 170 // all "normal" conditional tags, core + woo + buddypress + edd + bbpress
144 171 // but we have to remove the prefix for the non-core ones for them to function.
145 172 $type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
146 173 if ( function_exists( $type ) && call_user_func( $type ) ) {
147 174 if ( 'none' != $_ccss_contents ) {
148 - if ( $ao_ccss_debug ) {
175 + if ( $debug ) {
149 176 $_ccss_contents = '/* TYPES: ' . $type . ' hash: ' . $rule['hash'] . ' file: ' . $rule['file'] . ' */ ' . $_ccss_contents;
150 177 }
151 - return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $ao_ccss_additional );
178 + return apply_filters( 'autoptimize_filter_ccss_core_ccss', $_ccss_contents . $additional );
152 179 } else {
180 + if ( $debug ) {
181 + $this->criticalcss->log( 'Conditional rule for ' . $type . ' with value "none" found.', 3 );
182 + }
153 183 $no_ccss = 'none';
154 184 }
155 185 }
156 186 }
@@ -161,10 +191,16 @@
161 191
162 192 // Finally, inline the default CriticalCSS if any or else the entire CSS for the page
163 193 // This also applies to logged in users if the option to add CCSS for logged in users has been disabled.
164 194 if ( ! empty( $inlined ) && 'none' !== $no_ccss ) {
165 - return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $ao_ccss_additional );
195 + if ( $debug ) {
196 + $this->criticalcss->log( 'Using default "above the fold" CSS.', 3 );
197 + }
198 + return apply_filters( 'autoptimize_filter_ccss_core_ccss', $inlined . $additional );
166 199 } else {
200 + if ( $debug ) {
201 + $this->criticalcss->log( 'No matching CCSS found, switching to inlining full CSS.', 3 );
202 + }
167 203 add_filter( 'autoptimize_filter_css_inline', '__return_true' );
168 204 return;
169 205 }
170 206 }
@@ -169,11 +205,12 @@
169 205 }
170 206 }
171 207
172 208 public function ao_ccss_defer_jquery( $in ) {
173 - global $ao_ccss_loggedin;
209 + $loggedin = $this->criticalcss->get_option( 'loggedin' );
210 +
174 211 // 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 ) ) {
212 + if ( ( ! is_user_logged_in() || $loggedin ) && preg_match_all( '#<script.*>(.*)</script>#Usmi', $in, $matches, PREG_SET_ORDER ) ) {
176 213 foreach ( $matches as $match ) {
177 214 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 215 // do not touch JS with noptimize/ cfasync/ pagespeed-no-defer flags.
179 216 continue;
@@ -192,22 +229,34 @@
192 229 }
193 230
194 231 public function ao_ccss_unloadccss( $html_in ) {
195 232 // 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>' );
233 + $_unloadccss_js = apply_filters( 'autoptimize_filter_ccss_core_unloadccss_js', '<script>window.addEventListener("load", function(event) {var el = document.getElementById("aoatfcss"); if(el) el.media = "none";})</script>' );
197 234
235 + if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
236 + return $html_in;
237 + }
238 +
198 239 return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
199 240 }
200 241
242 + /**
243 + * Get the types array.
244 + *
245 + * @return array|null
246 + */
247 + public function get_types() {
248 + return $this->_types;
249 + }
250 +
201 251 public function ao_ccss_extend_types() {
202 252 // Extend contidional tags
203 253 // Attach the conditional tags array.
204 - global $ao_ccss_types;
205 254
206 255 // 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 );
256 + if ( empty( $this->_types ) || ! is_array( $this->_types ) ) {
257 + $this->_types = $this->get_ao_ccss_core_types();
258 + $this->ao_ccss_log( 'Empty types array in extend, refetching array with core conditionals.', 3 );
210 259 }
211 260
212 261 // Custom Post Types.
213 262 $cpts = get_post_types(
@@ -218,9 +267,9 @@
218 267 'names',
219 268 'and'
220 269 );
221 270 foreach ( $cpts as $cpt ) {
222 - array_unshift( $ao_ccss_types, 'custom_post_' . $cpt );
271 + array_unshift( $this->_types, 'custom_post_' . $cpt );
223 272 }
224 273
225 274 // Templates.
226 275 // Transient cache to avoid frequent disk reads.
@@ -229,14 +278,14 @@
229 278 $templates = wp_get_theme()->get_page_templates();
230 279 set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
231 280 }
232 281 foreach ( $templates as $tplfile => $tplname ) {
233 - array_unshift( $ao_ccss_types, 'template_' . $tplfile );
282 + array_unshift( $this->_types, 'template_' . $tplfile );
234 283 }
235 284
236 285 // bbPress tags.
237 286 if ( function_exists( 'is_bbpress' ) ) {
238 - $ao_ccss_types = array_merge(
287 + $this->_types = array_merge(
239 288 array(
240 289 'bbp_is_bbpress',
241 290 'bbp_is_favorites',
242 291 'bbp_is_forum_archive',
@@ -260,15 +309,16 @@
260 309 'bbp_is_topic_tag_edit',
261 310 'bbp_is_topics_created',
262 311 'bbp_is_user_home',
263 312 'bbp_is_user_home_edit',
264 - ), $ao_ccss_types
313 + ),
314 + $this->_types
265 315 );
266 316 }
267 317
268 318 // BuddyPress tags.
269 319 if ( function_exists( 'is_buddypress' ) ) {
270 - $ao_ccss_types = array_merge(
320 + $this->_types = array_merge(
271 321 array(
272 322 'bp_is_activation_page',
273 323 'bp_is_activity',
274 324 'bp_is_blogs',
@@ -302,27 +352,29 @@
302 352 'bp_is_settings_component',
303 353 'bp_is_user',
304 354 'bp_is_user_profile',
305 355 'bp_is_wire',
306 - ), $ao_ccss_types
356 + ),
357 + $this->_types
307 358 );
308 359 }
309 360
310 361 // Easy Digital Downloads (EDD) tags.
311 362 if ( function_exists( 'edd_is_checkout' ) ) {
312 - $ao_ccss_types = array_merge(
363 + $this->_types = array_merge(
313 364 array(
314 365 'edd_is_checkout',
315 366 'edd_is_failed_transaction_page',
316 367 'edd_is_purchase_history_page',
317 368 'edd_is_success_page',
318 - ), $ao_ccss_types
369 + ),
370 + $this->_types
319 371 );
320 372 }
321 373
322 374 // WooCommerce tags.
323 375 if ( class_exists( 'WooCommerce' ) ) {
324 - $ao_ccss_types = array_merge(
376 + $this->_types = array_merge(
325 377 array(
326 378 'woo_is_account_page',
327 379 'woo_is_cart',
328 380 'woo_is_checkout',
@@ -331,44 +383,36 @@
331 383 'woo_is_product_tag',
332 384 'woo_is_shop',
333 385 'woo_is_wc_endpoint_url',
334 386 'woo_is_woocommerce',
335 - ), $ao_ccss_types
387 + ),
388 + $this->_types
336 389 );
337 390 }
338 391 }
339 392
340 393 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 - }
394 + return array(
395 + 'is_404',
396 + 'is_front_page',
397 + 'is_home',
398 + 'is_page',
399 + 'is_single',
400 + 'is_category',
401 + 'is_author',
402 + 'is_archive',
403 + 'is_search',
404 + 'is_attachment',
405 + 'is_sticky',
406 + 'is_paged',
407 + );
361 408 }
362 409
363 - public static function ao_ccss_key_status( $render ) {
410 + public function ao_ccss_key_status( $render ) {
364 411 // Provide key status
365 412 // 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;
413 + $key = $this->criticalcss->get_option( 'key' );
414 + $key_status = $this->criticalcss->get_option( 'keyst' );
371 415
372 416 // Prepare returned variables.
373 417 $key_return = array();
374 418 $status = false;
@@ -376,9 +420,9 @@
376 420 if ( $key && 2 == $key_status ) {
377 421 // Key exists and its status is valid.
378 422 // Set valid key status.
379 423 $status = 'valid';
380 - $status_msg = __( 'Valid' );
424 + $status_msg = esc_html__( 'Valid' );
381 425 $color = '#46b450'; // Green.
382 426 $message = null;
383 427 } elseif ( $key && 1 == $key_status ) {
384 428 // Key exists but its validation has failed.
@@ -383,31 +427,33 @@
383 427 } elseif ( $key && 1 == $key_status ) {
384 428 // Key exists but its validation has failed.
385 429 // Set invalid key status.
386 430 $status = 'invalid';
387 - $status_msg = __( 'Invalid' );
431 + $status_msg = esc_html__( 'Invalid' );
388 432 $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' );
433 + // Translators: link to criticalcss.com page.
434 + $message = sprintf( esc_html__( 'Your API key is invalid. Please enter a valid %1$scriticalcss.com%2$s key.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
390 435 } elseif ( $key && ! $key_status ) {
391 436 // Key exists but it has no valid status yet
392 437 // Perform key validation.
393 - $key_check = $self->ao_ccss_key_validation( $key );
438 + $key_check = $this->ao_ccss_key_validation( $key );
394 439
395 440 // Key is valid, set valid status.
396 441 if ( $key_check ) {
397 442 $status = 'valid';
398 - $status_msg = __( 'Valid' );
443 + $status_msg = esc_html__( 'Valid' );
399 444 $color = '#46b450'; // Green.
400 445 $message = null;
401 446 } else {
402 447 // Key is invalid, set invalid status.
403 448 $status = 'invalid';
404 - $status_msg = __( 'Invalid' );
449 + $status_msg = esc_html__( 'Invalid' );
405 450 $color = '#dc3232'; // Red.
406 451 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' );
452 + // Translators: link to criticalcss.com page.
453 + $message = sprintf( esc_html__( 'Your API key is invalid. Please enter a valid %1$scriticalcss.com%2$s key.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
408 454 } 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' );
455 + $message = esc_html__( '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 456 }
411 457 }
412 458 } else {
413 459 // No key nor status
@@ -412,11 +458,12 @@
412 458 } else {
413 459 // No key nor status
414 460 // Set no key status.
415 461 $status = 'nokey';
416 - $status_msg = __( 'None' );
462 + $status_msg = esc_html__( 'None' );
417 463 $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' );
464 + // Translators: link to criticalcss.com page.
465 + $message = sprintf( esc_html__( 'Please enter a valid %1$scriticalcss.com%2$s API key to start.', 'autoptimize' ), '<a href="https://criticalcss.com/?aff=1" target="_blank">', '</a>' );
419 466 }
420 467
421 468 // Fill returned values.
422 469 $key_return['status'] = $status;
@@ -431,9 +478,9 @@
431 478 return $key_return;
432 479 }
433 480
434 481 public function ao_ccss_key_validation( $key ) {
435 - global $ao_ccss_noptimize;
482 + $noptimize = $this->criticalcss->get_option( 'noptimize' );
436 483
437 484 // POST a dummy job to criticalcss.com to check for key validation
438 485 // Prepare home URL for the request.
439 486 $src_url = get_home_url();
@@ -438,127 +485,124 @@
438 485 // Prepare home URL for the request.
439 486 $src_url = get_home_url();
440 487
441 488 // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
442 - if ( ! empty( $ao_ccss_noptimize ) ) {
443 - $src_url .= '?ao_noptirocket=1';
489 + if ( ! empty( $noptimize ) ) {
490 + $src_url .= '/?ao_noptirocket=1';
444 491 } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
445 - $src_url .= '?ao_nolazy=1';
492 + $src_url .= '/?ao_nolazy=1';
446 493 }
447 494
448 495 $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );
449 496
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',
497 + if ( true !== autoptimizeUtils::is_local_server( parse_url( $src_url, PHP_URL_HOST ) ) ) {
498 + // Prepare the request.
499 + $url = esc_url_raw( AO_CCSS_API . 'generate' );
500 + $args = array(
501 + 'headers' => apply_filters(
502 + 'autoptimize_ccss_cron_api_generate_headers',
462 503 array(
463 - 'url' => $src_url,
464 - 'aff' => 1,
465 - 'aocssv' => AO_CCSS_VER,
504 + 'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
505 + 'Content-type' => 'application/json; charset=utf-8',
506 + 'Authorization' => 'JWT ' . $key,
507 + 'Connection' => 'close',
466 508 )
467 - )
468 - ),
469 - );
509 + ),
510 + // Body must be JSON.
511 + 'body' => json_encode(
512 + apply_filters(
513 + 'autoptimize_ccss_cron_api_generate_body',
514 + array(
515 + 'url' => $src_url,
516 + 'aff' => 1,
517 + 'aocssv' => AO_CCSS_VER,
518 + )
519 + ),
520 + JSON_UNESCAPED_SLASHES
521 + ),
522 + );
470 523
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 );
524 + // Dispatch the request and store its response code.
525 + $req = wp_safe_remote_post( $url, $args );
526 + $code = wp_remote_retrieve_response_code( $req );
527 + $body = json_decode( wp_remote_retrieve_body( $req ), true );
475 528
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 );
529 + if ( 200 == $code ) {
530 + // Response is OK.
531 + // Set key status as valid and log key check.
532 + update_option( 'autoptimize_ccss_keyst', 2 );
533 + $this->ao_ccss_log( 'criticalcss.com: API key is valid, updating key status', 3 );
481 534
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;
535 + // extract job-id from $body and put it in the queue as a P job
536 + // but only if no jobs and no rules!
537 + $queue = $this->criticalcss->get_option( 'queue' );
538 + $rules = $this->criticalcss->get_option( 'rules' );
486 539
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';
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 );
540 + if ( ( empty( $queue ) || 0 == count( $queue ) ) && ( empty( $rules ) || ( is_array( $rules ) && 0 == count( $rules['types'] ) && 0 == count( $rules['paths'] ) ) ) ) {
541 + if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
542 + $jprops['ljid'] = 'firstrun';
543 + $jprops['rtarget'] = 'types|is_front_page';
544 + $jprops['ptype'] = 'is_front_page';
545 + $jprops['hashes'][] = 'dummyhash';
546 + $jprops['hash'] = 'dummyhash';
547 + $jprops['file'] = null;
548 + $jprops['jid'] = $body['job']['id'];
549 + $jprops['jqstat'] = $body['job']['status'];
550 + $jprops['jrstat'] = null;
551 + $jprops['jvstat'] = null;
552 + $jprops['jctime'] = microtime( true );
553 + $jprops['jftime'] = null;
554 + $queue['/'] = $jprops;
555 + $queue_raw = json_encode( $queue );
556 + update_option( 'autoptimize_ccss_queue', $queue_raw, false );
557 + $this->ao_ccss_log( 'Created P job for is_front_page based on API key check response.', 3 );
558 + }
505 559 }
560 + return true;
561 + } elseif ( 401 == $code ) {
562 + // Response is unauthorized
563 + // Set key status as invalid and log key check.
564 + update_option( 'autoptimize_ccss_keyst', 1 );
565 + $this->ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
566 + return false;
567 + } else {
568 + // Response unkown
569 + // Log key check attempt.
570 + $this->ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
571 + if ( ! empty( $body ) ) {
572 + $this->ao_ccss_log( print_r( $body, true ), 2 );
573 + }
574 + if ( is_wp_error( $req ) ) {
575 + $this->ao_ccss_log( $req->get_error_message(), 2 );
576 + }
577 + return false;
506 578 }
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 579 } 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 - }
580 + // localhost/ private network server, no API check possible.
524 581 return false;
525 582 }
526 583 }
527 584
528 - public static function ao_ccss_viewport() {
585 + public function ao_ccss_viewport() {
529 586 // Get viewport size
530 587 // Attach viewport option.
531 - global $ao_ccss_viewport;
588 + $viewport = $this->criticalcss->get_option( 'viewport' );
532 589
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;
590 + return array(
591 + 'w' => ! empty( $viewport['w'] ) ? $viewport['w'] : '',
592 + 'h' => ! empty( $viewport['h'] ) ? $viewport['h'] : '',
593 + );
551 594 }
552 595
553 - public static function ao_ccss_check_contents( $ccss ) {
596 + public function ao_ccss_check_contents( $ccss ) {
554 597 // Perform basic exploit avoidance and CSS validation.
598 + // todo; use autoptimizeStyles::check_css which is way stricter? or could that break things??
555 599 if ( ! empty( $ccss ) ) {
556 600 // Try to avoid code injection.
557 - $blocklist = array( '#!/', 'function(', '<script', '<?php' );
601 + $blocklist = array( '#!/', 'function(', '<script', '<?php', '<?=', '</style', 'onload=', 'onerror=', ' onmouse', 'onscroll=', 'onclick=' );
558 602 foreach ( $blocklist as $blocklisted ) {
559 - if ( strpos( $ccss, $blocklisted ) !== false ) {
560 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
603 + if ( stripos( $ccss, $blocklisted ) !== false ) {
604 + $this->ao_ccss_log( 'Critical CSS received contained blocklisted content.', 2 );
561 605 return false;
562 606 }
563 607 }
564 608
@@ -565,9 +609,9 @@
565 609 // Check for most basics CSS structures.
566 610 $needlist = array( '{', '}', ':' );
567 611 foreach ( $needlist as $needed ) {
568 612 if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
569 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
613 + $this->ao_ccss_log( 'Critical CSS received did not seem to contain real CSS.', 2 );
570 614 return false;
571 615 }
572 616 }
573 617 }
@@ -575,12 +619,12 @@
575 619 // Return true if file critical CSS is sane.
576 620 return true;
577 621 }
578 622
579 - public static function ao_ccss_log( $msg, $lvl ) {
623 + public function ao_ccss_log( $msg, $lvl ) {
580 624 // Commom logging facility
581 625 // Attach debug option.
582 - global $ao_ccss_debug;
626 + $debug = $this->criticalcss->get_option( 'debug' );
583 627
584 628 // Prepare log levels, where accepted $lvl are:
585 629 // 1: II (for info)
586 630 // 2: EE (for error)
@@ -586,23 +630,22 @@
586 630 // 2: EE (for error)
587 631 // 3: DD (for debug)
588 632 // Default: UU (for unkown).
589 633 $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 ) {
634 + if ( $debug ) {
635 + switch ( $lvl ) {
636 + case 1:
637 + $level = 'II';
638 + break;
639 + case 2:
640 + $level = 'EE';
641 + break;
642 + case 3:
600 643 $level = 'DD';
601 - }
602 - break;
603 - default:
604 - $level = 'UU';
644 + break;
645 + default:
646 + $level = 'UU';
647 + }
605 648 }
606 649
607 650 // Prepare and write a log message if there's a valid level.
608 651 if ( $level ) {
@@ -607,9 +650,9 @@
607 650 // Prepare and write a log message if there's a valid level.
608 651 if ( $level ) {
609 652
610 653 // Prepare message.
611 - $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>';
654 + $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>'; // @codingStandardsIgnoreLine
612 655
613 656 // Write message to log file.
614 657 error_log( $message, 3, AO_CCSS_LOG );
615 658 }
@@ -614,10 +657,26 @@
614 657 error_log( $message, 3, AO_CCSS_LOG );
615 658 }
616 659 }
617 660
618 - public static function ao_ccss_clear_page_tpl_cache() {
661 + public function ao_ccss_clear_page_tpl_cache() {
619 662 // Clears transient cache for page templates.
620 663 delete_transient( 'autoptimize_ccss_page_templates' );
621 664 }
665 +
666 + public function ao_ccss_file_get_contents( $critcssfile ) {
667 + // Resolve the path to collapse any traversal attempts like '../'
668 + $requested_path = realpath( $critcssfile );
622 669
670 + // Validate: path must exist, must be a .css file, and must remain inside AO_CCSS_DIR
671 + if ( $requested_path && str_starts_with( $requested_path, AO_CCSS_DIR ) && pathinfo( $requested_path, PATHINFO_EXTENSION ) === 'css' ) {
672 + $_ccss_contents = file_get_contents( $requested_path );
673 +
674 + // Sanitize: if contents looks fishy, strip all tags.
675 + if ( false === $this->criticalcss->check_contents( $_ccss_contents ) ) {
676 + $_ccss_contents = autoptimizeStyles::sanitize_css( $_ccss_contents );
677 + }
678 +
679 + return $_ccss_contents;
680 + }
681 + }
623 682 }