|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Critical CSS job enqueue logic. |
|
4
|
|
|
*/ |
|
5
|
|
|
|
|
6
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
|
7
|
|
|
exit; |
|
8
|
|
|
} |
|
9
|
|
|
|
|
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
|
|
|
} |
|
19
|
|
|
} |
|
20
|
|
|
|
|
21
|
|
|
public static function ao_ccss_enqueue( $hash ) { |
|
22
|
|
|
$self = new self(); |
|
23
|
|
|
// Get key status. |
|
24
|
|
|
$key = autoptimizeCriticalCSSCore::ao_ccss_key_status( false ); |
|
25
|
|
|
|
|
26
|
|
|
// Queue is available to anyone... |
|
27
|
|
|
$enqueue = true; |
|
28
|
|
|
|
|
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 ) ) { |
|
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 ); |
|
33
|
|
|
} |
|
34
|
|
|
|
|
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; |
|
42
|
|
|
|
|
43
|
|
|
// Get request path and page type, and initialize the queue update flag. |
|
44
|
|
|
$req_orig = $_SERVER['REQUEST_URI']; |
|
45
|
|
|
$req_path = strtok( $req_orig, '?' ); |
|
46
|
|
|
|
|
47
|
|
|
// Check if we have a lang param. we need to keep as WPML can switch languages based on that |
|
48
|
|
|
// and that includes RTL -> LTR so diff. structure, so rules would be RTL vs LTR |
|
49
|
|
|
// but this needs changes in the structur of the rule object so off by default for now |
|
50
|
|
|
// as now this will simply result in conditional rules being overwritten. |
|
51
|
|
|
if ( apply_filters( 'autoptimize_filter_ccss_coreenqueue_honor_lang', false ) && strpos( $req_orig, 'lang=' ) !== false ) { |
|
52
|
|
|
$req_params = strtok( '?' ); |
|
53
|
|
|
parse_str( $req_params, $req_params_arr ); |
|
54
|
|
|
if ( array_key_exists( 'lang', $req_params_arr ) && !empty( $req_params_arr['lang'] ) ) { |
|
55
|
|
|
$req_path .= '?lang=' . $req_params_arr['lang']; |
|
56
|
|
|
} |
|
57
|
|
|
} |
|
58
|
|
|
|
|
59
|
|
|
$req_type = $self->ao_ccss_get_type(); |
|
60
|
|
|
$job_qualify = false; |
|
61
|
|
|
$target_rule = false; |
|
62
|
|
|
$rule_properties = false; |
|
63
|
|
|
$queue_update = false; |
|
64
|
|
|
|
|
65
|
|
|
// Match for paths in rules. |
|
66
|
|
|
foreach ( $ao_ccss_rules['paths'] as $path => $props ) { |
|
67
|
|
|
|
|
68
|
|
|
// Prepare rule target and log. |
|
69
|
|
|
$target_rule = 'paths|' . $path; |
|
70
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Qualifying path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 ); |
|
71
|
|
|
|
|
72
|
|
|
// Path match |
|
73
|
|
|
// -> exact match needed for AUTO rules |
|
74
|
|
|
// -> partial match OK for MANUAL rules (which have empty hash and a file with CCSS). |
|
75
|
|
|
if ( $path === $req_path || ( false == $props['hash'] && false != $props['file'] && preg_match( '|' . $path . '|', $req_path ) ) ) { |
|
76
|
|
|
|
|
77
|
|
|
// There's a path match in the rule, so job QUALIFIES with a path rule match. |
|
78
|
|
|
$job_qualify = true; |
|
79
|
|
|
$rule_properties = $props; |
|
80
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 ); |
|
81
|
|
|
|
|
82
|
|
|
// Stop processing other path rules. |
|
83
|
|
|
break; |
|
84
|
|
|
} |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
// Match for types in rules if no path rule matches and if we're not enforcing paths. |
|
88
|
|
|
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 ) ) ) { |
|
89
|
|
|
foreach ( $ao_ccss_rules['types'] as $type => $props ) { |
|
90
|
|
|
|
|
91
|
|
|
// Prepare rule target and log. |
|
92
|
|
|
$target_rule = 'types|' . $type; |
|
93
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Qualifying page type <' . $req_type . '> on path <' . $req_path . '> for job submission by rule <' . $target_rule . '>', 3 ); |
|
94
|
|
|
|
|
95
|
|
|
if ( $req_type == $type ) { |
|
96
|
|
|
// Type match. |
|
97
|
|
|
// There's a type match in the rule, so job QUALIFIES with a type rule match. |
|
98
|
|
|
$job_qualify = true; |
|
99
|
|
|
$rule_properties = $props; |
|
100
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Page type <' . $req_type . '> on path <' . $req_path . '> QUALIFIED for job submission by rule <' . $target_rule . '>', 3 ); |
|
101
|
|
|
|
|
102
|
|
|
// Stop processing other type rules. |
|
103
|
|
|
break; |
|
104
|
|
|
} |
|
105
|
|
|
} |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
if ( $job_qualify && ( ( false == $rule_properties['hash'] && false != $rule_properties['file'] ) || strpos( $req_type, 'template_' ) !== false ) ) { |
|
109
|
|
|
// 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. |
|
110
|
|
|
$job_qualify = false; |
|
111
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission DISQUALIFIED by MANUAL rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 ); |
|
112
|
|
|
} elseif ( ! $job_qualify && empty( $rule_properties ) ) { |
|
113
|
|
|
// But if job does not qualify and rule properties are set, job qualifies as there is no matching rule for it yet |
|
114
|
|
|
// Fill-in the new target rule. |
|
115
|
|
|
$job_qualify = true; |
|
116
|
|
|
|
|
117
|
|
|
// Should we switch to path-base AUTO-rules? Conditions: |
|
118
|
|
|
// 1. forcepath option has to be enabled (off by default) |
|
119
|
|
|
// 2. request type should be (by default, but filterable) one of is_page (removed for now: woo_is_product or woo_is_product_category). |
|
120
|
|
|
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 ) ) { |
|
121
|
|
|
if ( '/' !== $req_path ) { |
|
122
|
|
|
$target_rule = 'paths|' . $req_path; |
|
123
|
|
|
} else { |
|
124
|
|
|
// 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. |
|
125
|
|
|
$target_rule = 'types|' . 'is_front_page'; |
|
126
|
|
|
} |
|
127
|
|
|
} else { |
|
128
|
|
|
$target_rule = 'types|' . $req_type; |
|
129
|
|
|
} |
|
130
|
|
|
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 ); |
|
131
|
|
|
} else { |
|
132
|
|
|
// Or just log a job qualified by a matching rule. |
|
133
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Job submission QUALIFIED by AUTO rule <' . $target_rule . '> with hash <' . $rule_properties['hash'] . '> and file <' . $rule_properties['file'] . '>', 3 ); |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
// Submit job. |
|
137
|
|
|
if ( $job_qualify ) { |
|
138
|
|
|
if ( ! array_key_exists( $req_path, $ao_ccss_queue ) ) { |
|
139
|
|
|
// This is a NEW job |
|
140
|
|
|
// Merge job into the queue. |
|
141
|
|
|
$ao_ccss_queue[ $req_path ] = $self->ao_ccss_define_job( |
|
142
|
|
|
$req_path, |
|
143
|
|
|
$target_rule, |
|
144
|
|
|
$req_type, |
|
145
|
|
|
$hash, |
|
146
|
|
|
null, |
|
147
|
|
|
null, |
|
148
|
|
|
null, |
|
149
|
|
|
null, |
|
150
|
|
|
true |
|
151
|
|
|
); |
|
152
|
|
|
// Set update flag. |
|
153
|
|
|
$queue_update = true; |
|
154
|
|
|
} else { |
|
155
|
|
|
// This is an existing job |
|
156
|
|
|
// The job is still NEW, most likely this is extra CSS file for the same page that needs a hash. |
|
157
|
|
|
if ( 'NEW' == $ao_ccss_queue[ $req_path ]['jqstat'] ) { |
|
158
|
|
|
// Add hash if it's not already in the job. |
|
159
|
|
|
if ( ! in_array( $hash, $ao_ccss_queue[ $req_path ]['hashes'] ) ) { |
|
160
|
|
|
// Push new hash to its array and update flag. |
|
161
|
|
|
$queue_update = array_push( $ao_ccss_queue[ $req_path ]['hashes'], $hash ); |
|
|
|
|
|
|
162
|
|
|
|
|
163
|
|
|
// Log job update. |
|
164
|
|
|
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 ); |
|
165
|
|
|
|
|
166
|
|
|
// Return from here as the hash array is already updated. |
|
167
|
|
|
return true; |
|
168
|
|
|
} |
|
169
|
|
|
} elseif ( 'NEW' != $ao_ccss_queue[ $req_path ]['jqstat'] && 'JOB_QUEUED' != $ao_ccss_queue[ $req_path ]['jqstat'] && 'JOB_ONGOING' != $ao_ccss_queue[ $req_path ]['jqstat'] ) { |
|
170
|
|
|
// Allow requeuing jobs that are not NEW, JOB_QUEUED or JOB_ONGOING |
|
171
|
|
|
// Merge new job keeping some previous job values. |
|
172
|
|
|
$ao_ccss_queue[ $req_path ] = $self->ao_ccss_define_job( |
|
173
|
|
|
$req_path, |
|
174
|
|
|
$target_rule, |
|
175
|
|
|
$req_type, |
|
176
|
|
|
$hash, |
|
177
|
|
|
$ao_ccss_queue[ $req_path ]['file'], |
|
178
|
|
|
$ao_ccss_queue[ $req_path ]['jid'], |
|
179
|
|
|
$ao_ccss_queue[ $req_path ]['jrstat'], |
|
180
|
|
|
$ao_ccss_queue[ $req_path ]['jvstat'], |
|
181
|
|
|
false |
|
182
|
|
|
); |
|
183
|
|
|
// Set update flag. |
|
184
|
|
|
$queue_update = true; |
|
185
|
|
|
} |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
if ( $queue_update ) { |
|
189
|
|
|
// Persist the job to the queue and return. |
|
190
|
|
|
$ao_ccss_queue_raw = json_encode( $ao_ccss_queue ); |
|
191
|
|
|
update_option( 'autoptimize_ccss_queue', $ao_ccss_queue_raw, false ); |
|
192
|
|
|
return true; |
|
193
|
|
|
} else { |
|
194
|
|
|
// Or just return false if no job was added. |
|
195
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'A job for path <' . $req_path . '> already exist with NEW or PENDING status, skipping job creation', 3 ); |
|
196
|
|
|
return false; |
|
197
|
|
|
} |
|
198
|
|
|
} |
|
199
|
|
|
} |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
public function ao_ccss_get_type() { |
|
203
|
|
|
// Get the type of a page |
|
204
|
|
|
// Attach the conditional tags array. |
|
205
|
|
|
global $ao_ccss_types; |
|
206
|
|
|
global $ao_ccss_forcepath; |
|
207
|
|
|
|
|
208
|
|
|
// By default, a page type is false. |
|
209
|
|
|
$page_type = false; |
|
210
|
|
|
|
|
211
|
|
|
// Iterates over the array to match a type. |
|
212
|
|
|
foreach ( $ao_ccss_types as $type ) { |
|
213
|
|
|
if ( is_404() ) { |
|
214
|
|
|
$page_type = 'is_404'; |
|
215
|
|
|
break; |
|
216
|
|
|
} elseif ( is_front_page() ) { |
|
217
|
|
|
// identify frontpage immediately to avoid it also matching a CPT or template. |
|
218
|
|
|
$page_type = 'is_front_page'; |
|
219
|
|
|
break; |
|
220
|
|
|
} elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) { |
|
221
|
|
|
// Match custom post types and not page or page not forced to path-based. |
|
222
|
|
|
if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) { |
|
223
|
|
|
$page_type = $type; |
|
224
|
|
|
break; |
|
225
|
|
|
} |
|
226
|
|
|
} elseif ( strpos( $type, 'template_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) { |
|
227
|
|
|
// Match templates if not page or if page is not forced to path-based. |
|
228
|
|
|
if ( is_page_template( substr( $type, 9 ) ) ) { |
|
229
|
|
|
$page_type = $type; |
|
230
|
|
|
break; |
|
231
|
|
|
} |
|
232
|
|
|
} else { |
|
233
|
|
|
// Match all other existing types |
|
234
|
|
|
// but remove prefix to be able to check if the function exists & returns true. |
|
235
|
|
|
$_type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type ); |
|
236
|
|
|
if ( function_exists( $_type ) && call_user_func( $_type ) ) { |
|
237
|
|
|
// Make sure we only return for one page, not for the "paged pages" (/page/2 ..). |
|
238
|
|
|
if ( ! is_page() || ! is_paged() ) { |
|
239
|
|
|
$page_type = $type; |
|
240
|
|
|
break; |
|
241
|
|
|
} |
|
242
|
|
|
} |
|
243
|
|
|
} |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
// Return the page type. |
|
247
|
|
|
return $page_type; |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
public function ao_ccss_define_job( $path, $target, $type, $hash, $file, $jid, $jrstat, $jvstat, $create ) { |
|
|
|
|
|
|
251
|
|
|
// Define a job entry to be created or updated |
|
252
|
|
|
// Define commom job properties. |
|
253
|
|
|
$path = array(); |
|
254
|
|
|
$path['ljid'] = $this->ao_ccss_job_id(); |
|
255
|
|
|
$path['rtarget'] = $target; |
|
256
|
|
|
$path['ptype'] = $type; |
|
257
|
|
|
$path['hashes'] = array( $hash ); |
|
258
|
|
|
$path['hash'] = $hash; |
|
259
|
|
|
$path['file'] = $file; |
|
260
|
|
|
$path['jid'] = $jid; |
|
261
|
|
|
$path['jqstat'] = 'NEW'; |
|
262
|
|
|
$path['jrstat'] = $jrstat; |
|
263
|
|
|
$path['jvstat'] = $jvstat; |
|
264
|
|
|
$path['jctime'] = microtime( true ); |
|
265
|
|
|
$path['jftime'] = null; |
|
266
|
|
|
|
|
267
|
|
|
// Set operation requested. |
|
268
|
|
|
if ( $create ) { |
|
269
|
|
|
$operation = 'CREATED'; |
|
270
|
|
|
} else { |
|
271
|
|
|
$operation = 'UPDATED'; |
|
272
|
|
|
} |
|
273
|
|
|
|
|
274
|
|
|
// Log job creation. |
|
275
|
|
|
autoptimizeCriticalCSSCore::ao_ccss_log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 ); |
|
276
|
|
|
|
|
277
|
|
|
return $path; |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
|
public function ao_ccss_job_id( $length = 6 ) { |
|
281
|
|
|
// Generate random strings for the local job ID |
|
282
|
|
|
// Based on https://stackoverflow.com/a/4356295 . |
|
283
|
|
|
$characters = '0123456789abcdefghijklmnopqrstuvwxyz'; |
|
284
|
|
|
$characters_length = strlen( $characters ); |
|
285
|
|
|
$random_string = 'j-'; |
|
286
|
|
|
for ( $i = 0; $i < $length; $i++ ) { |
|
287
|
|
|
$random_string .= $characters[ rand( 0, $characters_length - 1 ) ]; |
|
288
|
|
|
} |
|
289
|
|
|
return $random_string; |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
public function ao_ccss_ua() { |
|
293
|
|
|
// Check for criticalcss.com user agent. |
|
294
|
|
|
$agent = ''; |
|
295
|
|
|
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { |
|
296
|
|
|
$agent = $_SERVER['HTTP_USER_AGENT']; |
|
297
|
|
|
} |
|
298
|
|
|
|
|
299
|
|
|
// Check for UA and return TRUE when criticalcss.com is the detected UA, false when not. |
|
300
|
|
|
$rtn = strpos( $agent, AO_CCSS_URL ); |
|
301
|
|
|
if ( 0 === $rtn ) { |
|
302
|
|
|
$rtn = true; |
|
303
|
|
|
} else { |
|
304
|
|
|
$rtn = false; |
|
305
|
|
|
} |
|
306
|
|
|
return ( $rtn ); |
|
307
|
|
|
} |
|
308
|
|
|
} |
|
309
|
|
|
|
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.