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

1,469 lines 45.8 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 * Verify if the current request is related to the Activity Stream
618 *
619 * @return bool
620 */
621 if (!function_exists('wpo_is_activity_stream_requested')) :
622 function wpo_is_activity_stream_requested() {
623 return (isset($_SERVER['HTTP_ACCEPT']) && false !== strpos($_SERVER['HTTP_ACCEPT'], 'application/activity+json'));
624 }
625 endif;
626
627 /**
628 * Verify if the current request is robots.txt
629 */
630 if (!function_exists('wpo_is_robots_txt_requested')) :
631 function wpo_is_robots_txt_requested() {
632 return (isset($_SERVER['REQUEST_URI']) && 'robots.txt' === basename($_SERVER['REQUEST_URI']));
633 }
634 endif;
635
636 /**
637 * Serves the cache and exits
638 */
639 if (!function_exists('wpo_serve_cache')) :
640 function wpo_serve_cache() {
641 $file_name = wpo_cache_filename();
642
643 $file_name_rss_xml = wpo_cache_filename('.rss-xml');
644 $send_as_feed = false;
645
646 $path_dir = WPO_CACHE_FILES_DIR . '/' . wpo_get_url_path() . '/';
647 $path = $path_dir . $file_name;
648
649 if (wpo_feeds_caching_enabled()) {
650 // check for .xml cache file if .html cache file doesn't exist
651 if (!file_exists($path_dir . $file_name) && file_exists($path_dir . $file_name_rss_xml)) {
652 $path = $path_dir . $file_name_rss_xml;
653 $send_as_feed = true;
654 }
655 }
656
657 $use_gzip = false;
658
659 // if we can use gzip and gzipped file exist in cache we use it.
660 // if headers already sent we don't use gzipped file content.
661 if (!headers_sent() && wpo_cache_gzip_accepted() && file_exists($path . '.gz')) {
662 $path .= '.gz';
663 $use_gzip = true;
664 }
665
666 $modified_time = file_exists($path) ? (int) filemtime($path) : time();
667
668 // Cache has expired, purge and exit.
669 if (!empty($GLOBALS['wpo_cache_config']['page_cache_length'])) {
670 if (time() > ($GLOBALS['wpo_cache_config']['page_cache_length'] + $modified_time)) {
671 wpo_delete_files($path);
672 return;
673 }
674 }
675
676 if ($use_gzip) {
677 // Disable zlib output compression to avoid double content compression
678 ini_set('zlib.output_compression', 'Off');
679 }
680
681 $gzip_header_already_sent = wpo_cache_is_in_response_headers_list('Content-Encoding', 'gzip');
682
683 header('Cache-Control: no-cache'); // Check back later
684
685 if (!empty($modified_time) && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $modified_time) {
686 if ($use_gzip && !$gzip_header_already_sent) {
687 header('Content-Encoding: gzip');
688 }
689
690 if ($send_as_feed) {
691 header('Content-type: application/rss+xml');
692 }
693
694 header('WPO-Cache-Status: cached');
695 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT');
696 header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304);
697 exit;
698 }
699
700 if (file_exists($path) && is_readable($path)) {
701
702 if (wpo_is_canonical_redirection_needed()) return;
703
704 if ($use_gzip && !$gzip_header_already_sent) {
705 header('Content-Encoding: gzip');
706 }
707
708 // send correct headers for xml and txt files
709 $filename = basename(dirname($path));
710
711 if (preg_match('/\.xml$/i', $filename)) {
712 header('Content-type: text/xml');
713 }
714
715 if (preg_match('/\.txt$/i', $filename)) {
716 header('Content-type: text/plain');
717 }
718
719 if ($send_as_feed) {
720 header('Content-type: application/rss+xml');
721 }
722
723 header('WPO-Cache-Status: cached');
724 if (!empty($modified_time)) {
725 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $modified_time) . ' GMT');
726 }
727
728 readfile($path);
729
730 exit;
731 }
732 }
733 endif;
734
735 /**
736 * Checks and does redirection, if needed
737 *
738 * @return bool
739 */
740 if (!function_exists('wpo_is_canonical_redirection_needed')) :
741 function wpo_is_canonical_redirection_needed() {
742 $permalink_structure = isset($GLOBALS['wpo_cache_config']['permalink_structure']) ? $GLOBALS['wpo_cache_config']['permalink_structure'] : '';
743 $site_url = wpo_site_url();
744
745 // Exit if server variables are not available.
746 if (!isset($_SERVER['HTTP_HOST'])) return false;
747
748 $schema = isset($_SERVER['HTTPS']) && 'on' === $_SERVER['HTTPS'] ? "https" : "http";
749 $url_part = "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
750 $requested_url = $schema . $url_part;
751 $url_parts = parse_url($requested_url);
752 $extension = pathinfo($url_parts['path'], PATHINFO_EXTENSION);
753
754 if (!empty($permalink_structure) && $requested_url != $site_url && ((isset($url_parts['path']) && '/' !== $url_parts['path']) || isset($url_parts['query']))) {
755 $request_uri = rtrim($_SERVER['REQUEST_URI'], '?');
756 if ('/' == substr($permalink_structure, -1) && empty($extension) && empty($url_parts['query']) && empty($url_parts['fragment'])) {
757 $url = preg_replace('/(.+?)([\/]*)(\[\?\#][^\/]+|$)/', '$1/$3', $request_uri);
758 if (0 !== strcmp($request_uri, $url)) return true;
759 } else {
760 $url = rtrim($request_uri, '/');
761 if (0 !== strcmp($request_uri, $url)) return true;
762 }
763 }
764 return false;
765 }
766 endif;
767
768 /**
769 * Clears the cache
770 */
771 if (!function_exists('wpo_cache_flush')) :
772 function wpo_cache_flush() {
773
774 if (defined('WPO_CACHE_FILES_DIR') && '' != WPO_CACHE_FILES_DIR) wpo_delete_files(WPO_CACHE_FILES_DIR);
775
776 if (function_exists('wp_cache_flush')) {
777 wp_cache_flush();
778 }
779
780 do_action('wpo_cache_flush');
781 }
782 endif;
783
784 /**
785 * Get URL path for caching
786 *
787 * @since 1.0
788 * @return string
789 */
790 if (!function_exists('wpo_get_url_path')) :
791 function wpo_get_url_path($url = '') {
792 $url = '' == $url ? wpo_current_url() : $url;
793 $url_parts = parse_url($url);
794
795 if (isset($url_parts['path']) && false !== stripos($url_parts['path'], '/index.php')) {
796 $url_parts['path'] = preg_replace('/(.*?)index\.php(\/.+)/i', '$1index-php$2', $url_parts['path']);
797 }
798
799 /*
800 * Convert the hexadecimal digits within the percent-encoded triplet to uppercase, to ensure that the path remains
801 * consistent. For instance, "example.com/%e0%a6" will be converted to "example.com/%E0%A6".
802 */
803 if (isset($url_parts['path'])) {
804 $url_parts['path'] = preg_replace_callback('/%[0-9A-F]{2}/i', function($matches) {
805 return strtoupper($matches[0]);
806 }, $url_parts['path']);
807 }
808
809 if (!isset($url_parts['host'])) $url_parts['host'] = '';
810 if (!isset($url_parts['path'])) $url_parts['path'] = '';
811
812 return $url_parts['host'].$url_parts['path'];
813 }
814 endif;
815
816 /**
817 * Get requested url.
818 *
819 * @return string
820 */
821 if (!function_exists('wpo_current_url')) :
822 function wpo_current_url() {
823 // 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).
824 // Otherwise this function would return a different URL at the beginning and end of the cache process.
825 static $url = '';
826 if ('' != $url) return $url;
827 $http_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
828 $url = rtrim('http' . ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS']) ||
829 isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO']) ? 's' : '' )
830 . '://' . $http_host.$_SERVER['REQUEST_URI'], '/');
831 return $url;
832 }
833 endif;
834
835 /**
836 * Return list of conditional tag exceptions.
837 *
838 * @return array
839 */
840 if (!function_exists('wpo_get_conditional_tags_exceptions')) :
841 function wpo_get_conditional_tags_exceptions() {
842 static $exceptions = null;
843
844 if (null !== $exceptions) return $exceptions;
845
846 if (!empty($GLOBALS['wpo_cache_config'])) {
847 if (empty($GLOBALS['wpo_cache_config']['cache_exception_conditional_tags'])) {
848 $exceptions = array();
849
850 } else {
851
852 $exceptions = $GLOBALS['wpo_cache_config']['cache_exception_conditional_tags'];
853
854 }
855
856 } elseif (class_exists('WPO_Page_Cache')) {
857
858 $config = WPO_Page_Cache::instance()->config->get();
859
860 if (is_array($config) && array_key_exists('cache_exception_conditional_tags', $config)) {
861 $exceptions = $config['cache_exception_conditional_tags'];
862 } else {
863 $exceptions = array();
864 }
865
866 $exceptions = is_array($exceptions) ? $exceptions : preg_split('#(\n|\r|\r\n)#', $exceptions);
867 $exceptions = array_filter($exceptions, 'trim');
868
869 } else {
870 $exceptions = array();
871 }
872
873 return $exceptions;
874 }
875 endif;
876
877 /**
878 * Return list of url exceptions.
879 *
880 * @return array
881 */
882 if (!function_exists('wpo_get_url_exceptions')) :
883 function wpo_get_url_exceptions() {
884 static $exceptions = null;
885
886 if (null !== $exceptions) return $exceptions;
887
888 // if called from file-based-page-cache.php when WP loading
889 // and cache settings exists then use it otherwise get settings from database.
890 if (!empty($GLOBALS['wpo_cache_config'])) {
891 if (empty($GLOBALS['wpo_cache_config']['cache_exception_urls'])) {
892 $exceptions = array();
893 } else {
894 $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']);
895 }
896 } elseif (class_exists('WPO_Page_Cache')) {
897 $config = WPO_Page_Cache::instance()->config->get();
898
899 if (is_array($config) && array_key_exists('cache_exception_urls', $config)) {
900 $exceptions = $config['cache_exception_urls'];
901 } else {
902 $exceptions = array();
903 }
904
905 $exceptions = is_array($exceptions) ? $exceptions : preg_split('#(\n|\r)#', $exceptions);
906 $exceptions = array_filter($exceptions, 'trim');
907 } else {
908 $exceptions = array();
909 }
910
911 return apply_filters('wpo_get_url_exceptions', $exceptions);
912 }
913 endif;
914
915 /**
916 * Return true of exception url matches current url
917 *
918 * @param string $exception Exceptions to check URL against.
919 * @param bool $regex Whether to check with regex or not.
920 * @return bool true if matched, false otherwise
921 */
922 if (!function_exists('wpo_current_url_exception_match')) :
923 function wpo_current_url_exception_match($exception) {
924
925 return wpo_url_exception_match(wpo_current_url(), $exception);
926 }
927 endif;
928
929 /**
930 * Check if url in conditional tags exceptions list.
931 *
932 * @return string
933 */
934 if (!function_exists('wpo_url_in_conditional_tags_exceptions')) :
935 function wpo_url_in_conditional_tags_exceptions() {
936
937 $exceptions = wpo_get_conditional_tags_exceptions();
938 $restricted = '';
939 $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');
940 //Filter for add more conditional tags to whitelist in the exceptions list.
941 $allowed_functions = apply_filters('wpo_allowed_conditional_tags_exceptions', $allowed_functions);
942 if (!empty($exceptions)) {
943 foreach ($exceptions as $exception) {
944 if (false !== strpos($exception, 'is_')) {
945 $exception_function = $exception;
946 if ('()' == substr($exception, -2)) {
947 $exception_function = substr($exception, 0, -2);
948 }
949
950 if (in_array($exception_function, $allowed_functions) && function_exists($exception_function) && call_user_func($exception_function)) {
951 $restricted = sprintf(__('In the settings, caching is disabled for %s', 'wp-optimize'), $exception_function);
952 }
953 }
954 }
955 }
956 return $restricted;
957 }
958 endif;
959
960
961 /**
962 * Check if url in exceptions list.
963 *
964 * @param string $url
965 *
966 * @return bool
967 */
968 if (!function_exists('wpo_url_in_exceptions')) :
969 function wpo_url_in_exceptions($url) {
970 $exceptions = wpo_get_url_exceptions();
971
972 if (!empty($exceptions)) {
973 foreach ($exceptions as $exception) {
974
975 // don't check / - front page using regexp, we handle it in wpo_restricted_cache_page_type()
976 if ('/' == $exception) continue;
977
978 if (wpo_url_exception_match($url, $exception)) {
979 // Exception match.
980 return true;
981 }
982 }
983 }
984
985 return false;
986 }
987 endif;
988
989 /**
990 * Checks if an URL matches against listed exceptions.
991 *
992 * @param string $url - complete url string i.e. http(s)://domain/path
993 * @param string $exception - complete url or absolute path, can contain (.*) wildcards; Sometimes can be urlencoded
994 *
995 * @return bool
996 */
997 if (!function_exists('wpo_url_exception_match')) :
998 function wpo_url_exception_match($url, $exception) {
999 if (preg_match('#^[\s]*$#', $exception)) {
1000 return false;
1001 }
1002
1003 $exception = str_replace('*', '.*', $exception);
1004
1005 $exception = trim($exception);
1006
1007 // Used to test websites placed in subdirectories.
1008 $sub_dir = '';
1009
1010 // If exception defined from root i.e. /page1 then remove domain part in url.
1011 if (preg_match('/^\//', $exception)) {
1012 // get site sub directory.
1013 $sub_dir = preg_replace('#^(http|https):\/\/.*\/#Ui', '', wpo_site_url());
1014 // add prefix slash and remove slash.
1015 $sub_dir = ('' == $sub_dir) ? '' : '/' . rtrim($sub_dir, '/');
1016 // get relative path
1017 $url = preg_replace('#^(http|https):\/\/.*\/#Ui', '/', $url);
1018 }
1019
1020 $url = urldecode(rtrim($url, '/')) . '/';
1021 $exception = rtrim($exception, '/');
1022
1023 // if we have no wildcard in the end of exception then add slash.
1024 if (!preg_match('#\(\.\*\)$#', $exception)) $exception .= '/';
1025
1026 $exception = preg_quote($exception);
1027
1028 // fix - unescape some possibly escaped mask characters
1029 $search = array(
1030 '\\.\\*',
1031 '\\-',
1032 );
1033 $replace = array(
1034 '.*',
1035 '-',
1036 );
1037 $exception = urldecode(str_replace($search, $replace, $exception));
1038
1039 return (preg_match('#^'.$exception.'$#i', $url) || preg_match('#^'.$sub_dir.$exception.'$#i', $url));
1040 }
1041 endif;
1042
1043 /**
1044 * Checks if its a mobile device
1045 *
1046 * @see https://developer.wordpress.org/reference/functions/wp_is_mobile/
1047 */
1048 if (!function_exists('wpo_is_mobile')) :
1049 function wpo_is_mobile() {
1050 if (empty($_SERVER['HTTP_USER_AGENT'])) {
1051 $is_mobile = false;
1052 // many mobile devices (all iPhone, iPad, etc.)
1053 } elseif (strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') !== false
1054 || strpos($_SERVER['HTTP_USER_AGENT'], 'Android') !== false
1055 || strpos($_SERVER['HTTP_USER_AGENT'], 'Silk/') !== false
1056 || strpos($_SERVER['HTTP_USER_AGENT'], 'Kindle') !== false
1057 || strpos($_SERVER['HTTP_USER_AGENT'], 'BlackBerry') !== false
1058 || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mini') !== false
1059 || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera Mobi') !== false
1060 ) {
1061 $is_mobile = true;
1062 } else {
1063 $is_mobile = false;
1064 }
1065
1066 return $is_mobile;
1067 }
1068 endif;
1069
1070 /**
1071 * Check if current browser agent is not disabled in options.
1072 *
1073 * @return bool
1074 */
1075 if (!function_exists('wpo_is_accepted_user_agent')) :
1076 function wpo_is_accepted_user_agent($user_agent) {
1077
1078 $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']);
1079
1080 if (!empty($exceptions)) {
1081 foreach ($exceptions as $exception) {
1082 if ('' == trim($exception)) continue;
1083
1084 if (preg_match('#'.$exception.'#i', $user_agent)) return false;
1085 }
1086 }
1087
1088 return true;
1089 }
1090 endif;
1091
1092 if (!function_exists('wpo_delete_files')) :
1093 /**
1094 * Deletes a specified source file or directory.
1095 *
1096 * If $src is a file, only that file will be deleted. If $src is a directory, the behavior depends on the
1097 * $recursive parameter. When $recursive is true, the directory and its contents (including files and subdirectories)
1098 * will be deleted. When $recursive is false, only the files in the top-level directory(eg. $src directory) will be deleted,
1099 * while the $src directory itself and its subdirectories will remain untouched.
1100 *
1101 * @param string $src The path to the source file or directory to delete.
1102 * @param bool $recursive (Optional) When set to true, the directory and its contents (including files and subdirectories)
1103 * will be deleted. If false, only the files in the top-level directory will be deleted while
1104 * its subdirectories will be preserved. Defaults to true.
1105 *
1106 * @return bool Returns true if the specified file or all files within the specified directory (and its subdirectories,
1107 * when $recursive is true) are successfully deleted. Returns false if any file(s) could not be deleted
1108 * due to file permissions or other reasons.
1109 */
1110 function wpo_delete_files($src, $recursive = true) {
1111 // If the source doesn't exist, consider it deleted and return true
1112 if (!file_exists($src)) {
1113 return true;
1114 }
1115
1116 /*
1117 * If the source is a file, delete it and return the result.
1118 * If `unlink()` fails, we also verify if the file still exists before returning the result, as another
1119 * PHP process may have already deleted the file between the execution of `is_file()` and `unlink()` operations.
1120 */
1121 if (is_file($src)) {
1122 if (!@unlink($src) && file_exists($src)) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1123 return false;
1124 }
1125 return true;
1126 }
1127
1128 $success = true;
1129
1130 // If recursive is false, delete only the top-level files and return the result
1131 if (!$recursive) {
1132 $dir_handle = @opendir($src); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1133
1134 /*
1135 * If opendir() is successful, process directory contents. If not, check if the directory exists.
1136 * If it exists, return false (failure). Otherwise, assume it's already deleted and return true (success).
1137 */
1138 if (false !== $dir_handle) {
1139
1140 while (false !== ($file = readdir($dir_handle))) {
1141 if ('.' == $file || '..' == $file) {
1142 continue;
1143 }
1144
1145 $full_path = $src . '/' . $file;
1146
1147 // If it's a file, delete it
1148 if (is_file($full_path)) {
1149 if (!wpo_delete_files($full_path)) {
1150 $success = false;
1151 }
1152 }
1153 }
1154
1155 closedir($dir_handle);
1156 } else {
1157 if (file_exists($src)) {
1158 $success = false;
1159 }
1160 }
1161
1162 return $success;
1163 }
1164
1165 // If recursive is true, delete all files and directories recursively
1166 $dir_handle = @opendir($src); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1167
1168 /*
1169 * If opendir() is successful, process directory contents. If not, check if the directory exists.
1170 * If it exists, return false (failure). Otherwise, assume it's already deleted and return true (success).
1171 */
1172 if (false !== $dir_handle) {
1173
1174 while (false !== ($file = readdir($dir_handle))) {
1175 if ('.' == $file || '..' == $file) {
1176 continue;
1177 }
1178
1179 $full_path = $src . '/' . $file;
1180
1181 if (!wpo_delete_files($full_path)) {
1182 $success = false;
1183 }
1184 }
1185
1186 closedir($dir_handle);
1187 } else {
1188 if (file_exists($src)) {
1189 $success = false;
1190 }
1191 }
1192
1193 /*
1194 * Delete the source directory itself.
1195 * Success of `rmdir` operation is not recorded; we only ultimately care about emptying, not removing
1196 * entirely (empty folders in our context are harmless)
1197 */
1198 if ($success) {
1199 @rmdir($src); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- suppress PHP warning in case of failure
1200 }
1201
1202 // Delete cached information about cache size
1203 WP_Optimize()->get_page_cache()->delete_cache_size_information();
1204
1205 return $success;
1206 }
1207 endif;
1208
1209 if (!function_exists('wpo_is_empty_dir')) :
1210 /**
1211 * Check if selected directory is empty or has only index.php which we added for security reasons.
1212 *
1213 * @param string $dir
1214 *
1215 * @return bool
1216 */
1217 function wpo_is_empty_dir($dir) {
1218 if (!file_exists($dir) || !is_dir($dir)) return false;
1219
1220 $handle = opendir($dir);
1221
1222 if (false === $handle) return false;
1223
1224 $is_empty = true;
1225 $file = readdir($handle);
1226
1227 while (false !== $file) {
1228
1229 if ('.' != $file && '..' != $file && 'index.php' != $file) {
1230 $is_empty = false;
1231 break;
1232 }
1233
1234 $file = readdir($handle);
1235 }
1236
1237 closedir($handle);
1238 return $is_empty;
1239 }
1240 endif;
1241
1242 /**
1243 * Either store for later output, or output now. Only the most-recent call will be effective.
1244 *
1245 * @param String|Null $output - if not null, then the string to use when called by the shutdown action.
1246 */
1247 if (!function_exists('wpo_cache_add_footer_output')) :
1248 function wpo_cache_add_footer_output($output = null) {
1249
1250 static $buffered = null;
1251
1252 if (function_exists('current_filter') && 'shutdown' == current_filter()) {
1253 // Only add the line if it was a page, not something else (e.g. REST response)
1254 if (function_exists('did_action') && did_action('wp_footer')) {
1255 echo "\n<!-- WP Optimize page cache - https://getwpo.com - ".$buffered." -->\n";
1256 } elseif (defined('WPO_CACHE_DEBUG') && WPO_CACHE_DEBUG && (!defined('REST_REQUEST') || !REST_REQUEST)) {
1257 error_log('[CACHE DEBUG] '.wpo_current_url() . ' - ' . $buffered);
1258 }
1259 } else {
1260 if (null == $buffered && function_exists('add_action')) add_action('shutdown', 'wpo_cache_add_footer_output', 11);
1261 $buffered = $output;
1262 }
1263
1264 }
1265 endif;
1266
1267 /**
1268 * Remove variable names that shouldn't influence cache.
1269 *
1270 * @param array $variables List of variable names.
1271 *
1272 * @return array
1273 */
1274 if (!function_exists('wpo_cache_maybe_ignore_query_variables')) :
1275 function wpo_cache_maybe_ignore_query_variables($variables) {
1276
1277 /**
1278 * Filters the current $_GET variables that will be used when caching or excluding from cache.
1279 * Currently:
1280 * - 'wpo_cache_debug' (Shows the reason for not being cached even when WP_DEBUG isn't set)
1281 * - 'doing_wp_cron' (alternative cron)
1282 * - 'aiosp_sitemap_path', 'aiosp_sitemap_page' (All in one SEO sitemap)
1283 * - 'xml_sitemap', 'seopress_sitemap', 'seopress_news', 'seopress_video', 'seopress_cpt', 'seopress_paged' (SEOPress sitemap)
1284 * - 'sitemap', 'sitemap_n' (YOAST SEO sitemap)
1285 */
1286 $exclude_variables = array(
1287 'wpo_cache_debug', // Shows the reason for not being cached even when WP_DEBUG isn't set
1288 'doing_wp_cron', // alternative cron
1289 'aiosp_sitemap_path', // All in one SEO sitemap
1290 'aiosp_sitemap_page',
1291 'xml_sitemap', // SEOPress sitemap
1292 'seopress_sitemap',
1293 'seopress_news',
1294 'seopress_video',
1295 'seopress_cpt',
1296 'seopress_paged',
1297 'sitemap', // YOAST SEO sitemap
1298 'sitemap_n',
1299 );
1300 $exclude_variables = function_exists('apply_filters') ? apply_filters('wpo_cache_ignore_query_variables', $exclude_variables) : $exclude_variables;
1301
1302 if (empty($exclude_variables)) return $variables;
1303
1304 foreach ($exclude_variables as $variable) {
1305 $exclude = array_search($variable, $variables);
1306 if (false !== $exclude) {
1307 array_splice($variables, $exclude, 1);
1308 }
1309 }
1310
1311 return $variables;
1312 }
1313 endif;
1314
1315 /**
1316 * Get cache config
1317 *
1318 * @param string $key - The config item
1319 * @param mixed $default - The default value
1320 *
1321 * @return mixed
1322 */
1323 if (!function_exists('wpo_cache_config_get')) :
1324 function wpo_cache_config_get($key, $default = false) {
1325 $config = $GLOBALS['wpo_cache_config'];
1326
1327 if (!$config) return false;
1328
1329 if (isset($config[$key])) {
1330 return $config[$key];
1331 } else {
1332 return $default;
1333 }
1334 }
1335 endif;
1336
1337 if (!function_exists('wpo_disable_cache_directories_viewing')) :
1338 function wpo_disable_cache_directories_viewing() {
1339 global $is_apache, $is_IIS, $is_iis7;
1340
1341 if (!is_dir(WPO_CACHE_FILES_DIR)) return;
1342
1343 // Create .htaccess file for apache server.
1344 if ($is_apache) {
1345 $htaccess_filename = WPO_CACHE_FILES_DIR . '/.htaccess';
1346
1347 // CS does not like heredoc
1348 // phpcs:disable
1349 $htaccess_content = <<<EOF
1350 # Disable directory browsing
1351 Options -Indexes
1352
1353 # Disable access to any files
1354 <FilesMatch ".*">
1355 Order allow,deny
1356 Deny from all
1357 </FilesMatch>
1358 EOF;
1359 // phpcs:enable
1360
1361 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
1362 }
1363
1364 // Create web.config file for IIS servers.
1365 if ($is_IIS || $is_iis7) {
1366 $webconfig_filename = WPO_CACHE_FILES_DIR . '/web.config';
1367 $webconfig_content = "<configuration>\n<system.webServer>\n<authorization>\n<deny users=\"*\" />\n</authorization>\n</system.webServer>\n</configuration>\n";
1368
1369 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
1370 }
1371
1372 // Create empty index.php file for all servers.
1373 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
1374 }
1375 endif;
1376
1377 /**
1378 * Add the headers indicating why the page is not cached or served from cache
1379 *
1380 * @param string $message - The headers
1381 *
1382 * @return void
1383 */
1384 if (!function_exists('wpo_cache_add_nocache_http_header')) :
1385 function wpo_cache_add_nocache_http_header($message = '') {
1386 static $buffered_message = null;
1387
1388 if (function_exists('current_filter') && 'send_headers' === current_filter() && $buffered_message && !headers_sent()) {
1389 header('WPO-Cache-Status: not cached');
1390 header('WPO-Cache-Message: '. trim(str_replace(array("\r", "\n", ':'), ' ', strip_tags($buffered_message))));
1391 } else {
1392 if (!$buffered_message && function_exists('add_action')) add_action('send_headers', 'wpo_cache_add_nocache_http_header', 11);
1393 $buffered_message = $message;
1394 }
1395 }
1396 endif;
1397
1398 /**
1399 * Check if feeds caching enabled
1400 *
1401 * @return bool
1402 */
1403 if (!function_exists('wpo_feeds_caching_enabled')) :
1404 function wpo_feeds_caching_enabled() {
1405 return apply_filters('wpo_feeds_caching_enabled', true);
1406 }
1407 endif;
1408
1409 if (!function_exists('wpo_debug_backtrace_summary')) {
1410 function wpo_debug_backtrace_summary($ignore_class = null, $skip_frames = 0, $pretty = true) {
1411 static $truncate_paths;
1412
1413 $trace = debug_backtrace(false);
1414 $caller = array();
1415 $check_class = !is_null($ignore_class);
1416 $skip_frames++; // Skip this function.
1417
1418 if (!isset($truncate_paths)) {
1419 $truncate_paths = array(
1420 wpo_normalize_path(WP_CONTENT_DIR),
1421 wpo_normalize_path(ABSPATH),
1422 );
1423 }
1424
1425 foreach ($trace as $call) {
1426 if ($skip_frames > 0) {
1427 $skip_frames--;
1428 } elseif (isset($call['class'])) {
1429 if ($check_class && $ignore_class == $call['class']) {
1430 continue; // Filter out calls.
1431 }
1432
1433 $caller[] = "{$call['class']}{$call['type']}{$call['function']}";
1434 } else {
1435 if (in_array($call['function'], array('do_action', 'apply_filters', 'do_action_ref_array', 'apply_filters_ref_array'), true)) {
1436 $caller[] = "{$call['function']}('{$call['args'][0]}')";
1437 } elseif (in_array($call['function'], array('include', 'include_once', 'require', 'require_once'), true)) {
1438 $filename = isset($call['args'][0]) ? $call['args'][0] : '';
1439 $caller[] = $call['function'] . "('" . str_replace($truncate_paths, '', wpo_normalize_path($filename)) . "')";
1440 } else {
1441 $caller[] = $call['function'];
1442 }
1443 }
1444 }
1445 if ($pretty) {
1446 return implode(', ', array_reverse($caller));
1447 } else {
1448 return $caller;
1449 }
1450 }
1451 }
1452
1453 if (!function_exists('wpo_normalize_path')) {
1454 function wpo_normalize_path($path) {
1455 // Standardise all paths to use '/'.
1456 $path = str_replace('\\', '/', $path);
1457
1458 // Replace multiple slashes down to a singular, allowing for network shares having two slashes.
1459 $path = preg_replace('|(?<=.)/+|', '/', $path);
1460
1461 // Windows paths should uppercase the drive letter.
1462 if (':' === substr($path, 1, 1)) {
1463 $path = ucfirst($path);
1464 }
1465
1466 return $path;
1467 }
1468 }
1469