PluginProbe
Autoptimize / 2.8.0
Autoptimize v2.8.0
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 2.7.8 All 107 releases
autoptimize / classes / autoptimizeCriticalCSSEnqueue.php

autoptimizeCriticalCSSEnqueue.php in Autoptimize 2.8.0, at classes/autoptimizeCriticalCSSEnqueue.php

291 lines 13.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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_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;
50
51 // Match for paths in rules.
52 foreach ( $ao_ccss_rules['paths'] as $path => $props ) {
53
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 );
57
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 ) ) ) {
62
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 );
67
68 // Stop processing other path rules.
69 break;
70 }
71 }
72
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 ) {
76
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 );
80
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 );
87
88 // Stop processing other type rules.
89 break;
90 }
91 }
92 }
93
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;
102
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 }
113 } else {
114 $target_rule = 'types|' . $req_type;
115 }
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 } 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 );
120 }
121
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(
128 $req_path,
129 $target_rule,
130 $req_type,
131 $hash,
132 null,
133 null,
134 null,
135 null,
136 true
137 );
138 // Set update flag.
139 $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 }
173
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 }
184 }
185 }
186 }
187
188 public function ao_ccss_get_type() {
189 // Get the type of a page
190 // Attach the conditional tags array.
191 global $ao_ccss_types;
192 global $ao_ccss_forcepath;
193
194 // By default, a page type is false.
195 $page_type = false;
196
197 // Iterates over the array to match a type.
198 foreach ( $ao_ccss_types as $type ) {
199 if ( is_404() ) {
200 $page_type = 'is_404';
201 break;
202 } elseif ( strpos( $type, 'custom_post_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) {
203 // Match custom post types and not page or page not forced to path-based.
204 if ( get_post_type( get_the_ID() ) === substr( $type, 12 ) ) {
205 $page_type = $type;
206 break;
207 }
208 } elseif ( strpos( $type, 'template_' ) !== false && ( ! $ao_ccss_forcepath || ! is_page() ) ) {
209 // Match templates if not page or if page is not forced to path-based.
210 if ( is_page_template( substr( $type, 9 ) ) ) {
211 $page_type = $type;
212 break;
213 }
214 } else {
215 // Match all other existing types
216 // but remove prefix to be able to check if the function exists & returns true.
217 $_type = str_replace( array( 'woo_', 'bp_', 'bbp_', 'edd_' ), '', $type );
218 if ( function_exists( $_type ) && call_user_func( $_type ) ) {
219 // Make sure we only return for one page, not for the "paged pages" (/page/2 ..).
220 if ( ! is_page() || ! is_paged() ) {
221 $page_type = $type;
222 break;
223 }
224 }
225 }
226 }
227
228 // Return the page type.
229 return $page_type;
230 }
231
232 public function ao_ccss_define_job( $path, $target, $type, $hash, $file, $jid, $jrstat, $jvstat, $create ) {
233 // Define a job entry to be created or updated
234 // Define commom job properties.
235 $path = array();
236 $path['ljid'] = $this->ao_ccss_job_id();
237 $path['rtarget'] = $target;
238 $path['ptype'] = $type;
239 $path['hashes'] = array( $hash );
240 $path['hash'] = $hash;
241 $path['file'] = $file;
242 $path['jid'] = $jid;
243 $path['jqstat'] = 'NEW';
244 $path['jrstat'] = $jrstat;
245 $path['jvstat'] = $jvstat;
246 $path['jctime'] = microtime( true );
247 $path['jftime'] = null;
248
249 // Set operation requested.
250 if ( $create ) {
251 $operation = 'CREATED';
252 } else {
253 $operation = 'UPDATED';
254 }
255
256 // Log job creation.
257 autoptimizeCriticalCSSCore::ao_ccss_log( 'Job ' . $operation . ' with local job id <' . $path['ljid'] . '> for target rule <' . $target . '>', 3 );
258
259 return $path;
260 }
261
262 public function ao_ccss_job_id( $length = 6 ) {
263 // Generate random strings for the local job ID
264 // Based on https://stackoverflow.com/a/4356295 .
265 $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
266 $characters_length = strlen( $characters );
267 $random_string = 'j-';
268 for ( $i = 0; $i < $length; $i++ ) {
269 $random_string .= $characters[ rand( 0, $characters_length - 1 ) ];
270 }
271 return $random_string;
272 }
273
274 public function ao_ccss_ua() {
275 // Check for criticalcss.com user agent.
276 $agent = '';
277 if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
278 $agent = $_SERVER['HTTP_USER_AGENT'];
279 }
280
281 // Check for UA and return TRUE when criticalcss.com is the detected UA, false when not.
282 $rtn = strpos( $agent, AO_CCSS_URL );
283 if ( 0 === $rtn ) {
284 $rtn = true;
285 } else {
286 $rtn = false;
287 }
288 return ( $rtn );
289 }
290 }
291