PluginProbe
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution / 1.8.0
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution v1.8.0
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 / API / Routes / Page_Cache / Preload.php

Preload.php in Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution 1.8.0, at src/Performance/API/Routes/Page_Cache/Preload.php

325 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The route for starting, cancelling and getting preloading progress updates.
4 *
5 * @package SolidWP\Performance
6 */
7
8 namespace SolidWP\Performance\API\Routes\Page_Cache;
9
10 use InvalidArgumentException;
11 use SolidWP\Performance\API\Base_Route;
12 use SolidWP\Performance\Preload\Exceptions\PreloaderInProgressException;
13 use SolidWP\Performance\Preload\Exceptions\PreloadException;
14 use SolidWP\Performance\Preload\Monitor\Exceptions\PreloadMonitorMaxRetriesException;
15 use SolidWP\Performance\Preload\Preload_Scheduler;
16 use SolidWP\Performance\Preload\State\Enums\Status;
17 use SolidWP\Performance\Storage\Exceptions\InvalidKeyException;
18 use Throwable;
19 use WP_Error;
20 use WP_Http;
21 use WP_REST_Request;
22 use WP_REST_Response;
23 use WP_REST_Server;
24
25 if ( ! defined( 'ABSPATH' ) ) {
26 exit;
27 }
28
29 /**
30 * The route for starting, cancelling and getting preloading progress updates.
31 *
32 * @package SolidWP\Performance
33 */
34 final class Preload extends Base_Route {
35
36 public const PARAM_FORCE = 'force';
37 private const CODE_RUNNING = 'solid_performance_preloader_already_running';
38 private const CODE_STARTED = 'solid_performance_preloader_started';
39 private const CODE_COMPLETED = 'solid_performance_preloader_completed';
40 private const CODE_CANCELED = 'solid_performance_preloader_canceled';
41 private const CODE_STALLED = 'solid_performance_preloader_stalled';
42 private const CODE_FAILED = 'solid_performance_preloader_failed';
43
44 private const CODES = [
45 self::CODE_RUNNING,
46 self::CODE_STARTED,
47 self::CODE_COMPLETED,
48 self::CODE_CANCELED,
49 self::CODE_STALLED,
50 self::CODE_FAILED,
51 ];
52
53 /**
54 * @var Preload_Scheduler
55 */
56 private Preload_Scheduler $preloader;
57
58 /**
59 * @param Preload_Scheduler $preloader The preload scheduler.
60 */
61 public function __construct( Preload_Scheduler $preloader ) {
62 $this->preloader = $preloader;
63 }
64
65 /**
66 * {@inheritdoc}
67 */
68 public function get_path(): string {
69 return '/page/preload';
70 }
71
72 /**
73 * {@inheritdoc}
74 */
75 public function get_methods() {
76 return [
77 WP_REST_Server::READABLE,
78 WP_REST_Server::CREATABLE,
79 WP_REST_Server::DELETABLE,
80 ];
81 }
82
83 /**
84 * {@inheritdoc}
85 */
86 public function callback( WP_REST_Request $request ) {
87 $is_preloading = $this->preloader->is_running();
88
89 // Start the preloader.
90 if ( $request->get_method() === WP_REST_Server::CREATABLE ) {
91 return $this->start_preloader( $request );
92 } elseif ( $request->get_method() === WP_REST_Server::DELETABLE ) {
93 return $this->preloader_canceled();
94 }
95
96 $state = $this->preloader->state()->get();
97
98 try {
99 // sessionStorage on the frontend prevents this from being displayed over and over.
100 if ( ! $is_preloading ) {
101 if ( $state->status === Status::COMPLETED ) {
102 // Preloading completed normally.
103 return new WP_REST_Response(
104 [
105 'code' => self::CODE_COMPLETED,
106 /* translators: %s: The human-readable duration. */
107 'message' => sprintf( __( 'Preloading completed in %s.', 'solid-performance' ), $state->duration ),
108 'running' => false,
109 'progress' => $this->preloader->progress(),
110 'preloadId' => $state->id,
111 ]
112 );
113 } elseif ( $state->status === Status::CANCELED ) {
114 return new WP_REST_Response(
115 [
116 'code' => self::CODE_CANCELED,
117 /* translators: %s: The source of what initiated or canceled the preloader. */
118 'message' => sprintf( __( 'Preloading canceled via "%s".', 'solid-performance' ), $state->source ),
119 'running' => false,
120 'progress' => $this->preloader->progress(),
121 'preloadId' => $state->id,
122 ]
123 );
124 }
125 }
126
127 // Send the current progress.
128 return new WP_REST_Response(
129 [
130 'running' => $is_preloading,
131 'source' => $state->source,
132 'progress' => $this->preloader->progress(),
133 ]
134 );
135 } catch ( PreloadMonitorMaxRetriesException $e ) {
136 // The preloader was stalled and tried to restart until the max retries were reached.
137 $this->preloader->fail( $e->getMessage() );
138
139 return new WP_REST_Response(
140 [
141 'code' => self::CODE_FAILED,
142 /* translators: %s: The error message. */
143 'message' => sprintf( __( 'Preloading Failed: %s', 'solid-performance' ), $e->getMessage() ),
144 'running' => false,
145 'progress' => 0,
146 'preloadId' => $state->id,
147 ]
148 );
149 } catch ( PreloadException $e ) {
150 // The preloader is stalled and is being retried.
151 return new WP_REST_Response(
152 [
153 'code' => self::CODE_STALLED,
154 /* translators: %s: The error message. */
155 'message' => sprintf( __( 'Attempting to restart the Preloader. An error occurred: %s', 'solid-performance' ), $e->getMessage() ),
156 'running' => true,
157 'progress' => 0,
158 'preloadId' => $state->id,
159 ]
160 );
161 }
162 }
163
164 /**
165 * {@inheritdoc}
166 */
167 public function get_arguments(): array {
168 return [
169 WP_REST_Server::CREATABLE => [
170 self::PARAM_FORCE => [
171 'type' => 'boolean',
172 'description' => esc_html__( 'Whether to force a full site preload.', 'solid-performance' ),
173 'default' => false,
174 'required' => false,
175 ],
176 ],
177 ];
178 }
179
180 /**
181 * {@inheritdoc}
182 */
183 public function schema_callback(): array {
184 return [
185 '$schema' => 'http://json-schema.org/draft-04/schema#',
186 'title' => 'preload status',
187 'type' => 'object',
188 'properties' => [
189 'code' => [
190 'description' => esc_html__( 'The identification code of the preloading state.', 'solid-performance' ),
191 'type' => 'string',
192 'enum' => self::CODES,
193 'readonly' => true,
194 ],
195 'message' => [
196 'description' => esc_html__( 'The message describing the current preloading state or action result.', 'solid-performance' ),
197 'type' => 'string',
198 'readonly' => true,
199 ],
200 'source' => [
201 'description' => esc_html__( 'The source of where the preloader started running.', 'solid-performance' ),
202 'type' => 'string',
203 'enum' => [
204 'web',
205 'cli',
206 ],
207 'readonly' => true,
208 ],
209 'running' => [
210 'description' => esc_html__( 'Indicates whether preloading is currently running.', 'solid-performance' ),
211 'type' => 'boolean',
212 'readonly' => true,
213 ],
214 'progress' => [
215 'description' => esc_html__( 'The progress of the preloading process as a percentage.', 'solid-performance' ),
216 'type' => 'integer',
217 'minimum' => 0,
218 'maximum' => 100,
219 'readonly' => true,
220 ],
221 'preloadId' => [
222 'description' => esc_html__( 'The preload ID to determine if the current session has shown it yet.', 'solid-performance' ),
223 'type' => 'string',
224 'readonly' => true,
225 ],
226 ],
227 'oneOf' => [
228 [
229 'required' => [ 'code', 'message' ],
230 ],
231 [
232 'required' => [ 'running', 'message', 'preloadId' ],
233 ],
234 [
235 'required' => [ 'running', 'source', 'progress' ],
236 ],
237 [
238 'required' => [ 'code', 'message', 'running', 'progress', 'preloadId' ],
239 ],
240 ],
241 ];
242 }
243
244 /**
245 * Start the preloader.
246 *
247 * @param WP_REST_Request $request The current request.
248 *
249 * @throws PreloadException If an error occurs when we fail the preloader.
250 * @throws InvalidKeyException If an invalid storage key is used.
251 * @throws InvalidArgumentException If an invalid source or status is provided.
252 *
253 * @return WP_Error|WP_REST_Response
254 */
255 private function start_preloader( WP_REST_Request $request ) {
256 $force = (bool) $request->get_param( self::PARAM_FORCE );
257
258 try {
259 $this->preloader->start( $force );
260 } catch ( PreloaderInProgressException $e ) {
261 return new WP_Error(
262 self::CODE_RUNNING,
263 sprintf(
264 /* translators: %s: The source of what initiated or canceled the preloader. */
265 __( 'A preloader is already running via "%s".', 'solid-performance' ),
266 $this->preloader->state()->get()->source
267 ),
268 [
269 'status' => WP_Http::CONFLICT,
270 ]
271 );
272 } catch ( PreloadException $e ) {
273 return new WP_Error(
274 self::CODE_RUNNING,
275 /* translators: %s: The error message. */
276 sprintf( __( 'Warning: %s', 'solid-performance' ), $e->getMessage() )
277 );
278 } catch ( Throwable $e ) {
279 $this->preloader->fail( $e->getMessage() );
280
281 return new WP_Error(
282 self::CODE_COMPLETED,
283 /* translators: %s: The error message. */
284 sprintf( __( 'An error occurred: %s', 'solid-performance' ), $e->getMessage() )
285 );
286 }
287
288 $force_text = $force ? __( 'Preload & Refresh All', 'solid-performance' ) : __( 'Preload Uncached Pages', 'solid-performance' );
289
290 return new WP_REST_Response(
291 [
292 'code' => self::CODE_STARTED,
293 'message' => $force_text . ': ' . sprintf(
294 /* translators: %s: The number of URLs to preload. */
295 _n(
296 'Preparing to preload %s crawled sitemap URL.',
297 'Preparing to preload %s crawled sitemap URLs.',
298 $this->preloader->count(),
299 'solid-performance'
300 ),
301 number_format_i18n( (float) $this->preloader->count() )
302 ),
303 ],
304 WP_Http::ACCEPTED
305 );
306 }
307
308 /**
309 * The preloader was canceled.
310 *
311 * @return WP_REST_Response
312 */
313 private function preloader_canceled(): WP_REST_Response {
314 $state = $this->preloader->cancel()->state()->get();
315
316 return new WP_REST_Response(
317 [
318 'running' => $this->preloader->is_running(),
319 'message' => __( 'Preloading successfully canceled.', 'solid-performance' ),
320 'preloadId' => $state->id,
321 ]
322 );
323 }
324 }
325