PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
← All changes | includes/class-wp-optimize-browser-cache.php +283 -62 4.5.4 → 4.7.0 View file →
@@ -30,8 +30,10 @@
30 30
31 31 $this->_htaccess = new WP_Optimize_Htaccess();
32 32
33 33 $this->_options = $this->_wp_optimize->get_options();
34 +
35 + $this->maybe_restore_expire_values_from_htaccess();
34 36 }
35 37
36 38 /**
37 39 * Returns singleton instance object
@@ -51,17 +53,14 @@
51 53 *
52 54 * @return bool|WP_Error
53 55 **/
54 56 public function is_enabled() {
55 -
56 - static $is_enabled;
57 - if (isset($is_enabled)) return $is_enabled;
58 -
59 57 $headers = $this->get_stylesheet_headers();
60 58
61 59 if (is_wp_error($headers)) return $headers;
62 60
63 - if (array_key_exists('cache-control', $headers) && array_key_exists('expires', $headers)) {
61 + $cache_control = isset($headers['cache-control']) ? (is_array($headers['cache-control']) ? implode(', ', $headers['cache-control']) : $headers['cache-control']) : '';
62 + if (array_key_exists('expires', $headers) && array_key_exists('cache-control', $headers) && preg_match('/\b(public|private)\b/i', $cache_control)) {
64 63 $is_enabled = true;
65 64 } else {
66 65 $is_enabled = false;
67 66 }
@@ -73,15 +72,62 @@
73 72 return $is_enabled;
74 73 }
75 74
76 75 /**
76 + * Get expire days and hours values from options.
77 + *
78 + * @return array // [0] - expire days, [1] - expire hours
79 + */
80 + private function get_expire_data() {
81 + return array(
82 + absint($this->_options->get_option('browser_cache_expire_days', 0)),
83 + absint($this->_options->get_option('browser_cache_expire_hours', 0)),
84 + );
85 + }
86 +
87 +
88 + /**
89 + * Check if .htaccess has already updated settings (manually by user) then reset error message output
90 + *
91 + * @return void
92 + */
93 + public function maybe_reset_error_message_output() {
94 + $error_message = $this->_options->get_option('browser_cache_error_message', '');
95 + if ('' === $error_message) return;
96 +
97 + list($expire_days, $expire_hours) = $this->get_expire_data();
98 +
99 + $expire = $this->prepare_interval($expire_days, $expire_hours);
100 + $search = 'ExpiresByType text/css "access '.$expire.'"';
101 +
102 + // if we already have setting in the file then we can reset error message output
103 + if (false !== strpos($this->_htaccess->get_content(), $search)) {
104 + $this->_options->update_option('browser_cache_error_message', '');
105 + $this->_options->update_option('browser_cache_output', '');
106 + }
107 + }
108 +
109 + /**
110 + * Set expire days and hours values.
111 + *
112 + * @param int $expire_days
113 + * @param int $expire_hours
114 + * @return void
115 + */
116 + private function set_expire_values($expire_days, $expire_hours) {
117 + $this->_options->update_option('browser_cache_expire_days', absint($expire_days));
118 + $this->_options->update_option('browser_cache_expire_hours', absint($expire_hours));
119 + }
120 +
121 + /**
77 122 * Enable browser cache - add settings into .htaccess.
78 123 *
79 - * @param int $expiry_days
80 - * @param int $expiry_hours
124 + * @return void
81 125 */
82 - public function enable(int $expiry_days, int $expiry_hours) {
83 - $this->_htaccess->update_commented_section($this->prepare_browser_cache_section($expiry_days, $expiry_hours), $this->_htaccess_section_comment);
126 + public function enable() {
127 + list($expire_days, $expire_hours) = $this->get_expire_data();
128 +
129 + $this->_htaccess->update_commented_section($this->prepare_browser_cache_section($expire_days, $expire_hours), $this->_htaccess_section_comment);
84 130 $this->_htaccess->write_file();
85 131 $this->_options->update_option('enable_browser_cache', true);
86 132 }
87 133
@@ -86,8 +132,10 @@
86 132 }
87 133
88 134 /**
89 135 * Disable cache - remove settings from .htaccess added in enable() function.
136 + *
137 + * @return void
90 138 */
91 139 public function disable() {
92 140 $this->_htaccess->remove_commented_section($this->_htaccess_section_comment);
93 141 $this->_htaccess->write_file();
@@ -94,20 +142,59 @@
94 142 $this->_options->update_option('enable_browser_cache', false);
95 143 }
96 144
97 145 /**
146 + * Restore expiration settings from .htaccess if the options are empty.
147 + *
148 + * @return void
149 + */
150 + public function maybe_restore_expire_values_from_htaccess() {
151 + $expire_days = $this->_options->get_option('browser_cache_expire_days', '');
152 + $expire_hours = $this->_options->get_option('browser_cache_expire_hours', '');
153 +
154 + if ('' === $expire_days && '' === $expire_hours) {
155 + $this->restore_expire_values_from_htaccess();
156 + }
157 + }
158 +
159 + /**
98 160 * Check if browser cache option is set to true then add section with browser cache settings into .htaccess.
161 + *
162 + * @return void
99 163 */
100 164 public function restore() {
101 - $expire_days = absint($this->_options->get_option('browser_cache_expire_days', 0));
102 - $expire_hours = absint($this->_options->get_option('browser_cache_expire_hours', 0));
165 + list($expire_days, $expire_hours) = $this->get_expire_data();
103 166
104 167 $enabled = 0 !== $expire_days || 0 !== $expire_hours;
105 168
106 - if ($enabled && $this->_htaccess->is_writable()) $this->enable($expire_days, $expire_hours);
169 + // If we don't have values in the database try to read them from .htaccess file
170 + if (!$enabled) {
171 + $this->restore_expire_values_from_htaccess();
172 +
173 + list($expire_days, $expire_hours) = $this->get_expire_data();
174 + $enabled = 0 !== $expire_days || 0 !== $expire_hours;
175 + }
176 +
177 + if ($enabled && $this->_htaccess->is_writable()) $this->enable();
107 178 }
108 179
109 180 /**
181 + * Restore expire days and expire hours values form .htaccess if possible.
182 + *
183 + * @return void
184 + */
185 + private function restore_expire_values_from_htaccess() {
186 + $htaccess_values = $this->get_browser_cache_section_expire_values();
187 + list($expire_days, $expire_hours) = $htaccess_values;
188 +
189 + $enabled = 0 !== $expire_days || 0 !== $expire_hours;
190 +
191 + if ($enabled) {
192 + $this->set_expire_values($expire_days, $expire_hours);
193 + }
194 + }
195 +
196 + /**
110 197 * Check if section with browser cache settings already exists.
111 198 *
112 199 * @return bool
113 200 */
@@ -115,8 +202,74 @@
115 202 return $this->_htaccess->is_commented_section_exists($this->_htaccess_section_comment);
116 203 }
117 204
118 205 /**
206 + * Get expire days and hours values from the .htaccess file
207 + *
208 + * @return array|bool
209 + */
210 + private function get_browser_cache_section_expire_values() {
211 + $section_content = $this->_htaccess->get_section_content();
212 +
213 + if (preg_match('/ExpiresByType text\/css "access ([0-9a-z\s]+)"/i', $section_content, $matches)) {
214 + return $this->expire_string_to_days_hours($matches[1]);
215 + }
216 +
217 + return array(0, 0);
218 + }
219 +
220 + /**
221 + * Returns days and hours values from expire string
222 + *
223 + * @param string $string
224 + * @return array
225 + */
226 + private function expire_string_to_days_hours($string) {
227 + $days = 0;
228 + $hours = 0;
229 +
230 + preg_match_all(
231 + '/(\d+)\s+(year|years|month|months|day|days|hour|hours)/i',
232 + $string,
233 + $matches,
234 + PREG_SET_ORDER
235 + );
236 +
237 + foreach ($matches as $match) {
238 + $value = (int) $match[1];
239 + $unit = strtolower($match[2]);
240 +
241 + switch ($unit) {
242 + case 'year':
243 + case 'years':
244 + $days += $value * 365;
245 + break;
246 +
247 + case 'month':
248 + case 'months':
249 + $days += $value * 30;
250 + break;
251 +
252 + case 'day':
253 + case 'days':
254 + $days += $value;
255 + break;
256 +
257 + case 'hour':
258 + case 'hours':
259 + $hours += $value;
260 + break;
261 + }
262 + }
263 +
264 + // Convert extra hours into days
265 + $days += intdiv($hours, 24);
266 + $hours %= 24;
267 +
268 + return array(absint($days), absint($hours));
269 + }
270 +
271 + /**
119 272 * Handle for enable_browser_cache command used in WP_Optimize_Commands.
120 273 *
121 274 * @param array $params - ['browser_cache_expire' => '1 month 15 days 2 hours' || '' - for disable cache]
122 275 * @return array
@@ -124,15 +277,19 @@
124 277 public function enable_browser_cache_command_handler(array $params): array {
125 278 $expire_days = absint($params['browser_cache_expire_days']);
126 279 $expire_hours = absint($params['browser_cache_expire_hours']);
127 280
128 - $current_expire_days = absint($this->_options->get_option('browser_cache_expire_days', 0));
129 - $current_expire_hours = absint($this->_options->get_option('browser_cache_expire_hours', 0));
281 + list($current_expire_days, $current_expire_hours) = $this->get_browser_cache_section_expire_values();
130 282
131 - $section_updated = false;
283 + // clear error message output
284 + $this->_options->update_option('browser_cache_error_message', '');
285 + $this->_options->update_option('browser_cache_output', '');
132 286
133 287 $enable = 0 !== $expire_days || 0 !== $expire_hours;
134 288
289 + // Store new expire values in the database
290 + $this->set_expire_values($expire_days, $expire_hours);
291 +
135 292 /**
136 293 * If we don't need to do anything in .htaccess then return message.
137 294 */
138 295 if ($enable === $this->_htaccess->is_commented_section_exists()
@@ -151,65 +308,51 @@
151 308 'message' => $message,
152 309 );
153 310 }
154 311
155 - if ($this->_htaccess->is_writable()) {
156 - // update commented section
312 + $section_updated = $this->update_htaccess_section($enable);
157 313
158 - if ($enable) {
159 - $this->enable($expire_days, $expire_hours);
160 - } else {
161 - $this->disable();
162 - }
314 + $is_enabled = $this->is_enabled();
163 315
164 - // read updated file.
165 - $this->_htaccess->read_file();
166 - // check if section added or removed successfully.
167 - $section_exists = $this->_htaccess->is_commented_section_exists();
168 - // set correct $section-updated flag.
169 - $section_updated = $enable === $section_exists;
170 - }
171 -
172 316 if ($section_updated) {
173 - $enabled = $this->is_enabled();
174 317
175 - // save $expire value to options.
176 - $this->_options->update_option('browser_cache_expire_days', $expire_days);
177 - $this->_options->update_option('browser_cache_expire_hours', $expire_hours);
178 -
179 - if (is_wp_error($enabled)) {
318 + if (is_wp_error($is_enabled)) {
180 319 return array(
181 320 'success' => true,
182 - 'enabled' => $enabled,
183 - 'error_message' => $enabled->get_error_message(),
321 + 'enabled' => false,
322 + 'error_message' => $is_enabled->get_error_message(),
184 323 );
185 324 } else {
186 325 return array(
187 326 'success' => true,
188 - 'enabled' => $enabled,
327 + 'enabled' => $is_enabled,
189 328 'message' => __('We successfully updated your .htaccess file.', 'wp-optimize'),
190 329 );
191 330 }
192 331 } else {
193 - $cache_section = $this->prepare_browser_cache_section($expire_days, $expire_hours);
194 332
195 333 if ($enable) {
196 334 // translators: %s is a file name
197 335 $message = sprintf(__("We can't update your %s file.", 'wp-optimize'), $this->_htaccess->get_filename()) . ' ' . __('Please try to add following lines manually:', 'wp-optimize');
198 - $output = htmlentities($this->_htaccess->get_section_begin_comment() . PHP_EOL .
199 - join(PHP_EOL, $this->_htaccess->get_flat_array($cache_section)).
200 - PHP_EOL . $this->_htaccess->get_section_end_comment());
201 336 } else {
202 337 // translators: %s is a file name
203 338 $message = sprintf(__("We can't update your %s file.", 'wp-optimize'), $this->_htaccess->get_filename()) . ' ' . __('Please try to remove following lines manually:', 'wp-optimize');
204 - $output = htmlentities($this->_htaccess->get_section_begin_comment() . PHP_EOL .
205 - ' ... ... ... '.
206 - PHP_EOL . $this->_htaccess->get_section_end_comment());
207 339 }
208 340
341 + $output = htmlentities($this->get_htaccess_section_output($enable));
342 +
343 + // store error message output
344 + $this->_options->update_option('browser_cache_error_message', $message);
345 + $this->_options->update_option('browser_cache_output', $output);
346 +
347 + if (is_wp_error($is_enabled)) {
348 + $message .= ' ' .$is_enabled->get_error_message();
349 + $is_enabled = false;
350 + }
351 +
209 352 return array(
210 353 'success' => false,
211 - 'enabled' => $this->is_enabled(),
354 + 'enabled' => $is_enabled,
212 355 'error_message' => $message,
213 356 'output' => $output,
214 357 );
215 358 }
@@ -215,10 +358,61 @@
215 358 }
216 359 }
217 360
218 361 /**
219 - * Use $days an $hours values to build correct time interval as a string like '2 days 3 hours' or empty string if date is empty.
362 + * Update the .htaccess section for browser caching.
220 363 *
364 + * @param boolean $enable
365 + * @return bool True if section updated successfully, false otherwise
366 + */
367 + private function update_htaccess_section(bool $enable) {
368 +
369 + if (!$this->_htaccess->is_writable()) {
370 + return false;
371 + }
372 +
373 + // update commented section
374 + if ($enable) {
375 + $this->enable();
376 + } else {
377 + $this->disable();
378 + }
379 +
380 + // read updated file.
381 + $this->_htaccess->read_file();
382 + // check if section added or removed successfully.
383 + $section_exists = $this->_htaccess->is_commented_section_exists();
384 + // return true if section updated successfully, false otherwise.
385 + return $enable === $section_exists;
386 + }
387 +
388 + /**
389 + * Get output content for enable/disable browser cache in .htaccess.
390 + *
391 + * @param bool $enable
392 + * @return string
393 + */
394 + private function get_htaccess_section_output($enable) {
395 + list($expire_days, $expire_hours) = $this->get_expire_data();
396 +
397 + if ($enable) {
398 + $cache_section = $this->prepare_browser_cache_section($expire_days, $expire_hours);
399 +
400 + $output = $this->_htaccess->get_section_begin_comment() . PHP_EOL .
401 + join(PHP_EOL, $this->_htaccess->get_flat_array($cache_section)).
402 + PHP_EOL . $this->_htaccess->get_section_end_comment();
403 + } else {
404 + $output = $this->_htaccess->get_section_begin_comment() . PHP_EOL .
405 + ' ... ... ... '.
406 + PHP_EOL . $this->_htaccess->get_section_end_comment();
407 + }
408 +
409 + return $output;
410 + }
411 +
412 + /**
413 + * Use $days and $hours values to build correct time interval as a string like '2 days 3 hours' or empty string if date is empty.
414 + *
221 415 * @param int $days
222 416 * @param int $hours
223 417 * @return string
224 418 */
@@ -284,12 +478,16 @@
284 478 $max_age = $this->convert_to_seconds($expire_days, $expire_hours);
285 479
286 480 return array(
287 481 array(
482 + '<IfModule mod_setenvif.c>',
483 + 'SetEnvIf Request_URI "/wp-admin/" WPO_NO_CACHE',
484 + '</IfModule>',
485 + ),
486 + array(
288 487 '<IfModule mod_expires.c>',
289 488 'ExpiresActive On',
290 489 'ExpiresByType text/css "access '.$expire.'"',
291 - 'ExpiresByType text/html "access '.$expire.'"',
292 490 'ExpiresByType image/gif "access '.$expire.'"',
293 491 'ExpiresByType image/png "access '.$expire.'"',
294 492 'ExpiresByType image/jpg "access '.$expire.'"',
295 493 'ExpiresByType image/jpeg "access '.$expire.'"',
@@ -298,11 +496,19 @@
298 496 'ExpiresByType application/pdf "access '.$expire.'"',
299 497 'ExpiresByType application/javascript "access '.$expire.'"',
300 498 'ExpiresByType text/x-javascript "access '.$expire.'"',
301 499 'ExpiresByType application/x-shockwave-flash "access '.$expire.'"',
500 + 'ExpiresByType image/svg+xml "access '.$expire.'"',
501 + 'ExpiresByType font/woff "access '.$expire.'"',
502 + 'ExpiresByType font/woff2 "access '.$expire.'"',
503 + 'ExpiresByType font/x-woff "access '.$expire.'"',
504 + 'ExpiresByType application/font-woff "access '.$expire.'"',
505 + 'ExpiresByType application/x-font-woff "access '.$expire.'"',
506 + 'ExpiresByType font/ttf "access '.$expire.'"',
507 + 'ExpiresByType font/otf "access '.$expire.'"',
508 + 'ExpiresByType application/vnd.ms-fontobject "access '.$expire.'"',
302 509 'ExpiresByType application/json "access 0 seconds"',
303 510 'ExpiresByType application/xml "access 0 seconds"',
304 - 'ExpiresDefault "access '.$expire.'"',
305 511 '</IfModule>',
306 512 ),
307 513 '',
308 514 array(
@@ -307,27 +513,42 @@
307 513 '',
308 514 array(
309 515 '<IfModule mod_headers.c>',
310 516 array(
311 - '<filesMatch "\.(ico|jpe?g|png|gif|webp|swf)$">',
312 - 'Header set Cache-Control "public, max-age='.$max_age.'"',
313 - '</filesMatch>',
517 + '<FilesMatch "\.(ico|jpe?g|png|gif|webp|swf|svg)$">',
518 + 'Header set Cache-Control "public, max-age='.$max_age.'" env=!WPO_NO_CACHE',
519 + '</FilesMatch>',
314 520 ),
315 521 array(
316 - '<filesMatch "\.(css)$">',
317 - 'Header set Cache-Control "public, max-age='.$max_age.'"',
318 - '</filesMatch>',
522 + '<FilesMatch "\.(css)$">',
523 + 'Header set Cache-Control "public, max-age='.$max_age.'" env=!WPO_NO_CACHE',
524 + '</FilesMatch>',
319 525 ),
320 526 array(
321 - '<filesMatch "\.(js)$">',
322 - 'Header set Cache-Control "private, max-age='.$max_age.'"',
323 - '</filesMatch>',
527 + '<FilesMatch "\.(js)$">',
528 + 'Header set Cache-Control "private, max-age='.$max_age.'" env=!WPO_NO_CACHE',
529 + '</FilesMatch>',
324 530 ),
325 531 array(
326 - '<filesMatch "\.(x?html?|php)$">',
327 - 'Header set Cache-Control "private, must-revalidate, max-age='.$max_age.'"',
328 - '</filesMatch>',
532 + '<FilesMatch "\.(woff2?|ttf|otf|eot)$">',
533 + 'Header set Cache-Control "public, max-age='.$max_age.'" env=!WPO_NO_CACHE',
534 + '</FilesMatch>',
329 535 ),
536 + array(
537 + '<FilesMatch "\.(x?html?)$">',
538 + 'Header set Cache-Control "private, must-revalidate, max-age='.$max_age.'" env=!WPO_NO_CACHE',
539 + '</FilesMatch>',
540 + ),
541 + array(
542 + '<FilesMatch "\.php$">',
543 + 'Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"',
544 + '</FilesMatch>',
545 + ),
546 + '</IfModule>',
547 + ),
548 + array(
549 + '<IfModule mod_headers.c>',
550 + 'Header unset Expires env=WPO_NO_CACHE',
330 551 '</IfModule>',
331 552 ),
332 553 '',
333 554 '#Disable ETag',