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 +297 -238 2.7.6 → 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,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 }