PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 1.2.3
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v1.2.3
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.3, at includes/dropins/page-cache.php

479 lines 14.7 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
372 return rtrim( $host, '/' ) . parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
373 }
374
375 function powered_cache_get_user_cookie() {
376 if ( empty( $_COOKIE ) ) {
377 return false;
378 }
379
380 foreach ( $_COOKIE as $c_key => $val ) {
381 if ( false !== strpos( $c_key, 'wordpress_logged_in_' ) ) {
382 return $val;
383 }
384 }
385
386 return false;
387 }
388
389
390 /**
391 * Determines cache file names
392 *
393 * @param string $content_type
394 *
395 * @since 1.0
396 * @since 1.2 `$content_type`
397 * @return string
398 */
399 function powered_cache_index_file( $content_type = 'text/html' ) {
400 $file_name = 'index';
401
402 if ( powered_cache_is_ssl() ) {
403 $file_name .= '-https';
404 }
405
406 // separate file for mobile cache
407 if ( isset( $GLOBALS['powered_cache_options']['cache_mobile'], $GLOBALS['powered_cache_options']['cache_mobile_separate_file'] )
408 && true === $GLOBALS['powered_cache_options']['cache_mobile']
409 && true === $GLOBALS['powered_cache_options']['cache_mobile_separate_file']
410 ) {
411
412 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
413
414 $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_browsers ) ), ' ' );
415 $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_prefixes ) ), ' ' );
416
417 // Don't cache if mobile detection is activated
418 if ( (preg_match( '#^.*(' . $mobile_browsers . ').*#i', $_SERVER['HTTP_USER_AGENT'] ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $_SERVER['HTTP_USER_AGENT'], 0, 4 ) ) ) ) {
419 $file_name .= '-mobile';
420 }
421 }
422
423 if ( isset( $GLOBALS['powered_cache_options']['loggedin_user_cache'] )
424 && true === $GLOBALS['powered_cache_options']['loggedin_user_cache']
425 ) {
426 $usr_cookie = powered_cache_get_user_cookie();
427 if ( false !== $usr_cookie ) {
428 $cookie_info = explode( '|', $usr_cookie );
429
430 // user specific cache dir
431 $file_name .= '_' . $cookie_info[0] . '-' . $cookie_info[1];
432 }
433 }
434
435 if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
436 $file_name .= '_' . sha1( $_SERVER['QUERY_STRING'] );
437 }
438
439 /**
440 * Content-Type is not always text/html (like feed, wp-json etc..)
441 * We should respect proper format!
442 * Alternatively, we can add multiple level lookup for apache/nginx config
443 * but that makes things much more complicated.
444 */
445 if ( false === strpos( $content_type, 'text/html' ) ) {
446 $file_name .= '-' . substr( sha1( $content_type ), 0, 6 );
447 }
448
449 $file_name .= '.html';
450
451 if ( function_exists( 'gzencode' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
452 $file_name .= '_gzip';
453 }
454
455 return $file_name;
456 }
457
458
459 /**
460 * is_ssl moved load.php in WP 4.6 but we support WP 4.1+
461 *
462 * @return bool
463 */
464 function powered_cache_is_ssl() {
465 if ( isset( $_SERVER['HTTPS'] ) ) {
466 if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) {
467 return true;
468 }
469
470 if ( '1' == $_SERVER['HTTPS'] ) {
471 return true;
472 }
473 } elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
474 return true;
475 }
476
477 return false;
478 }
479