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/autoptimizeCriticalCSSEnqueue.php +198 -142 2.7.2 → trunk 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'] ) {
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'] ) {
95 - // If job qualifies but rule hash is false and file isn't false (MANUAL rule), 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,33 +234,42 @@
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 - // If templates; don't break, templates become manual-only rules.
259 + } elseif ( strpos( $type, 'template_' ) !== false && ( ! $forcepath || ! is_page() ) ) {
260 + // Match templates if not page or if page is not forced to path-based.
261 + if ( is_page_template( substr( $type, 9 ) ) ) {
262 + $page_type = $type;
263 + break;
264 + }
209 265 } else {
210 266 // Match all other existing types
211 267 // but remove prefix to be able to check if the function exists & returns true.
212 268 $_type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
213 269 if ( function_exists( $_type ) && call_user_func( $_type ) ) {
214 - // Make sure we only return is_front_page (and is_home) for one page, not for the "paged frontpage" (/page/2 ..).
215 - 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() ) {
216 272 $page_type = $type;
217 273 break;
218 274 }
219 275 }
@@ -248,9 +304,9 @@
248 304 $operation = 'UPDATED';
249 305 }
250 306
251 307 // Log job creation.
252 - 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 );
253 309
254 310 return $path;
255 311 }
256 312