PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.4
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.4
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
wp-optimize / includes / class-wp-optimize-browser-cache.php

class-wp-optimize-browser-cache.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.4, at includes/class-wp-optimize-browser-cache.php

338 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) die('No direct access allowed');
4
5 /**
6 * Class WP_Optimize_Browser_Cache
7 */
8 class WP_Optimize_Browser_Cache {
9
10 use WP_Optimize_HTTP_Error_Codes_Trait;
11
12 private $_htaccess;
13
14 private $_options;
15
16 private $_wp_optimize;
17
18 /**
19 * Browser cache section in htaccess will be wrapped with this comment
20 *
21 * @var string
22 */
23 private $_htaccess_section_comment = 'WP-Optimize Browser Cache';
24
25 /**
26 * WP_Optimize_Browser_Cache constructor.
27 */
28 public function __construct() {
29 $this->_wp_optimize = WP_Optimize();
30
31 $this->_htaccess = new WP_Optimize_Htaccess();
32
33 $this->_options = $this->_wp_optimize->get_options();
34 }
35
36 /**
37 * Returns singleton instance object
38 *
39 * @return WP_Optimize_Browser_Cache Returns `WP_Optimize_Browser_Cache` object
40 */
41 public static function instance() {
42 static $_instance = null;
43 if (null === $_instance) {
44 $_instance = new self();
45 }
46 return $_instance;
47 }
48
49 /**
50 * Check headers for Cache-Control and Etag. And if they are exist return true.
51 *
52 * @return bool|WP_Error
53 **/
54 public function is_enabled() {
55
56 static $is_enabled;
57 if (isset($is_enabled)) return $is_enabled;
58
59 $headers = $this->get_stylesheet_headers();
60
61 if (is_wp_error($headers)) return $headers;
62
63 if (array_key_exists('cache-control', $headers) && array_key_exists('expires', $headers)) {
64 $is_enabled = true;
65 } else {
66 $is_enabled = false;
67 }
68
69 if ($this->is_browser_cache_section_exists() && false === $this->_wp_optimize->is_apache_module_loaded(array('mod_expires', 'mod_headers'))) {
70 $is_enabled = new WP_Error('Browser cache', __('We successfully updated your .htaccess file.', 'wp-optimize') . ' ' . __('But it seems one of Apache modules - mod_expires or mod_headers is not active.', 'wp-optimize'));
71 }
72
73 return $is_enabled;
74 }
75
76 /**
77 * Enable browser cache - add settings into .htaccess.
78 *
79 * @param int $expiry_days
80 * @param int $expiry_hours
81 */
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);
84 $this->_htaccess->write_file();
85 $this->_options->update_option('enable_browser_cache', true);
86 }
87
88 /**
89 * Disable cache - remove settings from .htaccess added in enable() function.
90 */
91 public function disable() {
92 $this->_htaccess->remove_commented_section($this->_htaccess_section_comment);
93 $this->_htaccess->write_file();
94 $this->_options->update_option('enable_browser_cache', false);
95 }
96
97 /**
98 * Check if browser cache option is set to true then add section with browser cache settings into .htaccess.
99 */
100 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));
103
104 $enabled = 0 !== $expire_days || 0 !== $expire_hours;
105
106 if ($enabled && $this->_htaccess->is_writable()) $this->enable($expire_days, $expire_hours);
107 }
108
109 /**
110 * Check if section with browser cache settings already exists.
111 *
112 * @return bool
113 */
114 public function is_browser_cache_section_exists() {
115 return $this->_htaccess->is_commented_section_exists($this->_htaccess_section_comment);
116 }
117
118 /**
119 * Handle for enable_browser_cache command used in WP_Optimize_Commands.
120 *
121 * @param array $params - ['browser_cache_expire' => '1 month 15 days 2 hours' || '' - for disable cache]
122 * @return array
123 */
124 public function enable_browser_cache_command_handler(array $params): array {
125 $expire_days = absint($params['browser_cache_expire_days']);
126 $expire_hours = absint($params['browser_cache_expire_hours']);
127
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));
130
131 $section_updated = false;
132
133 $enable = 0 !== $expire_days || 0 !== $expire_hours;
134
135 /**
136 * If we don't need to do anything in .htaccess then return message.
137 */
138 if ($enable === $this->_htaccess->is_commented_section_exists()
139 && $expire_days === $current_expire_days
140 && $expire_hours === $current_expire_hours
141 ) {
142 if ($enable) {
143 $message = __('Browser static caching settings already exists in the .htaccess file', 'wp-optimize');
144 } else {
145 $message = '';
146 }
147
148 return array(
149 'success' => true,
150 'enabled' => $enable,
151 'message' => $message,
152 );
153 }
154
155 if ($this->_htaccess->is_writable()) {
156 // update commented section
157
158 if ($enable) {
159 $this->enable($expire_days, $expire_hours);
160 } else {
161 $this->disable();
162 }
163
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 if ($section_updated) {
173 $enabled = $this->is_enabled();
174
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)) {
180 return array(
181 'success' => true,
182 'enabled' => $enabled,
183 'error_message' => $enabled->get_error_message(),
184 );
185 } else {
186 return array(
187 'success' => true,
188 'enabled' => $enabled,
189 'message' => __('We successfully updated your .htaccess file.', 'wp-optimize'),
190 );
191 }
192 } else {
193 $cache_section = $this->prepare_browser_cache_section($expire_days, $expire_hours);
194
195 if ($enable) {
196 // translators: %s is a file name
197 $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 } else {
202 // translators: %s is a file name
203 $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 }
208
209 return array(
210 'success' => false,
211 'enabled' => $this->is_enabled(),
212 'error_message' => $message,
213 'output' => $output,
214 );
215 }
216 }
217
218 /**
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.
220 *
221 * @param int $days
222 * @param int $hours
223 * @return string
224 */
225 private function prepare_interval(int $days, int $hours): string {
226
227 if (0 === $days && 0 === $hours) {
228 return '';
229 }
230
231 $parts = array();
232
233 // if hours value more than one day then fix it.
234 $days += floor($hours / 24);
235 $hours = $hours % 24;
236
237 $years = floor($days / 365);
238 $days = $days % 365;
239 $months = floor($days / 30);
240 $days = $days % 30;
241
242 if ($years > 0) {
243 $parts[] = $years . ($years > 1 ? ' years' : ' year');
244 }
245
246 if ($months > 0) {
247 $parts[] = $months . ($months > 1 ? ' months' : ' month');
248 }
249
250 if ($days > 0) {
251 $parts[] = $days . ($days > 1 ? ' days' : ' day');
252 }
253
254 if ($hours > 0) {
255 $parts[] = $hours . ($hours > 1 ? ' hours' : ' hour');
256 }
257
258 return join(' ', $parts);
259 }
260
261 /**
262 * Converts a human-readable time string into total seconds
263 *
264 * @param int $days
265 * @param int $hours
266 * @return string Total number of seconds
267 */
268 private function convert_to_seconds(int $days, int $hours): string {
269 // Split the string into parts
270 $total_seconds = $days * 24 * 60 * 60 + $hours * 60 * 60;
271
272 return strval($total_seconds);
273 }
274
275 /**
276 * Build browser cache section array.
277 *
278 * @param int $expire_days
279 * @param int $expire_hours
280 * @return array
281 */
282 public function prepare_browser_cache_section(int $expire_days, int $expire_hours): array {
283 $expire = $this->prepare_interval($expire_days, $expire_hours);
284 $max_age = $this->convert_to_seconds($expire_days, $expire_hours);
285
286 return array(
287 array(
288 '<IfModule mod_expires.c>',
289 'ExpiresActive On',
290 'ExpiresByType text/css "access '.$expire.'"',
291 'ExpiresByType text/html "access '.$expire.'"',
292 'ExpiresByType image/gif "access '.$expire.'"',
293 'ExpiresByType image/png "access '.$expire.'"',
294 'ExpiresByType image/jpg "access '.$expire.'"',
295 'ExpiresByType image/jpeg "access '.$expire.'"',
296 'ExpiresByType image/webp "access '.$expire.'"',
297 'ExpiresByType image/x-icon "access '.$expire.'"',
298 'ExpiresByType application/pdf "access '.$expire.'"',
299 'ExpiresByType application/javascript "access '.$expire.'"',
300 'ExpiresByType text/x-javascript "access '.$expire.'"',
301 'ExpiresByType application/x-shockwave-flash "access '.$expire.'"',
302 'ExpiresByType application/json "access 0 seconds"',
303 'ExpiresByType application/xml "access 0 seconds"',
304 'ExpiresDefault "access '.$expire.'"',
305 '</IfModule>',
306 ),
307 '',
308 array(
309 '<IfModule mod_headers.c>',
310 array(
311 '<filesMatch "\.(ico|jpe?g|png|gif|webp|swf)$">',
312 'Header set Cache-Control "public, max-age='.$max_age.'"',
313 '</filesMatch>',
314 ),
315 array(
316 '<filesMatch "\.(css)$">',
317 'Header set Cache-Control "public, max-age='.$max_age.'"',
318 '</filesMatch>',
319 ),
320 array(
321 '<filesMatch "\.(js)$">',
322 'Header set Cache-Control "private, max-age='.$max_age.'"',
323 '</filesMatch>',
324 ),
325 array(
326 '<filesMatch "\.(x?html?|php)$">',
327 'Header set Cache-Control "private, must-revalidate, max-age='.$max_age.'"',
328 '</filesMatch>',
329 ),
330 '</IfModule>',
331 ),
332 '',
333 '#Disable ETag',
334 'FileETag None',
335 );
336 }
337 }
338