PluginProbe
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution / 1.3.2
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution v1.3.2
2.0.1 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.9.0 2.0.0
solid-performance / src / Performance / Page_Cache / Cache.php

Cache.php in Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution 1.3.2, at src/Performance/Page_Cache/Cache.php

248 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles all functionality related to storing and returning files in the page cache directory.
4 *
5 * @since 0.1.0
6 *
7 * @package SolidWP\Performance
8 */
9
10 declare( strict_types=1 );
11
12 namespace SolidWP\Performance\Page_Cache;
13
14 use Exception;
15 use SolidWP\Performance\Container;
16 use SolidWP\Performance\Http\Request;
17 use SolidWP\Performance\Pipelines\Page_Cache;
18 use SolidWP\Performance\StellarWP\Pipeline\Pipeline;
19 use SolidWP\Performance\StellarWP\SuperGlobals\SuperGlobals;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit;
23 }
24
25 /**
26 * Handles all functionality related to storing and returning files in the page cache directory.
27 *
28 * @since 0.1.0
29 *
30 * @package SolidWP\Performance
31 */
32 class Cache {
33
34 /**
35 * The current request.
36 *
37 * @since 0.1.0
38 *
39 * @var Request
40 */
41 private Request $request;
42
43 /**
44 * @var Container
45 */
46 private Container $container;
47
48 /**
49 * @var Cache_Handler
50 */
51 private Cache_Handler $handler;
52
53 /**
54 * The pipes to send all requests through before serving an existing cached page.
55 *
56 * @context pre-wordpress
57 *
58 * @since 0.1.0
59 *
60 * @var array<int,string>
61 */
62 private array $serve_pipes = [
63 Page_Cache\Response_Code::class,
64 Page_Cache\Constant::class,
65 Page_Cache\Admin::class,
66 Page_Cache\Authenticated::class,
67 Page_Cache\Method::class,
68 Page_Cache\Path::class,
69 Page_Cache\Query::class,
70 Page_Cache\Exclusion::class,
71
72 // Third Party Exclusions.
73 Page_Cache\Third_Party_Exclusions\GiveWP::class,
74 Page_Cache\Third_Party_Exclusions\WooCommerce::class,
75 ];
76
77 /**
78 * The pipes to send new requests through before saving them to the cache directory.
79 *
80 * @context after-wordpress
81 *
82 * @since 0.1.0
83 *
84 * @var array<int,string>
85 */
86 private array $save_pipes = [
87 Page_Cache\Response_Code::class,
88 Page_Cache\Constant::class,
89 Page_Cache\Admin::class,
90 Page_Cache\Authenticated::class,
91 Page_Cache\Content_Type::class,
92 Page_Cache\Wp::class,
93 Page_Cache\Method::class,
94 Page_Cache\Path::class,
95 Page_Cache\Post::class,
96 Page_Cache\Query::class,
97 Page_Cache\Exclusion::class,
98
99 // Third Party Exclusions.
100 Page_Cache\Third_Party_Exclusions\GiveWP::class,
101 Page_Cache\Third_Party_Exclusions\WooCommerce::class,
102 ];
103
104 /**
105 * The class constructor.
106 *
107 * @since 0.1.0
108 *
109 * @param Request $request The current request.
110 * @param Container $container The DI container.
111 * @param Cache_Handler $handler The Cache Handler.
112 */
113 public function __construct(
114 Request $request,
115 Container $container,
116 Cache_Handler $handler
117 ) {
118 $this->request = $request;
119 $this->container = $container;
120 $this->handler = $handler;
121 }
122
123 /**
124 * Returns the cached file to the browser if it exists.
125 *
126 * @since 0.1.0
127 *
128 * @return void
129 */
130 public function serve_cached_file(): void {
131 if ( ! $this->should_serve_cached_file() ) {
132 return;
133 }
134
135 // Cache file doesn't exist or is expired.
136 if ( ! $this->handler->is_valid() ) {
137 return;
138 }
139
140 // Send Content-Encoding headers for compression, if any.
141 $this->handler->compressor()->send_headers();
142
143 $cache_file = $this->handler->get_file_path();
144
145 // If the user is checking their version of the cached resource.
146 $mod_time = filemtime( $cache_file );
147
148 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $mod_time ) . ' GMT' );
149
150 $modified_since = SuperGlobals::get_server_var( 'HTTP_IF_MODIFIED_SINCE', '' );
151
152 header( sprintf( 'X-Cache-Age: %d', time() - $mod_time ) );
153 header( 'X-Cached-By: Solid Performance' );
154
155 if ( $modified_since && strtotime( $modified_since ) === $mod_time ) {
156 // Cache is current, just respond with 304 headers.
157 header( SuperGlobals::get_server_var( 'SERVER_PROTOCOL', '' ) . ' 304 Not Modified', true, 304 );
158 header( 'Expires: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
159 header( 'Cache-Control: no-cache, must-revalidate' );
160 exit;
161 }
162
163 readfile( $cache_file );
164 exit;
165 }
166
167 /**
168 * Saves content to a new file in the cache.
169 *
170 * @since 0.1.0
171 *
172 * @throws Exception When cache cannot be saved or the cache directory can't be created.
173 *
174 * @param string $output Output to save in the cached file.
175 *
176 * @return void
177 */
178 public function save_output( string $output ): void {
179 // Don't save empty buffers.
180 if ( $output === '' ) {
181 return;
182 }
183
184 // Check if the current request should be cached.
185 if ( ! $this->should_save_cached_file() ) {
186 return;
187 }
188
189 $this->handler->save( $output );
190 }
191
192 /**
193 * Determines if the current request should be served an existing cached page.
194 *
195 * If any pipe returns false, the request will not be served from existing cache.
196 *
197 * @since 0.1.0
198 *
199 * @return bool
200 */
201 private function should_serve_cached_file(): bool {
202 $pipeline = new Pipeline( $this->container );
203 $context = new WP_Context( $this->request );
204
205 return $pipeline
206 ->send( $context )
207 ->through( $this->serve_pipes )
208 ->then(
209 function () {
210 // If we make it through the pipeline, we should cache the request.
211 return true;
212 }
213 );
214 }
215
216 /**
217 * Run through pipeline and determine if the current request should be saved to the cache directory.
218 *
219 * If any middleware return false, the current request will not be cached (or served from existing cache).
220 *
221 * @since 0.1.0
222 *
223 * @return bool
224 */
225 private function should_save_cached_file(): bool {
226 $pipeline = new Pipeline( $this->container );
227 $context = $this->container->get( WP_Context::class );
228
229 return $pipeline
230 ->send( $context )
231 ->through( $this->save_pipes )
232 ->then(
233 function () use ( $context ) {
234 /**
235 * If the request makes it through this pipeline, the request should be saved.
236 *
237 * However, since this is fired within a loaded WordPress context, developers can optionally hook in and determine if the response should be saved.
238 *
239 * @param WP_Context $context The context of the current request.
240 *
241 * @return bool
242 */
243 return apply_filters( 'solidwp/performance/should_save_cached_file', true, $context );
244 }
245 );
246 }
247 }
248