PluginProbe
Autoptimize / trunk
Autoptimize vtrunk
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 +293 -238 2.7.7 → trunk 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,9 +229,9 @@
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
198 235 if ( false !== strpos( $html_in, $_unloadccss_js . '</body>' ) ) {
199 236 return $html_in;
200 237 }
@@ -201,17 +238,25 @@
201 238
202 239 return str_replace( '</body>', $_unloadccss_js . '</body>', $html_in );
203 240 }
204 241
242 + /**
243 + * Get the types array.
244 + *
245 + * @return array|null
246 + */
247 + public function get_types() {
248 + return $this->_types;
249 + }
250 +
205 251 public function ao_ccss_extend_types() {
206 252 // Extend contidional tags
207 253 // Attach the conditional tags array.
208 - global $ao_ccss_types;
209 254
210 255 // in some cases $ao_ccss_types is empty and/or not an array, this should work around that problem.
211 - if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
212 - $ao_ccss_types = get_ao_ccss_core_types();
213 - 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 );
214 259 }
215 260
216 261 // Custom Post Types.
217 262 $cpts = get_post_types(
@@ -222,9 +267,9 @@
222 267 'names',
223 268 'and'
224 269 );
225 270 foreach ( $cpts as $cpt ) {
226 - array_unshift( $ao_ccss_types, 'custom_post_' . $cpt );
271 + array_unshift( $this->_types, 'custom_post_' . $cpt );
227 272 }
228 273
229 274 // Templates.
230 275 // Transient cache to avoid frequent disk reads.
@@ -233,14 +278,14 @@
233 278 $templates = wp_get_theme()->get_page_templates();
234 279 set_transient( 'autoptimize_ccss_page_templates', $templates, HOUR_IN_SECONDS );
235 280 }
236 281 foreach ( $templates as $tplfile => $tplname ) {
237 - array_unshift( $ao_ccss_types, 'template_' . $tplfile );
282 + array_unshift( $this->_types, 'template_' . $tplfile );
238 283 }
239 284
240 285 // bbPress tags.
241 286 if ( function_exists( 'is_bbpress' ) ) {
242 - $ao_ccss_types = array_merge(
287 + $this->_types = array_merge(
243 288 array(
244 289 'bbp_is_bbpress',
245 290 'bbp_is_favorites',
246 291 'bbp_is_forum_archive',
@@ -264,15 +309,16 @@
264 309 'bbp_is_topic_tag_edit',
265 310 'bbp_is_topics_created',
266 311 'bbp_is_user_home',
267 312 'bbp_is_user_home_edit',
268 - ), $ao_ccss_types
313 + ),
314 + $this->_types
269 315 );
270 316 }
271 317
272 318 // BuddyPress tags.
273 319 if ( function_exists( 'is_buddypress' ) ) {
274 - $ao_ccss_types = array_merge(
320 + $this->_types = array_merge(
275 321 array(
276 322 'bp_is_activation_page',
277 323 'bp_is_activity',
278 324 'bp_is_blogs',
@@ -306,27 +352,29 @@
306 352 'bp_is_settings_component',
307 353 'bp_is_user',
308 354 'bp_is_user_profile',
309 355 'bp_is_wire',
310 - ), $ao_ccss_types
356 + ),
357 + $this->_types
311 358 );
312 359 }
313 360
314 361 // Easy Digital Downloads (EDD) tags.
315 362 if ( function_exists( 'edd_is_checkout' ) ) {
316 - $ao_ccss_types = array_merge(
363 + $this->_types = array_merge(
317 364 array(
318 365 'edd_is_checkout',
319 366 'edd_is_failed_transaction_page',
320 367 'edd_is_purchase_history_page',
321 368 'edd_is_success_page',
322 - ), $ao_ccss_types
369 + ),
370 + $this->_types
323 371 );
324 372 }
325 373
326 374 // WooCommerce tags.
327 375 if ( class_exists( 'WooCommerce' ) ) {
328 - $ao_ccss_types = array_merge(
376 + $this->_types = array_merge(
329 377 array(
330 378 'woo_is_account_page',
331 379 'woo_is_cart',
332 380 'woo_is_checkout',
@@ -335,44 +383,36 @@
335 383 'woo_is_product_tag',
336 384 'woo_is_shop',
337 385 'woo_is_wc_endpoint_url',
338 386 'woo_is_woocommerce',
339 - ), $ao_ccss_types
387 + ),
388 + $this->_types
340 389 );
341 390 }
342 391 }
343 392
344 393 public function get_ao_ccss_core_types() {
345 - global $ao_ccss_types;
346 - if ( empty( $ao_ccss_types ) || ! is_array( $ao_ccss_types ) ) {
347 - return array(
348 - 'is_404',
349 - 'is_archive',
350 - 'is_author',
351 - 'is_category',
352 - 'is_front_page',
353 - 'is_home',
354 - 'is_page',
355 - 'is_post',
356 - 'is_search',
357 - 'is_attachment',
358 - 'is_single',
359 - 'is_sticky',
360 - 'is_paged',
361 - );
362 - } else {
363 - return $ao_ccss_types;
364 - }
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 + );
365 408 }
366 409
367 - public static function ao_ccss_key_status( $render ) {
410 + public function ao_ccss_key_status( $render ) {
368 411 // Provide key status
369 412 // Get key and key status.
370 - global $ao_ccss_key;
371 - global $ao_ccss_keyst;
372 - $self = new self();
373 - $key = $ao_ccss_key;
374 - $key_status = $ao_ccss_keyst;
413 + $key = $this->criticalcss->get_option( 'key' );
414 + $key_status = $this->criticalcss->get_option( 'keyst' );
375 415
376 416 // Prepare returned variables.
377 417 $key_return = array();
378 418 $status = false;
@@ -380,9 +420,9 @@
380 420 if ( $key && 2 == $key_status ) {
381 421 // Key exists and its status is valid.
382 422 // Set valid key status.
383 423 $status = 'valid';
384 - $status_msg = __( 'Valid' );
424 + $status_msg = esc_html__( 'Valid' );
385 425 $color = '#46b450'; // Green.
386 426 $message = null;
387 427 } elseif ( $key && 1 == $key_status ) {
388 428 // Key exists but its validation has failed.
@@ -387,31 +427,33 @@
387 427 } elseif ( $key && 1 == $key_status ) {
388 428 // Key exists but its validation has failed.
389 429 // Set invalid key status.
390 430 $status = 'invalid';
391 - $status_msg = __( 'Invalid' );
431 + $status_msg = esc_html__( 'Invalid' );
392 432 $color = '#dc3232'; // Red.
393 - $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>' );
394 435 } elseif ( $key && ! $key_status ) {
395 436 // Key exists but it has no valid status yet
396 437 // Perform key validation.
397 - $key_check = $self->ao_ccss_key_validation( $key );
438 + $key_check = $this->ao_ccss_key_validation( $key );
398 439
399 440 // Key is valid, set valid status.
400 441 if ( $key_check ) {
401 442 $status = 'valid';
402 - $status_msg = __( 'Valid' );
443 + $status_msg = esc_html__( 'Valid' );
403 444 $color = '#46b450'; // Green.
404 445 $message = null;
405 446 } else {
406 447 // Key is invalid, set invalid status.
407 448 $status = 'invalid';
408 - $status_msg = __( 'Invalid' );
449 + $status_msg = esc_html__( 'Invalid' );
409 450 $color = '#dc3232'; // Red.
410 451 if ( get_option( 'autoptimize_ccss_keyst' ) == 1 ) {
411 - $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>' );
412 454 } else {
413 - $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' );
414 456 }
415 457 }
416 458 } else {
417 459 // No key nor status
@@ -416,11 +458,12 @@
416 458 } else {
417 459 // No key nor status
418 460 // Set no key status.
419 461 $status = 'nokey';
420 - $status_msg = __( 'None' );
462 + $status_msg = esc_html__( 'None' );
421 463 $color = '#ffb900'; // Yellow.
422 - $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>' );
423 466 }
424 467
425 468 // Fill returned values.
426 469 $key_return['status'] = $status;
@@ -435,9 +478,9 @@
435 478 return $key_return;
436 479 }
437 480
438 481 public function ao_ccss_key_validation( $key ) {
439 - global $ao_ccss_noptimize;
482 + $noptimize = $this->criticalcss->get_option( 'noptimize' );
440 483
441 484 // POST a dummy job to criticalcss.com to check for key validation
442 485 // Prepare home URL for the request.
443 486 $src_url = get_home_url();
@@ -442,127 +485,124 @@
442 485 // Prepare home URL for the request.
443 486 $src_url = get_home_url();
444 487
445 488 // Avoid AO optimizations if required by config or avoid lazyload if lazyload is active in AO.
446 - if ( ! empty( $ao_ccss_noptimize ) ) {
447 - $src_url .= '?ao_noptirocket=1';
489 + if ( ! empty( $noptimize ) ) {
490 + $src_url .= '/?ao_noptirocket=1';
448 491 } elseif ( class_exists( 'autoptimizeImages', false ) && autoptimizeImages::should_lazyload_wrapper() ) {
449 - $src_url .= '?ao_nolazy=1';
492 + $src_url .= '/?ao_nolazy=1';
450 493 }
451 494
452 495 $src_url = apply_filters( 'autoptimize_filter_ccss_cron_srcurl', $src_url );
453 496
454 - // Prepare the request.
455 - $url = esc_url_raw( AO_CCSS_API . 'generate' );
456 - $args = array(
457 - 'headers' => array(
458 - 'User-Agent' => 'Autoptimize v' . AO_CCSS_VER,
459 - 'Content-type' => 'application/json; charset=utf-8',
460 - 'Authorization' => 'JWT ' . $key,
461 - 'Connection' => 'close',
462 - ),
463 - // Body must be JSON.
464 - 'body' => json_encode(
465 - 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',
466 503 array(
467 - 'url' => $src_url,
468 - 'aff' => 1,
469 - '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',
470 508 )
471 - )
472 - ),
473 - );
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 + );
474 523
475 - // Dispatch the request and store its response code.
476 - $req = wp_safe_remote_post( $url, $args );
477 - $code = wp_remote_retrieve_response_code( $req );
478 - $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 );
479 528
480 - if ( 200 == $code ) {
481 - // Response is OK.
482 - // Set key status as valid and log key check.
483 - update_option( 'autoptimize_ccss_keyst', 2 );
484 - 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 );
485 534
486 - // extract job-id from $body and put it in the queue as a P job
487 - // but only if no jobs and no rules!
488 - global $ao_ccss_queue;
489 - 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' );
490 539
491 - if ( 0 == count( $ao_ccss_queue ) && 0 == count( $ao_ccss_rules['types'] ) && 0 == count( $ao_ccss_rules['paths'] ) ) {
492 - if ( 'JOB_QUEUED' == $body['job']['status'] || 'JOB_ONGOING' == $body['job']['status'] ) {
493 - $jprops['ljid'] = 'firstrun';
494 - $jprops['rtarget'] = 'types|is_front_page';
495 - $jprops['ptype'] = 'is_front_page';
496 - $jprops['hashes'][] = 'dummyhash';
497 - $jprops['hash'] = 'dummyhash';
498 - $jprops['file'] = null;
499 - $jprops['jid'] = $body['job']['id'];
500 - $jprops['jqstat'] = $body['job']['status'];
501 - $jprops['jrstat'] = null;
502 - $jprops['jvstat'] = null;
503 - $jprops['jctime'] = microtime( true );
504 - $jprops['jftime'] = null;
505 - $ao_ccss_queue['/'] = $jprops;
506 - $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
507 - update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
508 - 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 + }
509 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;
510 578 }
511 - return true;
512 - } elseif ( 401 == $code ) {
513 - // Response is unauthorized
514 - // Set key status as invalid and log key check.
515 - update_option( 'autoptimize_ccss_keyst', 1 );
516 - autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: API key is invalid, updating key status', 3 );
517 - return false;
518 579 } else {
519 - // Response unkown
520 - // Log key check attempt.
521 - autoptimizeCriticalCSSCore::ao_ccss_log( 'criticalcss.com: could not check API key status, this is a service error, body follows if any...', 2 );
522 - if ( ! empty( $body ) ) {
523 - autoptimizeCriticalCSSCore::ao_ccss_log( print_r( $body, true ), 2 );
524 - }
525 - if ( is_wp_error( $req ) ) {
526 - autoptimizeCriticalCSSCore::ao_ccss_log( $req->get_error_message(), 2 );
527 - }
580 + // localhost/ private network server, no API check possible.
528 581 return false;
529 582 }
530 583 }
531 584
532 - public static function ao_ccss_viewport() {
585 + public function ao_ccss_viewport() {
533 586 // Get viewport size
534 587 // Attach viewport option.
535 - global $ao_ccss_viewport;
588 + $viewport = $this->criticalcss->get_option( 'viewport' );
536 589
537 - // Prepare viewport array.
538 - $viewport = array();
539 -
540 - // Viewport Width.
541 - if ( ! empty( $ao_ccss_viewport['w'] ) ) {
542 - $viewport['w'] = $ao_ccss_viewport['w'];
543 - } else {
544 - $viewport['w'] = '';
545 - }
546 -
547 - // Viewport Height.
548 - if ( ! empty( $ao_ccss_viewport['h'] ) ) {
549 - $viewport['h'] = $ao_ccss_viewport['h'];
550 - } else {
551 - $viewport['h'] = '';
552 - }
553 -
554 - return $viewport;
590 + return array(
591 + 'w' => ! empty( $viewport['w'] ) ? $viewport['w'] : '',
592 + 'h' => ! empty( $viewport['h'] ) ? $viewport['h'] : '',
593 + );
555 594 }
556 595
557 - public static function ao_ccss_check_contents( $ccss ) {
596 + public function ao_ccss_check_contents( $ccss ) {
558 597 // Perform basic exploit avoidance and CSS validation.
598 + // todo; use autoptimizeStyles::check_css which is way stricter? or could that break things??
559 599 if ( ! empty( $ccss ) ) {
560 600 // Try to avoid code injection.
561 - $blocklist = array( '#!/', 'function(', '<script', '<?php' );
601 + $blocklist = array( '#!/', 'function(', '<script', '<?php', '<?=', '</style', 'onload=', 'onerror=', ' onmouse', 'onscroll=', 'onclick=' );
562 602 foreach ( $blocklist as $blocklisted ) {
563 - if ( strpos( $ccss, $blocklisted ) !== false ) {
564 - 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 );
565 605 return false;
566 606 }
567 607 }
568 608
@@ -569,9 +609,9 @@
569 609 // Check for most basics CSS structures.
570 610 $needlist = array( '{', '}', ':' );
571 611 foreach ( $needlist as $needed ) {
572 612 if ( false === strpos( $ccss, $needed ) && 'none' !== $ccss ) {
573 - 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 );
574 614 return false;
575 615 }
576 616 }
577 617 }
@@ -579,12 +619,12 @@
579 619 // Return true if file critical CSS is sane.
580 620 return true;
581 621 }
582 622
583 - public static function ao_ccss_log( $msg, $lvl ) {
623 + public function ao_ccss_log( $msg, $lvl ) {
584 624 // Commom logging facility
585 625 // Attach debug option.
586 - global $ao_ccss_debug;
626 + $debug = $this->criticalcss->get_option( 'debug' );
587 627
588 628 // Prepare log levels, where accepted $lvl are:
589 629 // 1: II (for info)
590 630 // 2: EE (for error)
@@ -590,23 +630,22 @@
590 630 // 2: EE (for error)
591 631 // 3: DD (for debug)
592 632 // Default: UU (for unkown).
593 633 $level = false;
594 - switch ( $lvl ) {
595 - case 1:
596 - $level = 'II';
597 - break;
598 - case 2:
599 - $level = 'EE';
600 - break;
601 - case 3:
602 - // Output debug messages only if debug mode is enabled.
603 - 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:
604 643 $level = 'DD';
605 - }
606 - break;
607 - default:
608 - $level = 'UU';
644 + break;
645 + default:
646 + $level = 'UU';
647 + }
609 648 }
610 649
611 650 // Prepare and write a log message if there's a valid level.
612 651 if ( $level ) {
@@ -611,9 +650,9 @@
611 650 // Prepare and write a log message if there's a valid level.
612 651 if ( $level ) {
613 652
614 653 // Prepare message.
615 - $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>';
654 + $message = date( 'c' ) . ' - [' . $level . '] ' . htmlentities( $msg ) . '<br>'; // @codingStandardsIgnoreLine
616 655
617 656 // Write message to log file.
618 657 error_log( $message, 3, AO_CCSS_LOG );
619 658 }
@@ -618,10 +657,26 @@
618 657 error_log( $message, 3, AO_CCSS_LOG );
619 658 }
620 659 }
621 660
622 - public static function ao_ccss_clear_page_tpl_cache() {
661 + public function ao_ccss_clear_page_tpl_cache() {
623 662 // Clears transient cache for page templates.
624 663 delete_transient( 'autoptimize_ccss_page_templates' );
625 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 );
626 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 + }
627 682 }