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/autoptimizeCriticalCSSEnqueue.php +194 -142 2.7.4 → 3.1.16 View file →
@@ -7,181 +7,228 @@
7 7 exit;
8 8 }
9 9
10 10 class autoptimizeCriticalCSSEnqueue {
11 - public function __construct()
12 - {
13 - // fetch all options at once and populate them individually explicitely as globals.
14 - $all_options = autoptimizeCriticalCSSBase::fetch_options();
15 - foreach ( $all_options as $_option => $_value ) {
16 - global ${$_option};
17 - ${$_option} = $_value;
18 - }
11 + /**
12 + * Critical CSS object.
13 + *
14 + * @var object
15 + */
16 + protected $criticalcss;
17 +
18 + public function __construct() {
19 + $this->criticalcss = autoptimize()->criticalcss();
19 20 }
20 21
21 - public static function ao_ccss_enqueue( $hash ) {
22 - $self = new self();
22 + public function ao_ccss_enqueue( $hash = '', $path = '', $type = 'is_page' ) {
23 23 // Get key status.
24 - $key = autoptimizeCriticalCSSCore::ao_ccss_key_status( false );
24 + $key = $this->criticalcss->key_status( false );
25 25
26 26 // Queue is available to anyone...
27 27 $enqueue = true;
28 28
29 29 // ... which are not the ones below.
30 - if ( is_user_logged_in() || is_feed() || is_404() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $self->ao_ccss_ua() || 'nokey' == $key['status'] || 'invalid' == $key['status'] || false === apply_filters( 'autoptimize_filter_ccss_enqueue_should_enqueue', true ) ) {
30 + if ( true === autoptimizeUtils::is_local_server() ) {
31 31 $enqueue = false;
32 - autoptimizeCriticalCSSCore::ao_ccss_log( "Job queuing is not available for WordPress's logged in users, feeds, error pages, ajax calls, to criticalcss.com itself or when a valid API key is not found", 3 );
32 + $this->criticalcss->log( 'cant enqueue as local/ private', 3 );
33 + } elseif ( 'nokey' == $key['status'] || 'invalid' == $key['status'] ) {
34 + $enqueue = false;
35 + $this->criticalcss->log( 'Job queuing is not available: no valid API key found.', 3 );
36 + } elseif ( ! empty( $hash ) && ( is_user_logged_in() || is_feed() || is_404() || is_search() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $this->ao_ccss_ua() || false === apply_filters( 'autoptimize_filter_ccss_enqueue_should_enqueue', true ) ) ) {
37 + $enqueue = false;
38 + $this->criticalcss->log( 'Job queuing is not available for WordPress\'s logged in users, feeds, error pages, ajax calls or calls from criticalcss.com itself.', 3 );
39 + } elseif ( empty( $hash ) && empty( $path ) || ( ( 'is_single' !== $type ) && ( 'is_page' !== $type ) ) ) {
40 + $enqueue = false;
41 + $this->criticalcss->log( 'Forced job queuing failed, no path or not right type', 3 );
33 42 }
34 43
35 - if ( $enqueue ) {
36 - // Continue if queue is available
37 - // Attach required arrays/ vars.
38 - global $ao_ccss_rules;
39 - global $ao_ccss_queue_raw;
40 - global $ao_ccss_queue;
41 - global $ao_ccss_forcepath;
44 + if ( ! $enqueue ) {
45 + return;
46 + }
42 47
43 - // Get request path and page type, and initialize the queue update flag.
44 - $req_path = strtok( $_SERVER['REQUEST_URI'], '?' );
45 - $req_type = $self->ao_ccss_get_type();
46 - $job_qualify = false;
47 - $target_rule = false;
48 - $rule_properties = false;
49 - $queue_update = false;
48 + // Continue if queue is available
49 + // Attach required arrays/ vars.
50 + $rules = $this->criticalcss->get_option( 'rules' );
51 + $queue_raw = $this->criticalcss->get_option( 'queue_raw' );
52 + $queue = $this->criticalcss->get_option( 'queue' );
53 + $forcepath = $this->criticalcss->get_option( 'forcepath' );
50 54
51 - // Match for paths in rules.
52 - foreach ( $ao_ccss_rules['paths'] as $path => $props ) {
55 + // Get request path and page type, and initialize the queue update flag.
56 + if ( ! empty( $hash ) ) {
57 + $req_orig = $_SERVER['REQUEST_URI'];
58 + $req_type = $this->ao_ccss_get_type();
59 + } elseif ( ! empty( $path ) ) {
60 + $req_orig = $path;
61 + if ( '/' === $path ) {
62 + $req_type = 'is_front_page';
63 + } else {
64 + $req_type = $type;
65 + }
66 + }
67 + $req_path = strtok( $req_orig, '?#' ); // remove querystring and fragment
53 68
54 - // Prepare rule target and log.
55 - $target_rule = 'paths|' . $path;
56 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Qualifying path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
69 + // now that we really have the path, check if there's no garbage in there (due to some themes serving a non 404 page even if the resource does not exist resulting in all sorts of nonsense rules).
70 + if ( true === apply_filters( 'autoptimize_filter_ccss_enqueue_block_garbage' , true ) && str_ireplace( apply_filters( 'autoptimize_filter_ccss_enqueue_blocklist', array( '.php', 'data:text/javascript;base64', '/.', '/null', '.jpeg', '.jpg', '.png' ) ), '', $req_path ) !== $req_path ) {
71 + $this->criticalcss->log( 'Job not enqueued looks like the path is just garbage; ' . $req_path, 3 );
72 + return;
73 + }
57 74
58 - // Path match
59 - // -> exact match needed for AUTO rules
60 - // -> partial match OK for MANUAL rules (which have empty hash and a file with CCSS).
61 - if ( $path === $req_path || ( false == $props['hash'] && false != $props['file'] && preg_match( '|' . $path . '|', $req_path ) ) ) {
75 + // ensure the path does not contain a potentially malicious payload
76 + if ( $req_path !== esc_url( $req_path ) ) {
77 + $this->criticalcss->log( 'Job not enqueued looks like the path contains malicious ; ' . esc_url( $req_path ), 3 );
78 + return;
79 + }
62 80
63 - // There's a path match in the rule, so job QUALIFIES with a path rule match.
64 - $job_qualify = true;
65 - $rule_properties = $props;
66 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
81 + // Check if we have a lang param. we need to keep as WPML can switch languages based on that
82 + // and that includes RTL -> LTR so diff. structure, so rules would be RTL vs LTR
83 + // but this needs changes in the structure of the rule object so off by default for now
84 + // as now this will simply result in conditional rules being overwritten.
85 + if ( apply_filters( 'autoptimize_filter_ccss_coreenqueue_honor_lang', false ) && strpos( $req_orig, 'lang=' ) !== false ) {
86 + $req_params = strtok( '?' );
87 + parse_str( $req_params, $req_params_arr );
88 + if ( array_key_exists( 'lang', $req_params_arr ) && ! empty( $req_params_arr['lang'] ) ) {
89 + $req_path .= '?lang=' . $req_params_arr['lang'];
90 + }
91 + }
67 92
68 - // Stop processing other path rules.
69 - break;
70 - }
93 + $job_qualify = false;
94 + $target_rule = false;
95 + $rule_properties = false;
96 + $queue_update = false;
97 +
98 + // Match for paths in rules.
99 + foreach ( $rules['paths'] as $path => $props ) {
100 +
101 + // Prepare rule target and log.
102 + $target_rule = 'paths|' . $path;
103 + $this->criticalcss->log( 'Qualifying path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
104 +
105 + // Path match
106 + // -> exact match needed for AUTO rules
107 + // -> partial match OK for MANUAL rules (which have empty hash and a file with CCSS).
108 + if ( $path === $req_path || ( false == $props['hash'] && false != $props['file'] && preg_match( '|' . $path . '|', $req_path ) ) ) {
109 +
110 + // There's a path match in the rule, so job QUALIFIES with a path rule match.
111 + $job_qualify = true;
112 + $rule_properties = $props;
113 + $this->criticalcss->log( 'Path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
114 +
115 + // Stop processing other path rules.
116 + break;
71 117 }
118 + }
72 119
73 - // Match for types in rules if no path rule matches and if we're not enforcing paths.
74 - if ( ! $job_qualify && ( ! $ao_ccss_forcepath || ! in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) || ! apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) ) ) {
75 - foreach ( $ao_ccss_rules['types'] as $type => $props ) {
120 + // Match for types in rules if no path rule matches and if we're not enforcing paths.
121 + if ( '' !== $hash && ! $job_qualify && ( ! $forcepath || ! in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) || ! apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) ) ) {
122 + foreach ( $rules['types'] as $type => $props ) {
76 123
77 - // Prepare rule target and log.
78 - $target_rule = 'types|' . $type;
79 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Qualifying page type <' . $req_type . '> on path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
124 + // Prepare rule target and log.
125 + $target_rule = 'types|' . $type;
126 + $this->criticalcss->log( 'Qualifying page type <' . $req_type . '> on path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 );
80 127
81 - if ( $req_type == $type ) {
82 - // Type match.
83 - // There's a type match in the rule, so job QUALIFIES with a type rule match.
84 - $job_qualify = true;
85 - $rule_properties = $props;
86 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Page type <' . $req_type . '> on path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
128 + if ( $req_type == $type ) {
129 + // Type match.
130 + // There's a type match in the rule, so job QUALIFIES with a type rule match.
131 + $job_qualify = true;
132 + $rule_properties = $props;
133 + $this->criticalcss->log( 'Page type <' . $req_type . '> on path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 );
87 134
88 - // Stop processing other type rules.
89 - break;
90 - }
135 + // Stop processing other type rules.
136 + break;
91 137 }
92 138 }
139 + }
93 140
94 - if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) {
95 - // If job qualifies but rule hash is false and file isn't false (MANUAL rule) or if template, job does not qualify despite what previous evaluations says.
96 - $job_qualify = false;
97 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
98 - } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
99 - // But if job does not qualify and rule properties are set, job qualifies as there is no matching rule for it yet
100 - // Fill-in the new target rule.
101 - $job_qualify = true;
141 + if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) {
142 + // If job qualifies but rule hash is false and file isn't false (MANUAL rule) or if template, job does not qualify despite what previous evaluations says.
143 + $job_qualify = false;
144 + $this->criticalcss->log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
145 + } elseif ( ! $job_qualify && empty( $rule_properties ) ) {
146 + // But if job does not qualify and rule properties are set, job qualifies as there is no matching rule for it yet
147 + // Fill-in the new target rule.
148 + $job_qualify = true;
102 149
103 - // Should we switch to path-base AUTO-rules? Conditions:
104 - // 1. forcepath option has to be enabled (off by default)
105 - // 2. request type should be (by default, but filterable) one of is_page (removed for now: woo_is_product or woo_is_product_category).
106 - if ( ( $ao_ccss_forcepath && in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) ) || apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) ) {
107 - if ( '/' !== $req_path ) {
108 - $target_rule = 'paths|' . $req_path;
109 - } else {
110 - // Exception; we don't want a path-based rule for "/" as that messes things up, hard-switch this to a type-based is_front_page rule.
111 - $target_rule = 'types|' . 'is_front_page';
112 - }
150 + // Should we switch to path-base AUTO-rules? Conditions:
151 + // 1. forcepath option has to be enabled (off by default)
152 + // 2. request type should be (by default, but filterable) one of is_page (removed for now: woo_is_product or woo_is_product_category).
153 + if ( ( $forcepath && in_array( $req_type, apply_filters( 'autoptimize_filter_ccss_coreenqueue_forcepathfortype', array( 'is_page' ) ) ) ) || apply_filters( 'autoptimize_filter_ccss_coreenqueue_ignorealltypes', false ) || empty( $hash ) ) {
154 + if ( '/' !== $req_path ) {
155 + $target_rule = 'paths|' . $req_path;
113 156 } else {
114 - $target_rule = 'types|' . $req_type;
157 + // Exception; we don't want a path-based rule for "/" as that messes things up, hard-switch this to a type-based is_front_page rule.
158 + $target_rule = 'types|' . 'is_front_page';
115 159 }
116 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission QUALIFIED by MISSING rule for page type <' . $req_type . '> on path <' . $req_path . '>, new rule target is <' . $target_rule . '>', 3 );
117 160 } else {
118 - // Or just log a job qualified by a matching rule.
119 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission QUALIFIED by AUTO rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
161 + $target_rule = 'types|' . $req_type;
120 162 }
163 + $this->criticalcss->log( 'Job submission QUALIFIED by MISSING rule for page type <' . $req_type . '> on path <' . $req_path . '>, new rule target is <' . $target_rule . '>', 3 );
164 + } else {
165 + // Or just log a job qualified by a matching rule.
166 + $this->criticalcss->log( 'Job submission QUALIFIED by AUTO rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 );
167 + }
121 168
122 - // Submit job.
123 - if ( $job_qualify ) {
124 - if ( ! array_key_exists( $req_path, $ao_ccss_queue ) ) {
125 - // This is a NEW job
126 - // Merge job into the queue.
127 - $ao_ccss_queue[ $req_path ] = $self->ao_ccss_define_job(
169 + // Submit job.
170 + if ( $job_qualify ) {
171 + if ( ! array_key_exists( $req_path, $queue ) ) {
172 + // This is a NEW job
173 + // Merge job into the queue.
174 + $queue[ $req_path ] = $this->ao_ccss_define_job(
175 + $req_path,
176 + $target_rule,
177 + $req_type,
178 + $hash,
179 + null,
180 + null,
181 + null,
182 + null,
183 + true
184 + );
185 + // Set update flag.
186 + $queue_update = true;
187 + } else {
188 + // This is an existing job
189 + // The job is still NEW, most likely this is extra CSS file for the same page that needs a hash.
190 + if ( 'NEW' == $queue[ $req_path ]['jqstat'] ) {
191 + // Add hash if it's not already in the job.
192 + if ( ! in_array( $hash, $queue[ $req_path ]['hashes'] ) ) {
193 + // Push new hash to its array and update flag.
194 + $queue_update = array_push( $queue[ $req_path ]['hashes'], $hash );
195 +
196 + // Log job update.
197 + $this->criticalcss->log( 'Hashes UPDATED on local job id <' . $queue[ $req_path ]['ljid'] . '>, job status NEW, target rule <' . $queue[ $req_path ]['rtarget'] . '>, hash added: ' . $hash, 3 );
198 +
199 + // Return from here as the hash array is already updated.
200 + return true;
201 + }
202 + } elseif ( 'NEW' != $queue[ $req_path ]['jqstat'] && 'JOB_QUEUED' != $queue[ $req_path ]['jqstat'] && 'JOB_ONGOING' != $queue[ $req_path ]['jqstat'] ) {
203 + // Allow requeuing jobs that are not NEW, JOB_QUEUED or JOB_ONGOING
204 + // Merge new job keeping some previous job values.
205 + $queue[ $req_path ] = $this->ao_ccss_define_job(
128 206 $req_path,
129 207 $target_rule,
130 208 $req_type,
131 209 $hash,
132 - null,
133 - null,
134 - null,
135 - null,
136 - true
210 + $queue[ $req_path ]['file'],
211 + $queue[ $req_path ]['jid'],
212 + $queue[ $req_path ]['jrstat'],
213 + $queue[ $req_path ]['jvstat'],
214 + false
137 215 );
138 216 // Set update flag.
139 217 $queue_update = true;
140 - } else {
141 - // This is an existing job
142 - // The job is still NEW, most likely this is extra CSS file for the same page that needs a hash.
143 - if ( 'NEW' == $ao_ccss_queue[ $req_path ]['jqstat'] ) {
144 - // Add hash if it's not already in the job.
145 - if ( ! in_array( $hash, $ao_ccss_queue[ $req_path ]['hashes'] ) ) {
146 - // Push new hash to its array and update flag.
147 - $queue_update = array_push( $ao_ccss_queue[ $req_path ]['hashes'], $hash );
148 -
149 - // Log job update.
150 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Hashes UPDATED on local job id <' . $ao_ccss_queue[ $req_path ]['ljid'] . '>, job status NEW, target rule <' . $ao_ccss_queue[ $req_path ]['rtarget'] . '>, hash added: ' . $hash, 3 );
151 -
152 - // Return from here as the hash array is already updated.
153 - return true;
154 - }
155 - } elseif ( 'NEW' != $ao_ccss_queue[ $req_path ]['jqstat'] && 'JOB_QUEUED' != $ao_ccss_queue[ $req_path ]['jqstat'] && 'JOB_ONGOING' != $ao_ccss_queue[ $req_path ]['jqstat'] ) {
156 - // Allow requeuing jobs that are not NEW, JOB_QUEUED or JOB_ONGOING
157 - // Merge new job keeping some previous job values.
158 - $ao_ccss_queue[ $req_path ] = $self->ao_ccss_define_job(
159 - $req_path,
160 - $target_rule,
161 - $req_type,
162 - $hash,
163 - $ao_ccss_queue[ $req_path ]['file'],
164 - $ao_ccss_queue[ $req_path ]['jid'],
165 - $ao_ccss_queue[ $req_path ]['jrstat'],
166 - $ao_ccss_queue[ $req_path ]['jvstat'],
167 - false
168 - );
169 - // Set update flag.
170 - $queue_update = true;
171 - }
172 218 }
219 + }
173 220
174 - if ( $queue_update ) {
175 - // Persist the job to the queue and return.
176 - $ao_ccss_queue_raw = json_encode( $ao_ccss_queue );
177 - update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false );
178 - return true;
179 - } else {
180 - // Or just return false if no job was added.
181 - autoptimizeCriticalCSSCore::ao_ccss_log( 'A job for path <' . $req_path . '> already exist with NEW or PENDING status, skipping job creation', 3 );
182 - return false;
183 - }
221 + if ( $queue_update ) {
222 + // Persist the job to the queue and return.
223 + $queue_raw = json_encode( $queue );
224 + update_option( 'autoptimize_ccss_queue', $queue_raw, false );
225 + $this->criticalcss->flush_options();
226 + return true;
227 + } else {
228 + // Or just return false if no job was added.
229 + $this->criticalcss->log( 'A job for path <' . $req_path . '> already exist with NEW or PENDING status, skipping job creation', 3 );
230 + return false;
184 231 }
185 232 }
186 233 }
187 234
@@ -187,26 +234,31 @@
187 234
188 235 public function ao_ccss_get_type() {
189 236 // Get the type of a page
190 237 // Attach the conditional tags array.
191 - global $ao_ccss_types;
238 + $types = $this->criticalcss->get_types();
239 + $forcepath = $this->criticalcss->get_option( 'forcepath' );
192 240
193 241 // By default, a page type is false.
194 242 $page_type = false;
195 243
196 244 // Iterates over the array to match a type.
197 - foreach ( $ao_ccss_types as $type ) {
245 + foreach ( $types as $type ) {
198 246 if ( is_404() ) {
199 247 $page_type = 'is_404';
200 248 break;
201 - } elseif ( strpos( $type, 'custom_post_' ) !== false ) {
202 - // Match custom post types.
249 + } elseif ( is_front_page() ) {
250 + // identify frontpage immediately to avoid it also matching a CPT or template.
251 + $page_type = 'is_front_page';
252 + break;
253 + } elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $forcepath || ! is_page() ) && is_singular() ) {
254 + // Match custom post types and not page or page not forced to path-based.
203 255 if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
204 256 $page_type = $type;
205 257 break;
206 258 }
207 - } elseif ( strpos( $type, 'template_' ) !== false ) {
208 - // Match templates.
259 + } elseif ( strpos( $type, 'template_' ) !== false && ( ! $forcepath || ! is_page() ) ) {
260 + // Match templates if not page or if page is not forced to path-based.
209 261 if ( is_page_template( substr( $type, 9 ) ) ) {
210 262 $page_type = $type;
211 263 break;
212 264 }
@@ -214,10 +266,10 @@
214 266 // Match all other existing types
215 267 // but remove prefix to be able to check if the function exists & returns true.
216 268 $_type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
217 269 if ( function_exists( $_type ) && call_user_func( $_type ) ) {
218 - // Make sure we only return is_front_page (and is_home) for one page, not for the "paged frontpage" (/page/2 ..).
219 - if ( ( 'is_front_page' !== $_type && 'is_home' !== $_type ) || ! is_paged() ) {
270 + // Make sure we only return for one page, not for the "paged pages" (/page/2 ..).
271 + if ( ! is_page() || ! is_paged() ) {
220 272 $page_type = $type;
221 273 break;
222 274 }
223 275 }
@@ -252,9 +304,9 @@
252 304 $operation = 'UPDATED';
253 305 }
254 306
255 307 // Log job creation.
256 - autoptimizeCriticalCSSCore::ao_ccss_log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 );
308 + $this->criticalcss->log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 );
257 309
258 310 return $path;
259 311 }
260 312