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

470 lines 14.2 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 if ( array_key_exists( 'show_cache_message', $GLOBALS['powered_cache_options'] ) && true === $GLOBALS['powered_cache_options']['show_cache_message'] ) {
228 if ( preg_match( '#</html>#i', $buffer ) ) {
229 $buffer .= PHP_EOL;
230 $buffer .= "<!-- Cache served by PoweredCache -->";
231 $buffer .= PHP_EOL;
232 $buffer .= "<!-- If you like fast websites like this, visit: https://poweredcache.com -->";
233 $buffer .= PHP_EOL;
234 $buffer .= "<!-- Last modified: " . gmdate( 'D, d M Y H:i:s', $modified_time ) . " GMT -->";
235 $buffer .= PHP_EOL;
236 $buffer .= "<!-- Dynamic page generated in $generation_time -->";
237 $buffer .= PHP_EOL;
238 }
239 }
240
241
242 $meta_file_name = 'meta.php';
243 $meta_file = '<?php exit; ?>' . PHP_EOL;
244
245 $meta_params = array(); // holds to metadata for cached file
246
247 $response_headers = powered_cache_get_response_headers();
248
249 foreach ( (array) $response_headers as $key => $value ) {
250 $meta_params['headers'][ $key ] = "$key: $value";
251 }
252
253 $meta_params = apply_filters( 'powered_cache_page_cache_meta_params', $meta_params, $response_headers );
254 $meta_file_contents = $meta_file . serialize( $meta_params );
255
256 $meta_file_contents = apply_filters( 'powered_cache_page_cache_meta_info', $meta_file_contents );
257
258 $filesystem->put_contents( $path . '/' . $meta_file_name, $meta_file_contents, FS_CHMOD_FILE );
259 $filesystem->touch( $path . '/' . $meta_file_name, $modified_time );
260
261 if ( isset( $meta_params['headers']['Content-Type'] ) ) {
262 $index_name = powered_cache_index_file( $meta_params['headers']['Content-Type'] );
263 } else {
264 $index_name = powered_cache_index_file();
265 }
266
267 if ( $GLOBALS['powered_cache_options']['gzip_compression'] && function_exists( 'gzencode' ) ) {
268 $filesystem->put_contents( $path . '/' . $index_name, gzencode( $buffer, 3 ), FS_CHMOD_FILE );
269 $filesystem->touch( $path . '/' . $index_name, $modified_time );
270 } else {
271 $filesystem->put_contents( $path . '/' . $index_name, $buffer, FS_CHMOD_FILE );
272 $filesystem->touch( $path . '/' . $index_name, $modified_time );
273 }
274
275 do_action( 'powered_cache_page_cached', $buffer );
276
277 header( 'Cache-Control: no-cache' ); // Check back every time to see if re-download is necessary
278
279 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $modified_time ) . ' GMT' );
280
281 if ( function_exists( 'ob_gzhandler' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
282 return ob_gzhandler( $buffer, $flags );
283 } else {
284 return $buffer;
285 }
286 }
287
288
289 /**
290 * Optionally serve cache and exit
291 *
292 * @since 1.0
293 */
294 function powered_cache_serve_cache() {
295 global $powered_cache_slash_check;
296
297 $path = rtrim( $GLOBALS['powered_cache_options']['cache_location'], '/' ) . '/powered-cache/' . rtrim( powered_cache_get_url_path(), '/' ) . '/';
298
299 $meta_file = $path.'/meta.php';
300
301
302 if ( @file_exists( $meta_file ) ) {
303 $meta_contents = trim( file_get_contents( $meta_file ) );
304 $meta_contents = str_replace( '<?php exit; ?>', '', $meta_contents );
305 $meta_params = unserialize( trim( $meta_contents ) );
306 $header_params = $meta_params['headers'];
307 }
308
309 $content_type = @$header_params['Content-Type'];
310
311 $file_name = powered_cache_index_file( $content_type );
312 $file_path = $path.$file_name;
313
314 $modified_time = (int) @filemtime( $file_path );
315
316 header( 'Cache-Control: no-cache' ); // Check back in an hour
317
318 if ( ! empty( $modified_time ) && ! empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) && strtotime( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) === $modified_time ) {
319 header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 );
320 exit;
321 }
322
323 // trailingslash check
324 if ( isset( $powered_cache_slash_check ) && $powered_cache_slash_check ) {
325 $current_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
326 if ( ! empty( $current_path ) && '/' !== substr( $current_path, - 1 ) ) {
327 header( 'X-Powered-Cache: Passing to WordPress' );
328
329 return;
330 }
331 }
332
333 if ( @file_exists( $file_path ) && @is_readable( $file_path ) ) {
334
335 if ( ! empty( $header_params ) ) {
336 foreach ( $header_params as $key => $response_header ) {
337 header( $response_header );
338 }
339 }
340
341 header( 'X-Powered-Cache: PHP' );
342
343 if ( function_exists( 'gzencode' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
344 header( 'Content-Encoding: gzip' );
345 }
346
347 @readfile( $file_path );
348
349 exit;
350 }
351 }
352
353 /**
354 * Get URL path for caching
355 *
356 * @since 1.0
357 * @return string
358 */
359 function powered_cache_get_url_path() {
360
361 $host = ( isset( $_SERVER['HTTP_HOST'] ) ) ? $_SERVER['HTTP_HOST'] : '';
362
363 return rtrim( $host, '/' ) . parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
364 }
365
366 function powered_cache_get_user_cookie() {
367 if ( empty( $_COOKIE ) ) {
368 return false;
369 }
370
371 foreach ( $_COOKIE as $c_key => $val ) {
372 if ( false !== strpos( $c_key, 'wordpress_logged_in_' ) ) {
373 return $val;
374 }
375 }
376
377 return false;
378 }
379
380
381 /**
382 * Determines cache file names
383 *
384 * @param string $content_type
385 *
386 * @since 1.0
387 * @since 1.2 `$content_type`
388 * @return string
389 */
390 function powered_cache_index_file( $content_type = 'text/html' ) {
391 $file_name = 'index';
392
393 if ( powered_cache_is_ssl() ) {
394 $file_name .= '-https';
395 }
396
397 // separate file for mobile cache
398 if ( isset( $GLOBALS['powered_cache_options']['cache_mobile'], $GLOBALS['powered_cache_options']['cache_mobile_separate_file'] )
399 && true === $GLOBALS['powered_cache_options']['cache_mobile']
400 && true === $GLOBALS['powered_cache_options']['cache_mobile_separate_file']
401 ) {
402
403 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
404
405 $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_browsers ) ), ' ' );
406 $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_prefixes ) ), ' ' );
407
408 // Don't cache if mobile detection is activated
409 if ( (preg_match( '#^.*(' . $mobile_browsers . ').*#i', $_SERVER['HTTP_USER_AGENT'] ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $_SERVER['HTTP_USER_AGENT'], 0, 4 ) ) ) ) {
410 $file_name .= '-mobile';
411 }
412 }
413
414 if ( isset( $GLOBALS['powered_cache_options']['loggedin_user_cache'] )
415 && true === $GLOBALS['powered_cache_options']['loggedin_user_cache']
416 ) {
417 $usr_cookie = powered_cache_get_user_cookie();
418 if ( false !== $usr_cookie ) {
419 $cookie_info = explode( '|', $usr_cookie );
420
421 // user specific cache dir
422 $file_name .= '_' . $cookie_info[0] . '-' . $cookie_info[1];
423 }
424 }
425
426 if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
427 $file_name .= '_' . sha1( $_SERVER['QUERY_STRING'] );
428 }
429
430 /**
431 * Content-Type is not always text/html (like feed, wp-json etc..)
432 * We should respect proper format!
433 * Alternatively, we can add multiple level lookup for apache/nginx config
434 * but that makes things much more complicated.
435 */
436 if ( false === strpos( $content_type, 'text/html' ) ) {
437 $file_name .= '-' . mb_substr( sha1( $content_type ), 0, 6 );
438 }
439
440 $file_name .= '.html';
441
442 if ( function_exists( 'gzencode' ) && $GLOBALS['powered_cache_options']['gzip_compression'] ) {
443 $file_name .= '_gzip';
444 }
445
446 return $file_name;
447 }
448
449
450 /**
451 * is_ssl moved load.php in WP 4.6 but we support WP 4.1+
452 *
453 * @return bool
454 */
455 function powered_cache_is_ssl() {
456 if ( isset( $_SERVER['HTTPS'] ) ) {
457 if ( 'on' == strtolower( $_SERVER['HTTPS'] ) ) {
458 return true;
459 }
460
461 if ( '1' == $_SERVER['HTTPS'] ) {
462 return true;
463 }
464 } elseif ( isset( $_SERVER['SERVER_PORT'] ) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
465 return true;
466 }
467
468 return false;
469 }
470