PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.3.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.3.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
wp-optimize / cache / file-based-page-cache-functions.php

file-based-page-cache-functions.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.3.0, at cache/file-based-page-cache-functions.php

1,446 lines 45.2 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 * Extensions directory.
7 */
8 if (!defined('WPO_CACHE_EXT_DIR')) define('WPO_CACHE_EXT_DIR', dirname(__FILE__).'/extensions');
9
10 /**
11 * Holds utility functions used by file based cache
12 */
13
14 /**
15 * Cache output before it goes to the browser. If moving/renaming this function, then also change the check above.
16 *
17 * @param String $buffer Page HTML.
18 * @param Int $flags OB flags to be passed through.
19 *
20 * @return String
21 */
22 if (!function_exists('wpo_cache')) :
23 function wpo_cache($buffer, $flags) {
24
25 // This case appears to happen for unclear reasons without WP being fully loaded, e.g. https://wordpress.org/support/topic/fatal-error-since-wp-5-8-update/ . It is simplest just to short-circuit it.
26 if ('' === $buffer) return '';
27
28 // This array records reasons why no caching took place. Be careful not to allow actions to proceed that should not - i.e. take note of its state appropriately.
29 $no_cache_because = array();
30
31 if (strlen($buffer) < 255) {
32 $no_cache_because[] = sprintf(__('Output is too small (less than %d bytes) to be worth caching', 'wp-optimize'), 255);
33 }
34
35 // Don't cache pages for logged in users.
36 if (!wpo_cache_loggedin_users() && (!function_exists('is_user_logged_in') || (function_exists('wp_get_current_user') && is_user_logged_in()))) {
37 $no_cache_because[] = __('User is logged in', 'wp-optimize');
38 }
39
40 $restricted_page_type_cache = apply_filters('wpo_restricted_cache_page_type', false);
41 if ($restricted_page_type_cache) {
42 $no_cache_because[] = $restricted_page_type_cache;
43 }
44
45 $conditional_tag_exceptions = apply_filters('wpo_url_in_conditional_tags_exceptions', false);
46 if ($conditional_tag_exceptions) {
47 $no_cache_because[] = $conditional_tag_exceptions;
48 }
49
50 // No root cache folder, so short-circuit here
51 if (!file_exists(WPO_CACHE_DIR)) {
52 $no_cache_because[] = __('WP-O cache parent directory was not found', 'wp-optimize').' ('.WPO_CACHE_DIR.')';
53 } elseif (!file_exists(WPO_CACHE_FILES_DIR)) {
54 // Try creating a folder for cached files, if it was flushed recently
55 if (!mkdir(WPO_CACHE_FILES_DIR)) {
56 $no_cache_because[] = __('WP-O cache directory was not found', 'wp-optimize').' ('.WPO_CACHE_FILES_DIR.')';
57 } else {
58 wpo_disable_cache_directories_viewing();
59 }
60 }
61
62 // If comments are opened and the user has saved his information.
63 if (function_exists('comments_open') && function_exists('get_post') && get_post() && comments_open()) {
64 $commenter = wp_get_current_commenter();
65 // if any of the fields contain something, do not save to cache
66 if ('' != $commenter['comment_author'] || '' != $commenter['comment_author_email'] || '' != $commenter['comment_author_url']) {
67 $no_cache_because[] = __('Comments are opened and the visitor saved his information.', 'wp-optimize');
68 }
69 }
70
71 $can_cache_page = true;
72
73 if (defined('DONOTCACHEPAGE') && DONOTCACHEPAGE) {
74 $can_cache_page = false;
75 }
76
77 /**
78 * Defines if the page can be cached or not
79 *
80 * @param boolean $can_cache_page
81 */
82 $can_cache_page_filter = apply_filters('wpo_can_cache_page', $can_cache_page);
83
84 if (!$can_cache_page_filter) {
85 if ($can_cache_page) {
86 $can_cache_page = false;
87 $no_cache_because[] = __('wpo_can_cache_page filter forbade it', 'wp-optimize');
88 } else {
89 $no_cache_because[] = __('DONOTCACHEPAGE constant forbade it and wpo_can_cache_page filter did not over-ride it', 'wp-optimize');
90 }
91 }
92
93 if (defined('REST_REQUEST') && REST_REQUEST) {
94 $no_cache_because[] = __('This is a REST API request (identified by REST_REQUEST constant)', 'wp-optimize');
95 }
96
97 // Don't cache with fatal error pages.
98 $last_error = error_get_last();
99 if (is_array($last_error) && E_ERROR == $last_error['type']) {
100 $no_cache_because[] = __('This page has a fatal error', 'wp-optimize');
101 }
102
103 if (http_response_code() >= 500) {
104 $no_cache_because[] = sprintf(__('This page has a critical error (HTTP code %s)', 'wp-optimize'), http_response_code());
105 } elseif (http_response_code() >= 400) {
106 $no_cache_because[] = sprintf(__('This page returned an HTTP unauthorised response code (%s)', 'wp-optimize'), http_response_code());
107 }
108
109 if (empty($no_cache_because)) {
110
111 $buffer = apply_filters('wpo_pre_cache_buffer', $buffer, $flags);
112
113 $url_path = wpo_get_url_path();
114
115 $dirs = explode('/', $url_path);
116
117 $path = WPO_CACHE_FILES_DIR;
118
119 foreach ($dirs as $dir) {
120 if (!empty($dir)) {
121 $path .= '/' . $dir;
122
123 if (!file_exists($path)) {
124 if (!mkdir($path)) {
125 $no_cache_because[] = __('Attempt to create subfolder within cache directory failed', 'wp-optimize')." ($path)";
126 break;
127 }
128 }
129 }
130 }
131 }
132
133 if (!empty($no_cache_because)) {
134
135 $message = implode(', ', $no_cache_because);
136
137 // Add http headers
138 wpo_cache_add_nocache_http_header($message);
139
140 if ((!defined('DOING_CRON') || !DOING_CRON) && (!defined('REST_REQUEST') || !REST_REQUEST)) {
141 $not_cached_details = "";
142
143 // Output the reason only when the user has turned on debugging
144 if (((defined('WP_DEBUG') && WP_DEBUG) || isset($_GET['wpo_cache_debug']))) {
145 $not_cached_details = "because: ".htmlspecialchars($message) . " ";
146 }
147
148 $buffer .= sprintf("\n<!-- WP Optimize page cache - https://getwpo.com - page NOT cached %s-->\n", $not_cached_details);
149 }
150
151 return $buffer;
152
153 } else {
154
155 // Prevent mixed content when there's an http request but the site URL uses https.
156 $home_url = get_home_url();
157
158 if (!is_ssl() && 'https' === strtolower(parse_url($home_url, PHP_URL_SCHEME))) {
159 $https_home_url = $home_url;
160 $http_home_url = str_ireplace('https://', 'http://', $https_home_url);
161 $buffer = str_replace(esc_url($http_home_url), esc_url($https_home_url), $buffer);
162 }
163
164 $modified_time = time(); // Take this as soon before writing as possible
165 $timezone_string = '';
166 $utc = isset($GLOBALS['wpo_cache_config']['gmt_offset']) ? (float) $GLOBALS['wpo_cache_config']['gmt_offset'] : 0;
167 $modified_time += $utc * 3600;
168
169 if (!empty($GLOBALS['wpo_cache_config']['timezone_string'])) {
170 $timezone_string = 'UTC' !== $GLOBALS['wpo_cache_config']['timezone_string'] ? $GLOBALS['wpo_cache_config']['timezone_string'] : '';
171 }
172
173 if (!empty($timezone_string)) {
174 $timezone_postfix = "(".$timezone_string." UTC:". $utc .")";
175 } else {
176 $timezone_postfix = "(UTC:" . $utc . ")";
177 }
178
179 $add_to_footer = '';
180
181 /**
182 * Filter whether to display the html comment <!-- Cached by WP-Optimize ... -->
183 *
184 * @param boolean $show - Whether to display the html comment
185 * @return boolean
186 */
187 if (preg_match('#</html>#i', $buffer) && (apply_filters('wpo_cache_show_cached_by_comment', true) || (defined('WP_DEBUG') && WP_DEBUG))) {
188 $date_time_format = 'F j, Y g:i a';
189 if (!empty($GLOBALS['wpo_cache_config']['date_format']) && !empty($GLOBALS['wpo_cache_config']['time_format'])) {
190 $date_time_format = $GLOBALS['wpo_cache_config']['date_format'] . ' ' . $GLOBALS['wpo_cache_config']['time_format'];
191 }
192
193 if (!empty($GLOBALS['wpo_cache_config']['enable_mobile_caching']) && wpo_is_mobile()) {
194 $add_to_footer .= "\n<!-- Cached by WP-Optimize - for mobile devices - https://getwpo.com - Last modified: " . gmdate($date_time_format, $modified_time) . " " . $timezone_postfix . " -->\n";
195 } else {
196 $add_to_footer .= "\n<!-- Cached by WP-Optimize - https://getwpo.com - Last modified: " . gmdate($date_time_format, $modified_time) . " " . $timezone_postfix . " -->\n";
197 }
198 }
199
200 // Create an empty index.php file in the cache directory for disable directory viewing.
201 if (!is_file($path . '/index.php')) file_put_contents($path . '/index.php', '');
202
203 /**
204 * Save $buffer into cache file.
205 */
206 $file_ext = '.html';
207
208 if (wpo_feeds_caching_enabled()) {
209 if (is_feed()) {
210 $file_ext = '.rss-xml';
211 }
212 }
213
214 $cache_filename = wpo_cache_filename($file_ext);
215 $cache_file = $path . '/' .$cache_filename;
216
217 if (defined('WPO_CACHE_FILENAME_DEBUG') && WPO_CACHE_FILENAME_DEBUG) {
218 $add_to_footer .= "\n<!-- WP Optimize page cache debug information -->\n";
219 if (!empty($GLOBALS['wpo_cache_filename_debug']) && is_array($GLOBALS['wpo_cache_filename_debug'])) {
220 $add_to_footer .= "<!-- \n" . join("\n", array_map('htmlspecialchars', $GLOBALS['wpo_cache_filename_debug'])) . "\n --->";
221 }
222 }
223
224 // if we can then cache gzipped content in .gz file.
225 if (function_exists('gzencode') && apply_filters('wpo_allow_cache_gzip_files', true)) {
226 // Only replace inside the addition, not inside the main buffer (e.g. post content)
227 file_put_contents($cache_file . '.gz', gzencode($buffer.str_replace('by WP-Optimize', 'by WP-Optimize (gzip)', $add_to_footer), apply_filters('wpo_cache_gzip_level', 6)));
228 }
229
230 file_put_contents($cache_file, $buffer.$add_to_footer);
231
232 if (is_callable('WP_Optimize')) {
233 // delete cached information about cache size.
234 WP_Optimize()->get_page_cache()->delete_cache_size_information();
235 } else {
236 // If the shutdown occurs before plugins are loaded,
237 // then this will trigger a fatal error, so, we check first
238 if (!doing_action('shutdown')) {
239 error_log('[WPO_CACHE] WP_Optimize() is not callable.');
240 $message = 'Please report this to WP-O support: ';
241 if (function_exists('wp_debug_backtrace_summary')) {
242 $message .= wp_debug_backtrace_summary();
243 } else {
244 $message .= wpo_debug_backtrace_summary();
245 }
246 error_log($message);
247 }
248 }
249
250 header('Cache-Control: no-cache'); // Check back every time to see if re-download is necessary.
251 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT');
252 header('WPO-Cache-Status: saving to cache');
253
254 if (wpo_cache_can_output_gzip_content()) {
255
256 if (!wpo_cache_is_in_response_headers_list('Content-Encoding', 'gzip')) {
257 header('Content-Encoding: gzip');
258 }
259
260 // Disable php gzip to avoid double compression.
261 ini_set('zlib.output_compression', 'Off');
262
263 return ob_gzhandler($buffer, $flags);
264 } else {
265 return $buffer;
266 }
267 }
268 }
269 endif;
270
271 /**
272 * Load files for support plugins.
273 */
274 if (!function_exists('wpo_cache_load_extensions')) :
275 function wpo_cache_load_extensions() {
276 $extensions = glob(WPO_CACHE_EXT_DIR . '/*.php');
277
278 // Add external extensions
279 if (defined('WPO_CACHE_CUSTOM_EXT_DIR') && is_dir(WPO_CACHE_CUSTOM_EXT_DIR)) {
280 $extensions = array_merge($extensions, glob(WPO_CACHE_CUSTOM_EXT_DIR . '/*.php'));
281 }
282
283 if (empty($extensions)) return;
284
285 foreach ($extensions as $extension) {
286 if (is_file($extension)) require_once $extension;
287 }
288 }
289 endif;
290
291 if (!function_exists('wpo_restricted_cache_page_type')) {
292 function wpo_restricted_cache_page_type($restricted) {
293 global $post;
294
295 // Don't cache search or password protected.
296 if ((function_exists('is_search') && is_search()) || (function_exists('is_404') && is_404()) || !empty($post->post_password)) {
297 $restricted = __('Page type is not cacheable (search, 404 or password-protected)', 'wp-optimize');
298 }
299
300 // Don't cache the front page if option is set.
301 if (in_array('/', wpo_get_url_exceptions()) && function_exists('is_front_page') && is_front_page()) {
302
303 $restricted = __('In the settings, caching is disabled for the front page', 'wp-optimize');
304 }
305
306 // Don't cache htacesss. Remember to properly escape any output to prevent injection.
307 if (strpos($_SERVER['REQUEST_URI'], '.htaccess') !== false) {
308 $restricted = 'The file path is unsuitable for caching ('.$_SERVER['REQUEST_URI'].')';
309 }
310
311 // Don't cache feeds.
312 if (function_exists('is_feed') && is_feed() && !wpo_feeds_caching_enabled()) {
313 $restricted = __('We don\'t cache RSS feeds', 'wp-optimize');
314 }
315
316 return $restricted;
317 }
318 }
319
320 /**
321 * Returns true if we need cache content for loggedin users.
322 *
323 * @return bool
324 */
325 if (!function_exists('wpo_cache_loggedin_users')) :
326 function wpo_cache_loggedin_users() {
327 return !empty($GLOBALS['wpo_cache_config']['enable_user_caching']) || !empty($GLOBALS['wpo_cache_config']['enable_user_specific_cache']) || (function_exists('wpo_we_cache_per_role') && wpo_we_cache_per_role());
328 }
329 endif;
330
331 /**
332 * Get filename for store cache, depending on gzip, mobile and cookie settings.
333 *
334 * @param string $ext
335 * @return string
336 */
337 if (!function_exists('wpo_cache_filename')) :
338 function wpo_cache_filename($ext = '.html') {
339
340 $wpo_cache_filename_debug = array();
341
342 $filename = 'index';
343
344 if (wpo_cache_mobile_caching_enabled() && wpo_is_mobile()) {
345 $filename = 'mobile.' . $filename;
346 }
347
348 if (wpo_webp_images_enabled() && !wpo_is_using_webp_images_redirection() && wpo_is_using_alter_html()) {
349 $filename = $filename . '.webp';
350 }
351
352 $cookies = wpo_cache_cookies();
353
354 $cache_key = '';
355
356 /**
357 * Add cookie values to filename if need.
358 * This section was inspired by things learned from WP-Rocket.
359 */
360 if (!empty($cookies)) {
361 foreach ($cookies as $key => $cookie_name) {
362 if (is_array($cookie_name) && isset($_COOKIE[$key])) {
363 foreach ($cookie_name as $cookie_key) {
364 if (isset($_COOKIE[$key][$cookie_key]) && '' !== $_COOKIE[$key][$cookie_key]) {
365 $_cache_key = $cookie_key.'='.$_COOKIE[$key][$cookie_key];
366 $_cache_key = preg_replace('/[^a-z0-9_\-\=]/i', '-', $_cache_key);
367 $cache_key .= '-' . $_cache_key;
368 $wpo_cache_filename_debug[] = 'Cookie: name: ' . $key . '[' . $cookie_key . '], value: *** , cache_key:' . $_cache_key;
369 }
370 }
371 continue;
372 }
373
374 if (isset($_COOKIE[$cookie_name]) && '' !== $_COOKIE[$cookie_name]) {
375 $_cache_key = $cookie_name.'='.$_COOKIE[$cookie_name];
376 $_cache_key = preg_replace('/[^a-z0-9_\-\=]/i', '-', $_cache_key);
377 $cache_key .= '-' . $_cache_key;
378 $wpo_cache_filename_debug[] = 'Cookie: name: ' . $cookie_name . ', value: *** , cache_key:' . $_cache_key;
379 }
380 }
381 }
382
383 $query_variables = wpo_cache_query_variables();
384
385 /**
386 * Add GET variables to cache file name if need.
387 */
388 if (!empty($query_variables)) {
389 foreach ($query_variables as $variable) {
390 if (isset($_GET[$variable]) && !empty($_GET[$variable])) {
391 $_cache_key = $variable.'='.$_GET[$variable];
392 $_cache_key = preg_replace('/[^a-z0-9_\-\=]/i', '-', $_cache_key);
393 $cache_key .= '-' . $_cache_key;
394 $wpo_cache_filename_debug[] = 'GET parameter: name: ' . $variable . ', value:' . htmlentities($_GET[$variable]) . ', cache_key:' . $_cache_key;
395 }
396 }
397 }
398
399 // add hash of queried cookies and variables to cache file name.
400 if ('' !== $cache_key) {
401 $hash = md5($cache_key);
402 $filename .= '-'.$hash;
403 $wpo_cache_filename_debug[] = 'Hash: ' . $hash;
404 }
405
406 $filename = apply_filters('wpo_cache_filename', $filename);
407
408 $wpo_cache_filename_debug[] = 'Extension: ' . $ext;
409 $wpo_cache_filename_debug[] = 'Filename: ' . $filename.$ext;
410
411 $GLOBALS['wpo_cache_filename_debug'] = $wpo_cache_filename_debug;
412
413 return $filename . $ext;
414 }
415 endif;
416
417 /**
418 * Returns site url from site_url() function or if it is not available from cache configuration.
419 */
420 if (!function_exists('wpo_site_url')) :
421 function wpo_site_url() {
422 if (is_callable('site_url')) return site_url('/');
423
424 $site_url = empty($GLOBALS['wpo_cache_config']['site_url']) ? '' : $GLOBALS['wpo_cache_config']['site_url'];
425 return $site_url;
426 }
427 endif;
428
429 /**
430 * Get cookie names which impact on cache file name.
431 *
432 * @return array
433 */
434 if (!function_exists('wpo_cache_cookies')) :
435 function wpo_cache_cookies() {
436 $cookies = empty($GLOBALS['wpo_cache_config']['wpo_cache_cookies']) ? array() : $GLOBALS['wpo_cache_config']['wpo_cache_cookies'];
437 return $cookies;
438 }
439 endif;
440
441 /**
442 * Get GET variable names which impact on cache file name.
443 *
444 * @return array
445 */
446 if (!function_exists('wpo_cache_query_variables')) :
447 function wpo_cache_query_variables() {
448 if (defined('WPO_CACHE_URL_PARAMS') && WPO_CACHE_URL_PARAMS) {
449 $variables = array_keys($_GET);
450 } else {
451 $variables = empty($GLOBALS['wpo_cache_config']['wpo_cache_query_variables']) ? array() : $GLOBALS['wpo_cache_config']['wpo_cache_query_variables'];
452 }
453
454 if (!empty($variables)) {
455 sort($variables);
456 }
457
458 return wpo_cache_maybe_ignore_query_variables($variables);
459 }
460 endif;
461
462 /**
463 * Get list of all received HTTP headers.
464 *
465 * @return array
466 */
467 if (!function_exists('wpo_get_http_headers')) :
468 function wpo_get_http_headers() {
469
470 static $headers;
471
472 if (!empty($headers)) return $headers;
473
474 $headers = array();
475
476 // if is apache server then use get allheaders() function.
477 if (function_exists('getallheaders')) {
478 $headers = getallheaders();
479 } else {
480 // https://www.php.net/manual/en/function.getallheaders.php
481 foreach ($_SERVER as $key => $value) {
482
483 $key = strtolower($key);
484
485 if ('HTTP_' == substr($key, 0, 5)) {
486 $headers[str_replace(' ', '-', ucwords(str_replace('_', ' ', substr($key, 5))))] = $value;
487 } elseif ('content_type' == $key) {
488 $headers["Content-Type"] = $value;
489 } elseif ('content_length' == $key) {
490 $headers["Content-Length"] = $value;
491 }
492 }
493 }
494
495 return $headers;
496 }
497 endif;
498
499 /**
500 * Check if requested Accept-Encoding headers has gzip value.
501 *
502 * @return bool
503 */
504 if (!function_exists('wpo_cache_gzip_accepted')) :
505 function wpo_cache_gzip_accepted() {
506 $headers = wpo_get_http_headers();
507
508 if (isset($headers['Accept-Encoding']) && preg_match('/gzip/i', $headers['Accept-Encoding'])) return true;
509
510 return false;
511 }
512 endif;
513
514 /**
515 * Check if we can output gzip content in current answer, i.e. check Accept-Encoding headers has gzip value
516 * and function ob_gzhandler is available.
517 *
518 * @return bool
519 */
520 if (!function_exists('wpo_cache_can_output_gzip_content')) :
521 function wpo_cache_can_output_gzip_content() {
522 return wpo_cache_gzip_accepted() && function_exists('ob_gzhandler');
523 }
524 endif;
525
526 /**
527 * Check if header with certain name exists in already prepared headers and has value comparable with $header_value.
528 *
529 * @param string $header_name header name
530 * @param string $header_value header value as regexp.
531 *
532 * @return bool
533 */
534 if (!function_exists('wpo_cache_is_in_response_headers_list')) :
535 function wpo_cache_is_in_response_headers_list($header_name, $header_value) {
536 $headers_list = headers_list();
537
538 if (!empty($headers_list)) {
539 $header_name = strtolower($header_name);
540
541 foreach ($headers_list as $value) {
542 $value = explode(':', $value);
543
544 if (strtolower($value[0]) == $header_name) {
545 if (preg_match('/'.$header_value.'/', $value[1])) {
546 return true;
547 } else {
548 return false;
549 }
550 }
551 }
552 }
553
554 return false;
555 }
556 endif;
557
558 /**
559 * Check if mobile cache is enabled and current request is from moblile device.
560 *
561 * @return bool
562 */
563 if (!function_exists('wpo_cache_mobile_caching_enabled')) :
564 function wpo_cache_mobile_caching_enabled() {
565 if (!empty($GLOBALS['wpo_cache_config']['enable_mobile_caching'])) return true;
566 return false;
567 }
568 endif;
569
570 /**
571 * Check if webp images enabled
572 *
573 * @return bool
574 */
575 if (!function_exists('wpo_webp_images_enabled')) :
576 function wpo_webp_images_enabled() {
577 if (!empty($GLOBALS['wpo_cache_config']['use_webp_images'])) return true;
578 return false;
579 }
580 endif;
581
582 /**
583 * Check whether webp images using alter html method or not
584 *
585 * @return bool
586 */
587 if (!function_exists('wpo_is_using_alter_html')) :
588 function wpo_is_using_alter_html() {
589 return (isset($_SERVER['HTTP_ACCEPT']) && false !== strpos($_SERVER['HTTP_ACCEPT'], 'image/webp'));
590 }
591 endif;
592
593 /**
594 * Check whether webp images are served using redirect
595 *
596 * @return bool
597 */
598 if (!function_exists('wpo_is_using_webp_images_redirection')) :
599 function wpo_is_using_webp_images_redirection() {
600 if (empty($GLOBALS['wpo_cache_config']['uploads'])) return false;
601
602 $uploads_dir = $GLOBALS['wpo_cache_config']['uploads'];
603 $htaccess_file = $uploads_dir . '/.htaccess';
604 if (!file_exists($htaccess_file)) return false;
605 $htaccess_content = file_get_contents($htaccess_file);
606 $comment_sections = array('Register webp mime type', 'WP-Optimize WebP Rules');
607
608 if (function_exists('str_contains')) {
609 return str_contains($htaccess_content, $comment_sections[0]) && str_contains($htaccess_content, $comment_sections[1]);
610 } else {
611 return strpos($htaccess_content, $comment_sections[0]) && strpos($htaccess_content, $comment_sections[1]);
612 }
613 }
614 endif;
615
616 /**
617 * Serves the cache and exits
618 */
619 if (!function_exists('wpo_serve_cache')) :
620 function wpo_serve_cache() {
621 $file_name = wpo_cache_filename();
622
623 $file_name_rss_xml = wpo_cache_filename('.rss-xml');
624 $send_as_feed = false;
625
626 $path_dir = WPO_CACHE_FILES_DIR . '/' . wpo_get_url_path() . '/';
627 $path = $path_dir . $file_name;
628
629 if (wpo_feeds_caching_enabled()) {
630 // check for .xml cache file if .html cache file doesn't exist
631 if (!file_exists($path_dir . $file_name) && file_exists($path_dir . $file_name_rss_xml)) {
632 $path = $path_dir . $file_name_rss_xml;
633 $send_as_feed = true;
634 }
635 }
636
637 $use_gzip = false;
638
639 // if we can use gzip and gzipped file exist in cache we use it.
640 // if headers already sent we don't use gzipped file content.
641 if (!headers_sent() && wpo_cache_gzip_accepted() && file_exists($path . '.gz')) {
642 $path .= '.gz';
643 $use_gzip = true;
644 }
645
646 $modified_time = file_exists($path) ? (int) filemtime($path) : time();
647
648 // Cache has expired, purge and exit.
649 if (!empty($GLOBALS['wpo_cache_config']['page_cache_length'])) {
650 if (time() > ($GLOBALS['wpo_cache_config']['page_cache_length'] + $modified_time)) {
651 wpo_delete_files($path);
652 return;
653 }
654 }
655
656 if ($use_gzip) {
657 // Disable zlib output compression to avoid double content compression
658 ini_set('zlib.output_compression', 'Off');
659 }
660
661 $gzip_header_already_sent = wpo_cache_is_in_response_headers_list('Content-Encoding', 'gzip');
662
663 header('Cache-Control: no-cache'); // Check back later
664
665 if (!empty($modified_time) && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modified_time) {
666 if ($use_gzip && !$gzip_header_already_sent) {
667 header('Content-Encoding: gzip');
668 }
669
670 if ($send_as_feed) {
671 header('Content-type: application/rss+xml');
672 }
673
674 header('WPO-Cache-Status: cached');
675 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT');
676 header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304);
677 exit;
678 }
679
680 if (file_exists($path) && is_readable($path)) {
681
682 if (wpo_is_canonical_redirection_needed()) return;
683
684 if ($use_gzip && !$gzip_header_already_sent) {
685 header('Content-Encoding: gzip');
686 }
687
688 // send correct headers for xml and txt files
689 $filename = basename(dirname($path));
690
691 if (preg_match('/\.xml$/i', $filename)) {
692 header('Content-type: text/xml');
693 }
694
695 if (preg_match('/\.txt$/i', $filename)) {
696 header('Content-type: text/plain');
697 }
698
699 if ($send_as_feed) {
700 header('Content-type: application/rss+xml');
701 }
702
703 header('WPO-Cache-Status: cached');
704 if (!empty($modified_time)) {
705 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT');
706 }
707
708 readfile($path);
709
710 exit;
711 }
712 }
713 endif;
714
715 /**
716 * Checks and does redirection, if needed
717 *
718 * @return bool
719 */
720 if (!function_exists('wpo_is_canonical_redirection_needed')) :
721 function wpo_is_canonical_redirection_needed() {
722 $permalink_structure = isset($GLOBALS['wpo_cache_config']['permalink_structure']) ? $GLOBALS['wpo_cache_config']['permalink_structure'] : '';
723 $site_url = wpo_site_url();
724
725 $schema = isset($_SERVER['HTTPS']) && 'on' === $_SERVER['HTTPS'] ? "https" : "http";
726 $url_part = "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
727 $requested_url = $schema . $url_part;
728 $url_parts = parse_url($requested_url);
729 $extension = pathinfo($url_parts['path'], PATHINFO_EXTENSION);
730
731 if (!empty($permalink_structure) && $requested_url != $site_url && ((isset($url_parts['path']) && '/' !== $url_parts['path']) || isset($url_parts['query']))) {
732 $request_uri = rtrim($_SERVER['REQUEST_URI'], '?');
733 if ('/' == substr($permalink_structure, -1) && empty($extension) && empty($url_parts['query']) && empty($url_parts['fragment'])) {
734 $url = preg_replace('/(.+?)([\/]*)(\[\?\#][^\/]+|$)/', '$1/$3', $request_uri);
735 if (0 !== strcmp($request_uri, $url)) return true;
736 } else {
737 $url = rtrim($request_uri, '/');
738 if (0 !== strcmp($request_uri, $url)) return true;
739 }
740 }
741 return false;
742 }
743 endif;
744
745 /**
746 * Clears the cache
747 */
748 if (!function_exists('wpo_cache_flush')) :
749 function wpo_cache_flush() {
750
751 if (defined('WPO_CACHE_FILES_DIR') && '' != WPO_CACHE_FILES_DIR) wpo_delete_files(WPO_CACHE_FILES_DIR);
752
753 if (function_exists('wp_cache_flush')) {
754 wp_cache_flush();
755 }
756
757 do_action('wpo_cache_flush');
758 }
759 endif;
760
761 /**
762 * Get URL path for caching
763 *
764 * @since 1.0
765 * @return string
766 */
767 if (!function_exists('wpo_get_url_path')) :
768 function wpo_get_url_path($url = '') {
769 $url = '' == $url ? wpo_current_url() : $url;
770 $url_parts = parse_url($url);
771
772 if (isset($url_parts['path']) && false !== stripos($url_parts['path'], '/index.php')) {
773 $url_parts['path'] = preg_replace('/(.*?)index\.php(\/.+)/i', '$1index-php$2', $url_parts['path']);
774 }
775
776 /*
777 * Convert the hexadecimal digits within the percent-encoded triplet to uppercase, to ensure that the path remains
778 * consistent. For instance, "example.com/%e0%a6" will be converted to "example.com/%E0%A6".
779 */
780 if (isset($url_parts['path'])) {
781 $url_parts['path'] = preg_replace_callback('/%[0-9A-F]{2}/i', function($matches) {
782 return strtoupper($matches[0]);
783 }, $url_parts['path']);
784 }
785
786 if (!isset($url_parts['host'])) $url_parts['host'] = '';
787 if (!isset($url_parts['path'])) $url_parts['path'] = '';
788
789 return $url_parts['host'].$url_parts['path'];
790 }
791 endif;
792
793 /**
794 * Get requested url.
795 *
796 * @return string
797 */
798 if (!function_exists('wpo_current_url')) :
799 function wpo_current_url() {
800 // Note: We use `static $url` to save the first value we retrieve, as some plugins change $_SERVER later on in the process (e.g. Weglot).
801 // Otherwise this function would return a different URL at the beginning and end of the cache process.
802 static $url = '';
803 if ('' != $url) return $url;
804 $http_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
805 $url = rtrim('http' . ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS']) ||
806 isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO']) ? 's' : '' )
807 . '://' . $http_host.$_SERVER['REQUEST_URI'], '/');
808 return $url;
809 }
810 endif;
811
812 /**
813 * Return list of conditional tag exceptions.
814 *
815 * @return array
816 */
817 if (!function_exists('wpo_get_conditional_tags_exceptions')) :
818 function wpo_get_conditional_tags_exceptions() {
819 static $exceptions = null;
820
821 if (null !== $exceptions) return $exceptions;
822
823 if (!empty($GLOBALS['wpo_cache_config'])) {
824 if (empty($GLOBALS['wpo_cache_config']['cache_exception_conditional_tags'])) {
825 $exceptions = array();
826
827 } else {
828
829 $exceptions = $GLOBALS['wpo_cache_config']['cache_exception_conditional_tags'];
830
831 }
832
833 } elseif (class_exists('WPO_Page_Cache')) {
834
835 $config = WPO_Page_Cache::instance()->config->get();
836
837 if (is_array($config) && array_key_exists('cache_exception_conditional_tags', $config)) {
838 $exceptions = $config['cache_exception_conditional_tags'];
839 } else {
840 $exceptions = array();
841 }
842
843 $exceptions = is_array($exceptions) ? $exceptions : preg_split('#(\n|\r|\r\n)#', $exceptions);
844 $exceptions = array_filter($exceptions, 'trim');
845
846 } else {
847 $exceptions = array();
848 }
849
850 return $exceptions;
851 }
852 endif;
853
854 /**
855 * Return list of url exceptions.
856 *
857 * @return array
858 */
859 if (!function_exists('wpo_get_url_exceptions')) :
860 function wpo_get_url_exceptions() {
861 static $exceptions = null;
862
863 if (null !== $exceptions) return $exceptions;
864
865 // if called from file-based-page-cache.php when WP loading
866 // and cache settings exists then use it otherwise get settings from database.
867 if (!empty($GLOBALS['wpo_cache_config'])) {
868 if (empty($GLOBALS['wpo_cache_config']['cache_exception_urls'])) {
869 $exceptions = array();
870 } else {
871 $exceptions = is_array($GLOBALS['wpo_cache_config']['cache_exception_urls']) ? $GLOBALS['wpo_cache_config']['cache_exception_urls'] : preg_split('#(\n|\r)#', $GLOBALS['wpo_cache_config']['cache_exception_urls']);
872 }
873 } elseif (class_exists('WPO_Page_Cache')) {
874 $config = WPO_Page_Cache::instance()->config->get();
875
876 if (is_array($config) && array_key_exists('cache_exception_urls', $config)) {
877 $exceptions = $config['cache_exception_urls'];
878 } else {
879 $exceptions = array();
880 }
881
882 $exceptions = is_array($exceptions) ? $exceptions : preg_split('#(\n|\r)#', $exceptions);
883 $exceptions = array_filter($exceptions, 'trim');
884 } else {
885 $exceptions = array();
886 }
887
888 return apply_filters('wpo_get_url_exceptions', $exceptions);
889 }
890 endif;
891
892 /**
893 * Return true of exception url matches current url
894 *
895 * @param string $exception Exceptions to check URL against.
896 * @param bool $regex Whether to check with regex or not.
897 * @return bool true if matched, false otherwise
898 */
899 if (!function_exists('wpo_current_url_exception_match')) :
900 function wpo_current_url_exception_match($exception) {
901
902 return wpo_url_exception_match(wpo_current_url(), $exception);
903 }
904 endif;
905
906 /**
907 * Check if url in conditional tags exceptions list.
908 *
909 * @return string
910 */
911 if (!function_exists('wpo_url_in_conditional_tags_exceptions')) :
912 function wpo_url_in_conditional_tags_exceptions() {
913
914 $exceptions = wpo_get_conditional_tags_exceptions();
915 $restricted = '';
916 $allowed_functions = array('is_single', 'is_page', 'is_front_page', 'is_home', 'is_archive', 'is_tag', 'is_category', 'is_feed', 'is_search', 'is_author', 'is_woocommerce', 'is_shop', 'is_product', 'is_account_page', 'is_product_category', 'is_product_tag', 'is_wc_endpoint_url', 'is_bbpress', 'bbp_is_forum_archive', 'bbp_is_topic_archive', 'bbp_is_topic_tag', 'bbp_is_single_forum', 'bbp_is_single_topic', 'bbp_is_single_view', 'bbp_is_single_user', 'bbp_is_user_home', 'bbp_is_search');
917 //Filter for add more conditional tags to whitelist in the exceptions list.
918 $allowed_functions = apply_filters('wpo_allowed_conditional_tags_exceptions', $allowed_functions);
919 if (!empty($exceptions)) {
920 foreach ($exceptions as $exception) {
921 if (false !== strpos($exception, 'is_')) {
922 $exception_function = $exception;
923 if ('()' == substr($exception, -2)) {
924 $exception_function = substr($exception, 0, -2);
925 }
926
927 if (in_array($exception_function, $allowed_functions) && function_exists($exception_function) && call_user_func($exception_function)) {
928 $restricted = sprintf(__('In the settings, caching is disabled for %s', 'wp-optimize'), $exception_function);
929 }
930 }
931 }
932 }
933 return $restricted;
934 }
935 endif;
936
937
938 /**
939 * Check if url in exceptions list.
940 *
941 * @param string $url
942 *
943 * @return bool
944 */
945 if (!function_exists('wpo_url_in_exceptions')) :
946 function wpo_url_in_exceptions($url) {
947 $exceptions = wpo_get_url_exceptions();
948
949 if (!empty($exceptions)) {
950 foreach ($exceptions as $exception) {
951
952 // don't check / - front page using regexp, we handle it in wpo_restricted_cache_page_type()
953 if ('/' == $exception) continue;
954
955 if (wpo_url_exception_match($url, $exception)) {
956 // Exception match.
957 return true;
958 }
959 }
960 }
961
962 return false;
963 }
964 endif;
965
966 /**
967 * Checks if an URL matches against listed exceptions.
968 *
969 * @param string $url - complete url string i.e. http(s)://domain/path
970 * @param string $exception - complete url or absolute path, can contain (.*) wildcards; Sometimes can be urlencoded
971 *
972 * @return bool
973 */
974 if (!function_exists('wpo_url_exception_match')) :
975 function wpo_url_exception_match($url, $exception) {
976 if (preg_match('#^[\s]*$#', $exception)) {
977 return false;
978 }
979
980 $exception = str_replace('*', '.*', $exception);
981
982 $exception = trim($exception);
983
984 // Used to test websites placed in subdirectories.
985 $sub_dir = '';
986
987 // If exception defined from root i.e. /page1 then remove domain part in url.
988 if (preg_match('/^\//', $exception)) {
989 // get site sub directory.
990 $sub_dir = preg_replace('#^(http|https):\/\/.*\/#Ui', '', wpo_site_url());
991 // add prefix slash and remove slash.
992 $sub_dir = ('' == $sub_dir) ? '' : '/' . rtrim($sub_dir, '/');
993 // get relative path
994 $url = preg_replace('#^(http|https):\/\/.*\/#Ui', '/', $url);
995 }
996
997 $url = urldecode(rtrim($url, '/')) . '/';
998 $exception = rtrim($exception, '/');
999
1000 // if we have no wildcard in the end of exception then add slash.
1001 if (!preg_match('#\(\.\*\)$#', $exception)) $exception .= '/';
1002
1003 $exception = preg_quote($exception);
1004
1005 // fix - unescape some possibly escaped mask characters
1006 $search = array(
1007 '\\.\\*',
1008 '\\-',
1009 );
1010 $replace = array(
1011 '.*',
1012 '-',
1013 );
1014 $exception = urldecode(str_replace($search, $replace, $exception));
1015
1016 return (preg_match('#^'.$exception.'$#i', $url) || preg_match('#^'.$sub_dir.$exception.'$#i', $url));
1017 }
1018 endif;
1019
1020 /**
1021 * Checks if its a mobile device
1022 *
1023 * @see https://developer.wordpress.org/reference/functions/wp_is_mobile/
1024 */
1025 if (!function_exists('wpo_is_mobile')) :
1026 function wpo_is_mobile() {
1027 if (empty($_SERVER['HTTP_USER_AGENT'])) {
1028 $is_mobile = false;
1029 // many mobile devices (all iPhone, iPad, etc.)
1030 } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false
1031 || strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false
1032 || strpos($_SERVER['HTTP_USER_AGENT'], 'Silk/') !== false
1033 || strpos($_SERVER['HTTP_USER_AGENT'], 'Kindle') !== false
1034 || strpos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false
1035 || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== false
1036 || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mobi') !== false
1037 ) {
1038 $is_mobile = true;
1039 } else {
1040 $is_mobile = false;
1041 }
1042
1043 return $is_mobile;
1044 }
1045 endif;
1046
1047 /**
1048 * Check if current browser agent is not disabled in options.
1049 *
1050 * @return bool
1051 */
1052 if (!function_exists('wpo_is_accepted_user_agent')) :
1053 function wpo_is_accepted_user_agent($user_agent) {
1054
1055 $exceptions = is_array($GLOBALS['wpo_cache_config']['cache_exception_browser_agents']) ? $GLOBALS['wpo_cache_config']['cache_exception_browser_agents'] : preg_split('#(\n|\r)#', $GLOBALS['wpo_cache_config']['cache_exception_browser_agents']);
1056
1057 if (!empty($exceptions)) {
1058 foreach ($exceptions as $exception) {
1059 if ('' == trim($exception)) continue;
1060
1061 if (preg_match('#'.$exception.'#i', $user_agent)) return false;
1062 }
1063 }
1064
1065 return true;
1066 }
1067 endif;
1068
1069 if (!function_exists('wpo_delete_files')) :
1070 /**
1071 * Deletes a specified source file or directory.
1072 *
1073 * If $src is a file, only that file will be deleted. If $src is a directory, the behavior depends on the
1074 * $recursive parameter. When $recursive is true, the directory and its contents (including files and subdirectories)
1075 * will be deleted. When $recursive is false, only the files in the top-level directory(eg. $src directory) will be deleted,
1076 * while the $src directory itself and its subdirectories will remain untouched.
1077 *
1078 * @param string $src The path to the source file or directory to delete.
1079 * @param bool $recursive (Optional) When set to true, the directory and its contents (including files and subdirectories)
1080 * will be deleted. If false, only the files in the top-level directory will be deleted while
1081 * its subdirectories will be preserved. Defaults to true.
1082 *
1083 * @return bool Returns true if the specified file or all files within the specified directory (and its subdirectories,
1084 * when $recursive is true) are successfully deleted. Returns false if any file(s) could not be deleted
1085 * due to file permissions or other reasons.
1086 */
1087 function wpo_delete_files($src, $recursive = true) {
1088 // If the source doesn't exist, consider it deleted and return true
1089 if (!file_exists($src)) {
1090 return true;
1091 }
1092
1093 /*
1094 * If the source is a file, delete it and return the result.
1095 * If `unlink()` fails, we also verify if the file still exists before returning the result, as another
1096 * PHP process may have already deleted the file between the execution of `is_file()` and `unlink()` operations.
1097 */
1098 if (is_file($src)) {
1099 if (!@unlink($src) && file_exists($src)) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1100 return false;
1101 }
1102 return true;
1103 }
1104
1105 $success = true;
1106
1107 // If recursive is false, delete only the top-level files and return the result
1108 if (!$recursive) {
1109 $dir_handle = @opendir($src); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1110
1111 /*
1112 * If opendir() is successful, process directory contents. If not, check if the directory exists.
1113 * If it exists, return false (failure). Otherwise, assume it's already deleted and return true (success).
1114 */
1115 if (false !== $dir_handle) {
1116
1117 while (false !== ($file = readdir($dir_handle))) {
1118 if ('.' == $file || '..' == $file) {
1119 continue;
1120 }
1121
1122 $full_path = $src . '/' . $file;
1123
1124 // If it's a file, delete it
1125 if (is_file($full_path)) {
1126 if (!wpo_delete_files($full_path)) {
1127 $success = false;
1128 }
1129 }
1130 }
1131
1132 closedir($dir_handle);
1133 } else {
1134 if (file_exists($src)) {
1135 $success = false;
1136 }
1137 }
1138
1139 return $success;
1140 }
1141
1142 // If recursive is true, delete all files and directories recursively
1143 $dir_handle = @opendir($src); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1144
1145 /*
1146 * If opendir() is successful, process directory contents. If not, check if the directory exists.
1147 * If it exists, return false (failure). Otherwise, assume it's already deleted and return true (success).
1148 */
1149 if (false !== $dir_handle) {
1150
1151 while (false !== ($file = readdir($dir_handle))) {
1152 if ('.' == $file || '..' == $file) {
1153 continue;
1154 }
1155
1156 $full_path = $src . '/' . $file;
1157
1158 if (!wpo_delete_files($full_path)) {
1159 $success = false;
1160 }
1161 }
1162
1163 closedir($dir_handle);
1164 } else {
1165 if (file_exists($src)) {
1166 $success = false;
1167 }
1168 }
1169
1170 /*
1171 * Delete the source directory itself.
1172 * Success of `rmdir` operation is not recorded; we only ultimately care about emptying, not removing
1173 * entirely (empty folders in our context are harmless)
1174 */
1175 if ($success) {
1176 @rmdir($src); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1177 }
1178
1179 // Delete cached information about cache size
1180 WP_Optimize()->get_page_cache()->delete_cache_size_information();
1181
1182 return $success;
1183 }
1184 endif;
1185
1186 if (!function_exists('wpo_is_empty_dir')) :
1187 /**
1188 * Check if selected directory is empty or has only index.php which we added for security reasons.
1189 *
1190 * @param string $dir
1191 *
1192 * @return bool
1193 */
1194 function wpo_is_empty_dir($dir) {
1195 if (!file_exists($dir) || !is_dir($dir)) return false;
1196
1197 $handle = opendir($dir);
1198
1199 if (false === $handle) return false;
1200
1201 $is_empty = true;
1202 $file = readdir($handle);
1203
1204 while (false !== $file) {
1205
1206 if ('.' != $file && '..' != $file && 'index.php' != $file) {
1207 $is_empty = false;
1208 break;
1209 }
1210
1211 $file = readdir($handle);
1212 }
1213
1214 closedir($handle);
1215 return $is_empty;
1216 }
1217 endif;
1218
1219 /**
1220 * Either store for later output, or output now. Only the most-recent call will be effective.
1221 *
1222 * @param String|Null $output - if not null, then the string to use when called by the shutdown action.
1223 */
1224 if (!function_exists('wpo_cache_add_footer_output')) :
1225 function wpo_cache_add_footer_output($output = null) {
1226
1227 static $buffered = null;
1228
1229 if (function_exists('current_filter') && 'shutdown' == current_filter()) {
1230 // Only add the line if it was a page, not something else (e.g. REST response)
1231 if (function_exists('did_action') && did_action('wp_footer')) {
1232 echo "\n<!-- WP Optimize page cache - https://getwpo.com - ".$buffered." -->\n";
1233 } elseif (defined('WPO_CACHE_DEBUG') && WPO_CACHE_DEBUG && (!defined('REST_REQUEST') || !REST_REQUEST)) {
1234 error_log('[CACHE DEBUG] '.wpo_current_url() . ' - ' . $buffered);
1235 }
1236 } else {
1237 if (null == $buffered && function_exists('add_action')) add_action('shutdown', 'wpo_cache_add_footer_output', 11);
1238 $buffered = $output;
1239 }
1240
1241 }
1242 endif;
1243
1244 /**
1245 * Remove variable names that shouldn't influence cache.
1246 *
1247 * @param array $variables List of variable names.
1248 *
1249 * @return array
1250 */
1251 if (!function_exists('wpo_cache_maybe_ignore_query_variables')) :
1252 function wpo_cache_maybe_ignore_query_variables($variables) {
1253
1254 /**
1255 * Filters the current $_GET variables that will be used when caching or excluding from cache.
1256 * Currently:
1257 * - 'wpo_cache_debug' (Shows the reason for not being cached even when WP_DEBUG isn't set)
1258 * - 'doing_wp_cron' (alternative cron)
1259 * - 'aiosp_sitemap_path', 'aiosp_sitemap_page' (All in one SEO sitemap)
1260 * - 'xml_sitemap', 'seopress_sitemap', 'seopress_news', 'seopress_video', 'seopress_cpt', 'seopress_paged' (SEOPress sitemap)
1261 * - 'sitemap', 'sitemap_n' (YOAST SEO sitemap)
1262 */
1263 $exclude_variables = array(
1264 'wpo_cache_debug', // Shows the reason for not being cached even when WP_DEBUG isn't set
1265 'doing_wp_cron', // alternative cron
1266 'aiosp_sitemap_path', // All in one SEO sitemap
1267 'aiosp_sitemap_page',
1268 'xml_sitemap', // SEOPress sitemap
1269 'seopress_sitemap',
1270 'seopress_news',
1271 'seopress_video',
1272 'seopress_cpt',
1273 'seopress_paged',
1274 'sitemap', // YOAST SEO sitemap
1275 'sitemap_n',
1276 );
1277 $exclude_variables = function_exists('apply_filters') ? apply_filters('wpo_cache_ignore_query_variables', $exclude_variables) : $exclude_variables;
1278
1279 if (empty($exclude_variables)) return $variables;
1280
1281 foreach ($exclude_variables as $variable) {
1282 $exclude = array_search($variable, $variables);
1283 if (false !== $exclude) {
1284 array_splice($variables, $exclude, 1);
1285 }
1286 }
1287
1288 return $variables;
1289 }
1290 endif;
1291
1292 /**
1293 * Get cache config
1294 *
1295 * @param string $key - The config item
1296 * @param mixed $default - The default value
1297 *
1298 * @return mixed
1299 */
1300 if (!function_exists('wpo_cache_config_get')) :
1301 function wpo_cache_config_get($key, $default = false) {
1302 $config = $GLOBALS['wpo_cache_config'];
1303
1304 if (!$config) return false;
1305
1306 if (isset($config[$key])) {
1307 return $config[$key];
1308 } else {
1309 return $default;
1310 }
1311 }
1312 endif;
1313
1314 if (!function_exists('wpo_disable_cache_directories_viewing')) :
1315 function wpo_disable_cache_directories_viewing() {
1316 global $is_apache, $is_IIS, $is_iis7;
1317
1318 if (!is_dir(WPO_CACHE_FILES_DIR)) return;
1319
1320 // Create .htaccess file for apache server.
1321 if ($is_apache) {
1322 $htaccess_filename = WPO_CACHE_FILES_DIR . '/.htaccess';
1323
1324 // CS does not like heredoc
1325 // phpcs:disable
1326 $htaccess_content = <<<EOF
1327 # Disable directory browsing
1328 Options -Indexes
1329
1330 # Disable access to any files
1331 <FilesMatch ".*">
1332 Order allow,deny
1333 Deny from all
1334 </FilesMatch>
1335 EOF;
1336 // phpcs:enable
1337
1338 if (!is_file($htaccess_filename)) @file_put_contents($htaccess_filename, $htaccess_content); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress the error when there is file permission issues
1339 }
1340
1341 // Create web.config file for IIS servers.
1342 if ($is_IIS || $is_iis7) {
1343 $webconfig_filename = WPO_CACHE_FILES_DIR . '/web.config';
1344 $webconfig_content = "<configuration>\n<system.webServer>\n<authorization>\n<deny users=\"*\" />\n</authorization>\n</system.webServer>\n</configuration>\n";
1345
1346 if (!is_file($webconfig_filename)) @file_put_contents($webconfig_filename, $webconfig_content); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress the error when there is file permission issues
1347 }
1348
1349 // Create empty index.php file for all servers.
1350 if (!is_file(WPO_CACHE_FILES_DIR . '/index.php')) @file_put_contents(WPO_CACHE_FILES_DIR . '/index.php', '');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress the error when there is file permission issues
1351 }
1352 endif;
1353
1354 /**
1355 * Add the headers indicating why the page is not cached or served from cache
1356 *
1357 * @param string $message - The headers
1358 *
1359 * @return void
1360 */
1361 if (!function_exists('wpo_cache_add_nocache_http_header')) :
1362 function wpo_cache_add_nocache_http_header($message = '') {
1363 static $buffered_message = null;
1364
1365 if (function_exists('current_filter') && 'send_headers' === current_filter() && $buffered_message && !headers_sent()) {
1366 header('WPO-Cache-Status: not cached');
1367 header('WPO-Cache-Message: '. trim(str_replace(array("\r", "\n", ':'), ' ', strip_tags($buffered_message))));
1368 } else {
1369 if (!$buffered_message && function_exists('add_action')) add_action('send_headers', 'wpo_cache_add_nocache_http_header', 11);
1370 $buffered_message = $message;
1371 }
1372 }
1373 endif;
1374
1375 /**
1376 * Check if feeds caching enabled
1377 *
1378 * @return bool
1379 */
1380 if (!function_exists('wpo_feeds_caching_enabled')) :
1381 function wpo_feeds_caching_enabled() {
1382 return apply_filters('wpo_feeds_caching_enabled', true);
1383 }
1384 endif;
1385
1386 if (!function_exists('wpo_debug_backtrace_summary')) {
1387 function wpo_debug_backtrace_summary($ignore_class = null, $skip_frames = 0, $pretty = true) {
1388 static $truncate_paths;
1389
1390 $trace = debug_backtrace(false);
1391 $caller = array();
1392 $check_class = !is_null($ignore_class);
1393 $skip_frames++; // Skip this function.
1394
1395 if (!isset($truncate_paths)) {
1396 $truncate_paths = array(
1397 wpo_normalize_path(WP_CONTENT_DIR),
1398 wpo_normalize_path(ABSPATH),
1399 );
1400 }
1401
1402 foreach ($trace as $call) {
1403 if ($skip_frames > 0) {
1404 $skip_frames--;
1405 } elseif (isset($call['class'])) {
1406 if ($check_class && $ignore_class == $call['class']) {
1407 continue; // Filter out calls.
1408 }
1409
1410 $caller[] = "{$call['class']}{$call['type']}{$call['function']}";
1411 } else {
1412 if (in_array($call['function'], array('do_action', 'apply_filters', 'do_action_ref_array', 'apply_filters_ref_array'), true)) {
1413 $caller[] = "{$call['function']}('{$call['args'][0]}')";
1414 } elseif (in_array($call['function'], array('include', 'include_once', 'require', 'require_once'), true)) {
1415 $filename = isset($call['args'][0]) ? $call['args'][0] : '';
1416 $caller[] = $call['function'] . "('" . str_replace($truncate_paths, '', wpo_normalize_path($filename)) . "')";
1417 } else {
1418 $caller[] = $call['function'];
1419 }
1420 }
1421 }
1422 if ($pretty) {
1423 return implode(', ', array_reverse($caller));
1424 } else {
1425 return $caller;
1426 }
1427 }
1428 }
1429
1430 if (!function_exists('wpo_normalize_path')) {
1431 function wpo_normalize_path($path) {
1432 // Standardise all paths to use '/'.
1433 $path = str_replace('\\', '/', $path);
1434
1435 // Replace multiple slashes down to a singular, allowing for network shares having two slashes.
1436 $path = preg_replace('|(?<=.)/+|', '/', $path);
1437
1438 // Windows paths should uppercase the drive letter.
1439 if (':' === substr($path, 1, 1)) {
1440 $path = ucfirst($path);
1441 }
1442
1443 return $path;
1444 }
1445 }
1446