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

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