PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 1.2.8
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v1.2.8
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / dropins / page-cache.php

page-cache.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score 1.2.8, at includes/dropins/page-cache.php

481 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * File based caching drop-in based on Taylor Lovett's Simple Cache
4 * @link https://github.com/tlovett1/simple-cache/blob/master/inc/dropins/file-based-page-cache.php
5 */
6 defined( 'ABSPATH' ) || exit;
7
8 $powered_cache_start_time = microtime( true );
9
10 // Don't cache robots.txt or htacesss
11 if ( strpos( $_SERVER['REQUEST_URI'], 'robots.txt' ) !== false || strpos( $_SERVER['REQUEST_URI'], '.htaccess' ) !== false ) {
12 return;
13 }
14
15 // Don't cache non-GET requests
16 if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || 'GET' !== $_SERVER['REQUEST_METHOD'] ) {
17 return;
18 }
19
20 // Don't cache wp-admin
21 if ( is_admin() ) {
22 return;
23 }
24
25
26
27 $file_extension = $_SERVER['REQUEST_URI'];
28 $file_extension = preg_replace( '#^(.*?)\?.*$#', '$1', $file_extension );
29 $file_extension = trim( preg_replace( '#^.*\.(.*)$#', '$1', $file_extension ) );
30
31 // Don't cache disallowed extensions. Prevents wp-cron.php, xmlrpc.php, etc.
32 if ( ! preg_match( '#index\.php$#i', $_SERVER['REQUEST_URI'] ) && in_array( $file_extension, array( 'php', 'xml', 'xsl' ) ) ) {
33 return;
34 }
35
36 if ( ! $GLOBALS['powered_cache_options']['enable_page_caching'] ) {
37 return;
38 }
39
40 // Don't cache page with these user agents
41 if ( ! empty( $GLOBALS['powered_cache_options']['rejected_user_agents'] ) ) {
42 $rejected_user_agents = preg_split( '#(\r\n|\r|\n)#', $GLOBALS['powered_cache_options']['rejected_user_agents'] );
43 $rejected_user_agents = implode( '|', $rejected_user_agents );
44 if ( ! empty( $rejected_user_agents ) && isset( $_SERVER['HTTP_USER_AGENT'] ) && preg_match( '#(' . $rejected_user_agents . ')#', $_SERVER['HTTP_USER_AGENT'] ) ) {
45 return;
46 }
47 }
48
49
50 // Don't cache SSL
51 if ( powered_cache_is_ssl() && ( ! isset( $GLOBALS['powered_cache_options']['ssl_cache'] ) || false === $GLOBALS['powered_cache_options']['ssl_cache'] ) ) {
52 return;
53 }
54
55
56 // dont cache mobile
57 if ( ! isset( $GLOBALS['powered_cache_options']['cache_mobile'] ) || true !== $GLOBALS['powered_cache_options']['cache_mobile'] ) {
58 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
59
60 $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_browsers ) ), ' ' );
61 $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_prefixes ) ), ' ' );
62 // Don't cache if mobile detection is activated
63 if ( (preg_match( '#^.*(' . $mobile_browsers . ').*#i', $_SERVER['HTTP_USER_AGENT'] ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $_SERVER['HTTP_USER_AGENT'], 0, 4 ) ) ) ) {
64 return '';
65 }
66 }
67
68
69 if ( ! empty( $_COOKIE ) ) {
70 //Don't cache if logged in
71 if ( ! isset( $GLOBALS['powered_cache_options']['loggedin_user_cache'] ) || false === $GLOBALS['powered_cache_options']['loggedin_user_cache'] ) {
72 $wp_cookies = array( 'wordpressuser_', 'wordpresspass_', 'wordpress_sec_', 'wordpress_logged_in_' );
73
74 // check logged-in cookie
75 foreach ( $_COOKIE as $key => $value ) {
76 foreach ( $wp_cookies as $cookie ) {
77 if ( strpos( $key, $cookie ) !== false ) {
78 // Logged in!
79 return;
80 }
81 }
82 }
83 }
84
85
86 if ( ! empty( $_COOKIE['powered_cache_commented_posts'] ) ) {
87 foreach ( $_COOKIE['powered_cache_commented_posts'] as $path ) {
88 if ( rtrim( $path, '/' ) === rtrim( $_SERVER['REQUEST_URI'], '/' ) ) {
89 // User commented on this post
90 return;
91 }
92 }
93 }
94
95 // don't cache specific cookie
96 if ( ! empty( $GLOBALS['powered_cache_options']['rejected_cookies'] ) ) {
97 $rejected_cookies = preg_split( '#(\r\n|\r|\n)#', $GLOBALS['powered_cache_options']['rejected_cookies'] );
98 $rejected_cookies = implode( '|', $rejected_cookies );
99 if ( preg_match( '#(' . $rejected_cookies . ')#', var_export( $_COOKIE, true ) ) ) {
100 return;
101 }
102 }
103 } // End if().
104
105 // Deal with optional cache exceptions
106 if ( ! empty( $GLOBALS['powered_cache_options']['rejected_uri'] ) ) {
107 $exceptions = preg_split( '#(\r\n|\r|\n)#', $GLOBALS['powered_cache_options']['rejected_uri'] );
108
109 foreach ( $exceptions as $exception ) {
110 if ( preg_match( '#^[\s]*$#', $exception ) ) {
111 continue;
112 }
113
114 // full url exception
115 if ( preg_match( '#^https?://#', $exception ) ) {
116 $exception = parse_url( $exception, PHP_URL_PATH );
117 }
118
119 if ( preg_match( '#^(' . $exception . ')$#', $_SERVER['REQUEST_URI'] ) ) {
120 return;
121 }
122 }
123 }
124
125
126 $accepted_query_strings = array();
127 // cache url with allowed query string
128 if ( ! empty( $GLOBALS['powered_cache_options']['accepted_query_strings'] ) ) {
129 $accepted_query_strings = preg_split( '#(\r\n|\r|\n)#', $GLOBALS['powered_cache_options']['accepted_query_strings'] );
130 }
131
132 if ( ! empty( $_GET ) && isset( $accepted_query_strings ) && is_array( $accepted_query_strings ) && ! array_intersect( array_keys( $_GET ), $accepted_query_strings ) ) {
133 return;
134 }
135
136 powered_cache_serve_cache();
137
138 ob_start( 'powered_cache_page_buffer' );
139
140 /**
141 * Cache output before it goes to the browser
142 *
143 * @param string $buffer
144 * @param int $flags
145 * @since 1.0
146 * @return string
147 */
148 function powered_cache_page_buffer( $buffer, $flags ) {
149 global $powered_cache_start_time, $post;
150
151 if ( strlen( $buffer ) < 255 ) {
152 return $buffer;
153 }
154
155 // Don't cache search, 404, or password protected
156 if ( is_404() || is_search() || ! empty( $post->post_password ) ) {
157 return $buffer;
158 }
159
160 // maybe we shouldn't cache template file has this constant
161 if ( defined( 'DONOTCACHEPAGE' ) && true === DONOTCACHEPAGE ) {
162 return $buffer;
163 }
164
165 // plugins might want to use filter
166 if ( true !== apply_filters( 'powered_cache_page_cache_enable', true ) ) {
167 return $buffer;
168 }
169
170 if ( ! defined( 'FS_CHMOD_DIR' ) ) {
171 define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
172 }
173
174 if ( ! defined( 'FS_CHMOD_FILE' ) ) {
175 define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
176 }
177
178 include_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
179 include_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
180
181 $filesystem = new WP_Filesystem_Direct( new StdClass() );
182
183 if ( ! function_exists( 'powered_cache_get_cache_dir' ) ) {
184 return $buffer;
185 }
186
187 // Make sure we can read/write files and that proper folders exist
188 if ( ! $filesystem->exists( untrailingslashit( powered_cache_get_cache_dir() ) ) ) {
189 if ( ! $filesystem->mkdir( untrailingslashit( powered_cache_get_cache_dir() ) ) ) {
190 // Can not cache!
191 return $buffer;
192 }
193 }
194
195 if ( ! $filesystem->exists( powered_cache_get_page_cache_dir() ) ) {
196 if ( ! $filesystem->mkdir( powered_cache_get_page_cache_dir() ) ) {
197 // Can not cache!
198 return $buffer;
199 }
200 }
201
202 $buffer = apply_filters( 'powered_cache_page_caching_buffer', $buffer );
203
204 $url_path = powered_cache_get_url_path();
205
206 $dirs = explode( '/', $url_path );
207
208 $path = untrailingslashit( powered_cache_get_page_cache_dir() );
209
210 foreach ( $dirs as $dir ) {
211 if ( ! empty( $dir ) ) {
212 $path .= '/' . $dir;
213
214 if ( ! $filesystem->exists( $path ) ) {
215 if ( ! $filesystem->mkdir( $path ) ) {
216 // Can not cache!
217 return $buffer;
218 }
219 }
220 }
221 }
222
223
224 $modified_time = time(); // Make sure modified time is consistent
225 $generation_time = number_format( microtime( true ) - $powered_cache_start_time, 3 );
226
227 // Prevent mixed content when there's an http request but the site URL uses https
228 // @see https://github.com/tlovett1/simple-cache/issues/67
229 $home_url = get_home_url();
230 if ( ! is_ssl() && 'https' === strtolower( parse_url( $home_url, PHP_URL_SCHEME ) ) ) {
231 $https_home_url = $home_url;
232 $http_home_url = str_replace( 'https://', 'http://', $https_home_url );
233 $buffer = str_replace( esc_url( $http_home_url ), esc_url( $https_home_url ), $buffer );
234 }
235
236 if ( array_key_exists( 'show_cache_message', $GLOBALS['powered_cache_options'] ) && true === $GLOBALS['powered_cache_options']['show_cache_message'] ) {
237 if ( preg_match( '#</html>#i', $buffer ) ) {
238 $buffer .= PHP_EOL;
239 $buffer .= "<!-- Cache served by PoweredCache -->";
240 $buffer .= PHP_EOL;
241 $buffer .= "<!-- If you like fast websites like this, visit: https://poweredcache.com -->";
242 $buffer .= PHP_EOL;
243 $buffer .= "<!-- Last modified: " . gmdate( 'D, d M Y H:i:s', $modified_time ) . " GMT -->";
244 $buffer .= PHP_EOL;
245 $buffer .= "<!-- Dynamic page generated in $generation_time -->";
246 $buffer .= PHP_EOL;
247 }
248 }
249
250
251 $meta_file_name = 'meta.php';
252 $meta_file = '<?php exit; ?>' . PHP_EOL;
253
254 $meta_params = array(); // holds to metadata for cached file
255
256 $response_headers = powered_cache_get_response_headers();
257
258 foreach ( (array) $response_headers as $key => $value ) {
259 $meta_params['headers'][ $key ] = "$key: $value";
260 }
261
262 $meta_params = apply_filters( 'powered_cache_page_cache_meta_params', $meta_params, $response_headers );
263 $meta_file_contents = $meta_file . serialize( $meta_params );
264
265 $meta_file_contents = apply_filters( 'powered_cache_page_cache_meta_info', $meta_file_contents );
266
267 $filesystem->put_contents( $path . '/' . $meta_file_name, $meta_file_contents, FS_CHMOD_FILE );
268 $filesystem->touch( $path . '/' . $meta_file_name, $modified_time );
269
270 if ( isset( $meta_params['headers']['Content-Type'] ) ) {
271 $index_name = powered_cache_index_file( $meta_params['headers']['Content-Type'] );
272 } else {
273 $index_name = powered_cache_index_file();
274 }
275
276 if ( $GLOBALS['powered_cache_options']['gzip_compression'] && function_exists( 'gzencode' ) ) {
277 $filesystem->put_contents( $path . '/' . $index_name, gzencode( $buffer, 3 ), FS_CHMOD_FILE );
278 $filesystem->touch( $path . '/' . $index_name, $modified_time );
279 } else {
280 $filesystem->put_contents( $path . '/' . $index_name, $buffer, FS_CHMOD_FILE );
281 $filesystem->touch( $path . '/' . $index_name, $modified_time );
282 }
283
284 do_action( 'powered_cache_page_cached', $buffer );
285
286 header( 'Cache-Control: no-cache' ); // Check back every time to see if re-download is necessary
287
288 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $modified_time ) . ' GMT' );
289
290 if ( function_exists( 'ob_gzhandler' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
291 return ob_gzhandler( $buffer, $flags );
292 } else {
293 return $buffer;
294 }
295 }
296
297
298 /**
299 * Optionally serve cache and exit
300 *
301 * @since 1.0
302 */
303 function powered_cache_serve_cache() {
304 global $powered_cache_slash_check;
305
306 $path = rtrim( $GLOBALS['powered_cache_options']['cache_location'], '/' ) . '/powered-cache/' . rtrim( powered_cache_get_url_path(), '/' ) . '/';
307
308 $meta_file = $path.'/meta.php';
309
310
311 if ( @file_exists( $meta_file ) ) {
312 $meta_contents = trim( file_get_contents( $meta_file ) );
313 $meta_contents = str_replace( '<?php exit; ?>', '', $meta_contents );
314 $meta_params = unserialize( trim( $meta_contents ) );
315 $header_params = $meta_params['headers'];
316 }
317
318 $content_type = @$header_params['Content-Type'];
319
320 $file_name = powered_cache_index_file( $content_type );
321 $file_path = $path.$file_name;
322
323 $modified_time = (int) @filemtime( $file_path );
324
325 header( 'Cache-Control: no-cache' ); // Check back in an hour
326
327 if ( ! empty( $modified_time ) && ! empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) && strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) === $modified_time ) {
328 header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
329 exit;
330 }
331
332 // trailingslash check
333 if ( isset( $powered_cache_slash_check ) && $powered_cache_slash_check ) {
334 $current_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
335 if ( ! empty( $current_path ) && '/' !== substr( $current_path, - 1 ) ) {
336 header( 'X-Powered-Cache: Passing to WordPress' );
337
338 return;
339 }
340 }
341
342 if ( @file_exists( $file_path ) && @is_readable( $file_path ) ) {
343
344 if ( ! empty( $header_params ) ) {
345 foreach ( $header_params as $key => $response_header ) {
346 header( $response_header );
347 }
348 }
349
350 header( 'X-Powered-Cache: PHP' );
351
352 if ( function_exists( 'gzencode' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
353 header( 'Content-Encoding: gzip' );
354 }
355
356 @readfile( $file_path );
357
358 exit;
359 }
360 }
361
362 /**
363 * Get URL path for caching
364 *
365 * @since 1.0
366 * @return string
367 */
368 function powered_cache_get_url_path() {
369
370 $host = ( isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '' );
371 $request_uri = preg_replace( '/(\/+)/', '/', $_SERVER['REQUEST_URI'] );
372 $request_uri = str_replace( '..', '', preg_replace( '/[ <>\'\"\r\n\t\(\)]/', '', $request_uri ) );
373
374 return rtrim( $host, '/' ) . $request_uri;
375 }
376
377 function powered_cache_get_user_cookie() {
378 if ( empty( $_COOKIE ) ) {
379 return false;
380 }
381
382 foreach ( $_COOKIE as $c_key => $val ) {
383 if ( false !== strpos( $c_key, 'wordpress_logged_in_' ) ) {
384 return $val;
385 }
386 }
387
388 return false;
389 }
390
391
392 /**
393 * Determines cache file names
394 *
395 * @param string $content_type
396 *
397 * @since 1.0
398 * @since 1.2 `$content_type`
399 * @return string
400 */
401 function powered_cache_index_file( $content_type = 'text/html' ) {
402 $file_name = 'index';
403
404 if ( powered_cache_is_ssl() ) {
405 $file_name .= '-https';
406 }
407
408 // separate file for mobile cache
409 if ( isset( $GLOBALS['powered_cache_options']['cache_mobile'], $GLOBALS['powered_cache_options']['cache_mobile_separate_file'] )
410 && true === $GLOBALS['powered_cache_options']['cache_mobile']
411 && true === $GLOBALS['powered_cache_options']['cache_mobile_separate_file']
412 ) {
413
414 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
415
416 $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_browsers ) ), ' ' );
417 $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_prefixes ) ), ' ' );
418
419 // Don't cache if mobile detection is activated
420 if ( (preg_match( '#^.*(' . $mobile_browsers . ').*#i', $_SERVER['HTTP_USER_AGENT'] ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $_SERVER['HTTP_USER_AGENT'], 0, 4 ) ) ) ) {
421 $file_name .= '-mobile';
422 }
423 }
424
425 if ( isset( $GLOBALS['powered_cache_options']['loggedin_user_cache'] )
426 && true === $GLOBALS['powered_cache_options']['loggedin_user_cache']
427 ) {
428 $usr_cookie = powered_cache_get_user_cookie();
429 if ( false !== $usr_cookie ) {
430 $cookie_info = explode( '|', $usr_cookie );
431
432 // user specific cache dir
433 $file_name .= '_' . $cookie_info[0] . '-' . $cookie_info[1];
434 }
435 }
436
437 if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
438 $file_name .= '_' . sha1( $_SERVER['QUERY_STRING'] );
439 }
440
441 /**
442 * Content-Type is not always text/html (like feed, wp-json etc..)
443 * We should respect proper format!
444 * Alternatively, we can add multiple level lookup for apache/nginx config
445 * but that makes things much more complicated.
446 */
447 if ( false === strpos( $content_type, 'text/html' ) ) {
448 $file_name .= '-' . substr( sha1( $content_type ), 0, 6 );
449 }
450
451 $file_name .= '.html';
452
453 if ( function_exists( 'gzencode' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
454 $file_name .= '_gzip';
455 }
456
457 return $file_name;
458 }
459
460
461 /**
462 * is_ssl moved load.php in WP 4.6 but we support WP 4.1+
463 *
464 * @return bool
465 */
466 function powered_cache_is_ssl() {
467 if ( isset( $_SERVER['HTTPS'] ) ) {
468 if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) {
469 return true;
470 }
471
472 if ( '1' == $_SERVER['HTTPS'] ) {
473 return true;
474 }
475 } elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
476 return true;
477 }
478
479 return false;
480 }
481