PluginProbe
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution / trunk
Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution vtrunk
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 / Preload / Provider.php
src/Performance/Preload
Contracts 1 year ago Exceptions 1 year ago Limiter 6 months ago Monitor 1 year ago Sitemap 1 year ago State 1 year ago Traits 1 year ago Client.php 2.5 KB 1 year ago Preload_Mode_Manager.php 1.5 KB 1 year ago Preload_Scheduler.php 8.2 KB 3 months ago Preloader.php 8.5 KB 1 year ago Provider.php 9.7 KB 3 months ago Proxy.php 1.0 KB 1 year ago Redirect_Client.php 4.0 KB 1 year ago Trigger_Url.php 1.0 KB 1 year ago Uri.php 961 B 1 year ago

Provider.php in Solid Performance – Your No-Code Caching, Performance, & Page Speed Solution trunk, at src/Performance/Preload/Provider.php

322 lines 9.7 KB Raw Download Zip
1 <?php
2 /**
3 * The Service Provider for Preloading functionality.
4 *
5 * @package SolidWP\Performance
6 */
7
8 declare( strict_types=1 );
9
10 namespace SolidWP\Performance\Preload;
11
12 use SolidWP\Performance\Contracts\Service_Provider;
13 use SolidWP\Performance\Core;
14 use SolidWP\Performance\Lock\Contracts\Blockable_Lock;
15 use SolidWP\Performance\Lock\Lock_Factory;
16 use SolidWP\Performance\Log\Log;
17 use SolidWP\Performance\Preload\Contracts\Preloadable;
18 use SolidWP\Performance\Preload\Limiter\Batch_Limiter;
19 use SolidWP\Performance\Preload\Limiter\Core_Counter;
20 use SolidWP\Performance\Preload\Limiter\Sleep_Limiter;
21 use SolidWP\Performance\Preload\Monitor\Monitor;
22 use SolidWP\Performance\Preload\Sitemap\Repositories\Contracts\Sitemap_Repository;
23 use SolidWP\Performance\Preload\Sitemap\Repositories\Sitemap_Option_Repository;
24 use SolidWP\Performance\Preload\Sitemap\Sitemap;
25 use SolidWP\Performance\Preload\State\Enums\Source;
26 use SolidWP\Performance\Preload\State\State;
27 use SolidWP\Performance\Psr\Log\LoggerInterface;
28 use SolidWP\Performance\Symfony\Component\HttpClient\HttpClient;
29 use SolidWP\Performance\Symfony\Component\HttpClient\ScopingHttpClient;
30 use SolidWP\Performance\Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
31 use SolidWP\Performance\Symfony\Contracts\HttpClient\Exception\TimeoutExceptionInterface;
32 use SolidWP\Performance\Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
33 use SolidWP\Performance\Symfony\Contracts\HttpClient\HttpClientInterface;
34 use Throwable;
35
36 /**
37 * The Service Provider for Preloading functionality.
38 *
39 * @package SolidWP\Performance
40 */
41 final class Provider extends Service_Provider {
42
43 public const PRELOADER_LOCK = 'solid_performance.preload.preloader_lock';
44
45 /**
46 * @inheritDoc
47 */
48 public function register(): void {
49 $this->register_http_client();
50 $this->register_preload_trigger_url();
51 $this->register_sitemap_repository();
52 $this->register_preload_monitor();
53 $this->register_rate_limiter();
54 $this->register_preloader();
55 $this->register_preload_scheduler();
56 }
57
58 /**
59 * Register the underlying Kadence Performance Preloader HTTP Client.
60 *
61 * @return void
62 */
63 private function register_http_client(): void {
64 $this->container->singleton(
65 HttpClientInterface::class,
66 function () {
67 $options = [
68 'headers' => [
69 'User-Agent' => sprintf(
70 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 (compatible; Kadence Performance/%s)',
71 $this->container->get( Core::PLUGIN_VERSION )
72 ),
73 ],
74 'verify_peer' => apply_filters( 'https_local_ssl_verify', false ),
75 'verify_host' => apply_filters( 'https_local_ssl_verify', false ),
76 ];
77
78 $proxy_url = $this->container->get( Proxy::class )->url();
79
80 if ( $proxy_url ) {
81 $options['proxy'] = $proxy_url;
82 }
83
84 $logger = $this->container->get( LoggerInterface::class );
85
86 $client = ScopingHttpClient::forBaseUri( HttpClient::create(), home_url(), $options );
87
88 $redirect_client = new Redirect_Client( $client );
89 $redirect_client->setLogger( $logger );
90
91 return $redirect_client;
92 }
93 );
94 }
95
96 /**
97 * Register the URL to use to trigger to a preload batch.
98 *
99 * @return void
100 */
101 private function register_preload_trigger_url(): void {
102 /**
103 * Filter the URL to use to trigger a preload batch.
104 *
105 * @param string $trigger_url The URL to use to trigger a preload batch.
106 */
107 $trigger_url = apply_filters( 'solidwp/performance/preload/trigger_url', home_url( '/' ) );
108
109 $this->container->when( Trigger_Url::class )
110 ->needs( '$trigger_url' )
111 ->give( static fn(): string => $trigger_url );
112
113 $this->container->singleton( Trigger_Url::class, Trigger_Url::class );
114 }
115
116 /**
117 * Register the concrete repository to use for the Sitemap Repository.
118 *
119 * @return void
120 */
121 private function register_sitemap_repository(): void {
122 $this->container->when( Sitemap::class )
123 ->needs( '$url' )
124 ->give(
125 function (): string {
126 // We assume this automatically redirects to the correct sitemap.
127 return home_url( '/sitemap.xml' );
128 }
129 );
130
131 $this->container->bind( Sitemap_Repository::class, Sitemap_Option_Repository::class );
132 }
133
134 /**
135 * Register the preload monitor to check for a stalled preloader.
136 *
137 * @return void
138 */
139 private function register_preload_monitor(): void {
140 /**
141 * Filter how many seconds to wait before we check for a stalled preloader.
142 *
143 * @param int $timeout The timeout in seconds.
144 */
145 $timeout = (int) apply_filters( 'solidwp/performance/preload/monitor/timeout', 12 );
146
147 /**
148 * Filter the maximum number of times we retry a stalled preloader before giving up.
149 *
150 * @param int $max_retries The number of retries.
151 */
152 $max_retries = (int) apply_filters( 'solidwp/performance/preload/monitor/max_retries', 10 );
153
154 $this->container->when( Monitor::class )
155 ->needs( '$timeout' )
156 ->give( static fn(): int => $timeout );
157
158 $this->container->when( Monitor::class )
159 ->needs( '$max_retries' )
160 ->give( static fn(): int => $max_retries );
161 }
162
163 /**
164 * Register the rate limiter that controls the batch size of the preloader.
165 *
166 * @return void
167 */
168 private function register_rate_limiter(): void {
169 $this->container->singleton( Core_Counter::class, Core_Counter::class );
170 $this->container->singleton( Sleep_Limiter::class, Sleep_Limiter::class );
171 $this->container->singleton( Batch_Limiter::class, Batch_Limiter::class );
172
173 /**
174 * Filters the maximum load per CPU core before we start rate limiting requests.
175 *
176 * 0-0.7 per core is optimal range, allowing headroom.
177 * 0.7-1.0 per core is high, but manageable.
178 * 1.0+ per core indicates CPU is likely bottle necked.
179 *
180 * @param float $max_load_per_core The maximum load per core before we begin rate limiting preloading.
181 */
182 $max_load_per_core = apply_filters( 'solidwp/performance/preload/max_load_per_core', 0.5 );
183
184 /**
185 * Filters the delay multiplier to calculate the sleep time.
186 *
187 * @param float $delay The delay multiplier to determine how much more sleep based on system load.
188 */
189 $delay = apply_filters( 'solidwp/performance/preload/sleep_multiplier', 1.5 );
190
191 $this->container->when( Sleep_Limiter::class )
192 ->needs( '$max_load_per_core' )
193 ->give( static fn(): float => $max_load_per_core );
194
195 $this->container->when( Sleep_Limiter::class )
196 ->needs( '$delay' )
197 ->give( static fn(): float => $delay );
198
199 /**
200 * Filter how many URLs are processed per batch.
201 *
202 * @param int $batch_size The number of URLs to process per batch.
203 */
204 $batch_size = apply_filters( 'solidwp/performance/preload/batch_size', 50 );
205
206 /**
207 * Filter the decay value for how much we'll reduce the batch size when server load is high.
208 *
209 * @param float $decay The negative value used to calculate exponential decay.
210 */
211 $decay = apply_filters( 'solidwp/performance/preload/decay', -0.9 );
212
213 $this->container->when( Batch_Limiter::class )
214 ->needs( '$batch_size' )
215 ->give( static fn(): int => $batch_size );
216
217 $this->container->when( Batch_Limiter::class )
218 ->needs( '$max_load_per_core' )
219 ->give( static fn(): float => $max_load_per_core );
220
221 $this->container->when( Batch_Limiter::class )
222 ->needs( '$decay' )
223 ->give( static fn(): float => $decay );
224 }
225
226 /**
227 * Register the Preloader.
228 *
229 * @return void
230 */
231 private function register_preloader(): void {
232 /**
233 * Filters how long to sleep between batches.
234 *
235 * @param int $seconds How to sleep for in seconds.
236 */
237 $seconds = apply_filters( 'solidwp/performance/preload/batch/sleep/seconds', 1 );
238
239 $this->container->when( Preloader::class )
240 ->needs( '$sleep' )
241 ->give( static fn(): int => $seconds );
242
243 $this->container->singleton( Preloadable::class, Preloader::class );
244
245 add_action( 'template_redirect', $this->container->callback( Preloadable::class, 'preload' ), 0 );
246
247 // Automatically try to preload URLs after they are purged.
248 add_action(
249 'solidwp/performance/cache/purge/url/after',
250 function ( string $url ) {
251 $preloader = $this->container->get( Preloadable::class );
252
253 try {
254 $preloader->preload_url( $url );
255 } catch ( TimeoutExceptionInterface |TransportExceptionInterface $e ) {
256 do_action(
257 Log::INFO,
258 'Timeout for preload {url}',
259 [
260 'url' => $url,
261 'exception' => $e,
262 ]
263 );
264 } catch ( ClientExceptionInterface $e ) {
265 do_action(
266 Log::WARNING,
267 'Failed to preload {url}',
268 [
269 'url' => $url,
270 'exception' => $e,
271 ]
272 );
273 } catch ( Throwable $e ) {
274 do_action(
275 Log::ERROR,
276 'Failed to preload {url}',
277 [
278 'url' => $url,
279 'exception' => $e,
280 ]
281 );
282 }
283 },
284 20,
285 1
286 );
287 }
288
289 /**
290 * Register the preload scheduler.
291 *
292 * @return void
293 */
294 private function register_preload_scheduler(): void {
295 $this->container->singleton( State::class, State::class );
296
297 /**
298 * Filter how long the preloader stays locked until it's canceled or completes.
299 *
300 * @param int $ttl How long in seconds the preloader will be locked until it's canceled or completes.
301 */
302 $ttl = apply_filters( 'solidwp/performance/preload/lock/ttl', DAY_IN_SECONDS );
303
304 $this->container->singleton(
305 self::PRELOADER_LOCK,
306 function () use ( $ttl ): Blockable_Lock {
307 $owner = Source::detect_source();
308
309 return $this->container->get( Lock_Factory::class )->make( 'is_preloading', $ttl, $owner );
310 }
311 );
312
313 $this->container->singleton( Preload_Scheduler::class, Preload_Scheduler::class );
314
315 $this->container->when( Preload_Scheduler::class )
316 ->needs( Blockable_Lock::class )
317 ->give(
318 fn(): Blockable_Lock => $this->container->get( self::PRELOADER_LOCK )
319 );
320 }
321 }
322