PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.26.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.26.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / api / class-performance-endpoint.php

class-performance-endpoint.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.26.0, at includes/api/class-performance-endpoint.php

486 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Performance API Endpoint
4 *
5 * REST API endpoints for Core Web Vitals monitoring and performance insights.
6 * Connects the frontend Performance tab to the existing Performance Monitoring Manager.
7 *
8 * @package ThinkRank
9 * @subpackage API
10 * @since 1.0.0
11 */
12
13 namespace ThinkRank\API;
14
15 use ThinkRank\SEO\Performance_Monitoring_Manager;
16 use ThinkRank\SEO\Performance_Data_Collector;
17 use ThinkRank\SEO\Analytics_Manager;
18 use ThinkRank\API\Traits\API_Cache;
19 use WP_REST_Controller;
20 use WP_REST_Server;
21 use WP_REST_Request;
22 use WP_REST_Response;
23 use WP_Error;
24
25 // Prevent direct access
26 if (!defined('ABSPATH')) {
27 exit;
28 }
29
30 // Load API Cache trait
31 require_once THINKRANK_PLUGIN_DIR . 'includes/api/traits/trait-api-cache.php';
32
33 /**
34 * Performance API Endpoint Class
35 *
36 * @since 1.0.0
37 */
38 class Performance_Endpoint extends WP_REST_Controller {
39
40 use API_Cache;
41
42 /**
43 * REST API namespace
44 *
45 * @since 1.0.0
46 * @var string
47 */
48 protected $namespace = 'thinkrank/v1';
49
50 /**
51 * REST API base
52 *
53 * @since 1.0.0
54 * @var string
55 */
56 protected $rest_base = 'performance';
57
58 /**
59 * Performance Monitoring Manager instance
60 *
61 * @since 1.0.0
62 * @var Performance_Monitoring_Manager|null
63 */
64 private ?Performance_Monitoring_Manager $performance_manager = null;
65
66 /**
67 * Performance Data Collector instance (lazy)
68 *
69 * @since 1.0.0
70 * @var Performance_Data_Collector|null
71 */
72 private ?Performance_Data_Collector $data_collector = null;
73
74 /**
75 * Constructor
76 *
77 * Endpoint objects are constructed on every REST request (any namespace),
78 * so the manager chain is built lazily — only when one of this endpoint's
79 * routes actually executes.
80 *
81 * @since 1.0.0
82 */
83 public function __construct() {
84 // Configure caching for performance endpoints
85 $this->set_cache_prefix('thinkrank_performance_');
86 $this->set_cache_duration(300); // 5 minutes for performance data
87 }
88
89 /**
90 * Get the Performance Monitoring Manager (lazy)
91 *
92 * @return Performance_Monitoring_Manager
93 */
94 private function get_performance_manager(): Performance_Monitoring_Manager {
95 if ($this->performance_manager === null) {
96 $this->performance_manager = new Performance_Monitoring_Manager();
97 }
98 return $this->performance_manager;
99 }
100
101 /**
102 * Get the Performance Data Collector (lazy)
103 *
104 * @return Performance_Data_Collector
105 */
106 private function get_data_collector(): Performance_Data_Collector {
107 if ($this->data_collector === null) {
108 $this->data_collector = new Performance_Data_Collector();
109 }
110 return $this->data_collector;
111 }
112
113 /**
114 * Register REST API routes
115 *
116 * @since 1.0.0
117 */
118 public function register_routes() {
119 // Monitor endpoint - Get current performance data
120 register_rest_route(
121 $this->namespace,
122 '/' . $this->rest_base . '/monitor',
123 [
124 [
125 'methods' => WP_REST_Server::READABLE,
126 'callback' => [$this, 'get_performance_data'],
127 'permission_callback' => [$this, 'check_read_permissions'],
128 'args' => [
129 'device_type' => [
130 'default' => 'mobile',
131 'type' => 'string',
132 'enum' => ['mobile', 'desktop'],
133 'sanitize_callback' => 'sanitize_key'
134 ]
135 ]
136 ]
137 ]
138 );
139
140
141
142 // Recommendations endpoint
143 register_rest_route(
144 $this->namespace,
145 '/' . $this->rest_base . '/recommendations',
146 [
147 [
148 'methods' => WP_REST_Server::READABLE,
149 'callback' => [$this, 'get_recommendations'],
150 'permission_callback' => [$this, 'check_read_permissions']
151 ]
152 ]
153 );
154
155 // Historical data endpoint
156 register_rest_route(
157 $this->namespace,
158 '/' . $this->rest_base . '/history',
159 [
160 [
161 'methods' => WP_REST_Server::READABLE,
162 'callback' => [$this, 'get_historical_data'],
163 'permission_callback' => [$this, 'check_read_permissions'],
164 'args' => $this->get_historical_data_args()
165 ]
166 ]
167 );
168
169 // Opportunities endpoint
170 register_rest_route(
171 $this->namespace,
172 '/' . $this->rest_base . '/opportunities',
173 [
174 [
175 'methods' => WP_REST_Server::READABLE,
176 'callback' => [$this, 'get_opportunities'],
177 'permission_callback' => [$this, 'check_read_permissions'],
178 'args' => [
179 'device_type' => [
180 'default' => 'mobile',
181 'type' => 'string',
182 'enum' => ['mobile', 'desktop'],
183 'sanitize_callback' => 'sanitize_key'
184 ]
185 ]
186 ]
187 ]
188 );
189
190 // Diagnostics endpoint
191 register_rest_route(
192 $this->namespace,
193 '/' . $this->rest_base . '/diagnostics',
194 [
195 [
196 'methods' => WP_REST_Server::READABLE,
197 'callback' => [$this, 'get_diagnostics'],
198 'permission_callback' => [$this, 'check_read_permissions'],
199 'args' => [
200 'device_type' => [
201 'default' => 'mobile',
202 'type' => 'string',
203 'enum' => ['mobile', 'desktop'],
204 'sanitize_callback' => 'sanitize_key'
205 ]
206 ]
207 ]
208 ]
209 );
210
211 // Data collection endpoint
212 register_rest_route(
213 $this->namespace,
214 '/' . $this->rest_base . '/collect',
215 [
216 [
217 'methods' => WP_REST_Server::CREATABLE,
218 'callback' => [$this, 'collect_performance_data'],
219 'permission_callback' => [$this, 'check_manage_permissions']
220 ]
221 ]
222 );
223
224
225 }
226
227 /**
228 * Get comprehensive performance data
229 *
230 * @since 1.0.0
231 *
232 * @param WP_REST_Request $request Request object
233 * @return WP_REST_Response|WP_Error Response object or error
234 */
235 public function get_performance_data(WP_REST_Request $request) {
236 try {
237 // Get device type from request
238 $device_type = $request->get_param('device_type') ?? 'mobile';
239
240 $cache_params = ['device_type' => $device_type];
241 // Core Web Vitals are site-wide, not user-specific — a per-user
242 // cache key would duplicate the entry (and the cold-path work)
243 // for every admin user.
244 $user_id = null;
245 $cache_enabled = $this->is_caching_enabled();
246
247 // Serve a fresh cached response when available.
248 if ($cache_enabled) {
249 $cached = $this->get_cached_response('performance_data', $cache_params, $user_id);
250 if ($cached !== null) {
251 return new WP_REST_Response(
252 array_merge($cached['data'], [
253 'cached' => true,
254 'cached_at' => $cached['cached_at'],
255 ]),
256 200
257 );
258 }
259 }
260
261 // Serve from the existing cache / collected DB data first; only the
262 // background collector performs a live PageSpeed audit. A cold cache
263 // no longer blocks the request on a 10-40s inline Lighthouse run.
264 $response_data = $this->get_performance_manager()->get_performance_snapshot($device_type);
265
266 // Don't pin a transient "collecting" state in the response cache — the
267 // background collection must be re-checked on the next request.
268 if ($cache_enabled && ($response_data['data']['status'] ?? '') !== 'collecting') {
269 $this->set_cached_response('performance_data', $response_data, $cache_params, null, $user_id);
270 }
271
272 return new WP_REST_Response(array_merge($response_data, ['cached' => false]), 200);
273
274 } catch (\Exception $e) {
275 return new WP_Error(
276 'performance_data_failed',
277 'Failed to retrieve performance data: ' . $e->getMessage(),
278 ['status' => 500]
279 );
280 }
281 }
282
283
284
285
286
287 /**
288 * Get performance recommendations
289 *
290 * @since 1.0.0
291 *
292 * @param WP_REST_Request $request Request object
293 * @return WP_REST_Response|WP_Error Response object or error
294 */
295 public function get_recommendations(WP_REST_Request $request) {
296 try {
297 $recommendations = $this->get_performance_manager()->get_performance_recommendations();
298
299 return new WP_REST_Response([
300 'success' => true,
301 'data' => $recommendations,
302 'message' => __('Performance recommendations retrieved successfully', 'thinkrank')
303 ], 200);
304
305 } catch (\Exception $e) {
306 return new WP_Error(
307 'recommendations_failed',
308 'Failed to retrieve recommendations: ' . $e->getMessage(),
309 ['status' => 500]
310 );
311 }
312 }
313
314 /**
315 * Get historical performance data
316 *
317 * @since 1.0.0
318 *
319 * @param WP_REST_Request $request Request object
320 * @return WP_REST_Response|WP_Error Response object or error
321 */
322 public function get_historical_data(WP_REST_Request $request) {
323 try {
324 $days = $request->get_param('days') ?? 30;
325 $metric = $request->get_param('metric') ?? 'all';
326
327 $historical_data = $this->get_performance_manager()->get_historical_data($days, $metric);
328
329 return new WP_REST_Response([
330 'success' => true,
331 'data' => $historical_data,
332 'message' => __('Historical data retrieved successfully', 'thinkrank')
333 ], 200);
334
335 } catch (\Exception $e) {
336 return new WP_Error(
337 'historical_data_failed',
338 'Failed to retrieve historical data: ' . $e->getMessage(),
339 ['status' => 500]
340 );
341 }
342 }
343
344 /**
345 * Get performance opportunities
346 *
347 * @since 1.0.0
348 *
349 * @param WP_REST_Request $request Request object
350 * @return WP_REST_Response|WP_Error Response object or error
351 */
352 public function get_opportunities(WP_REST_Request $request) {
353 try {
354 // Get device type from request
355 $device_type = $request->get_param('device_type') ?? 'mobile';
356
357 $opportunities = $this->get_performance_manager()->get_performance_opportunities('', $device_type);
358
359 return new WP_REST_Response([
360 'success' => true,
361 'data' => $opportunities,
362 'device_type' => $device_type,
363 'message' => __('Performance opportunities retrieved successfully', 'thinkrank')
364 ], 200);
365
366 } catch (\Exception $e) {
367 return new WP_Error(
368 'opportunities_failed',
369 'Failed to retrieve performance opportunities: ' . $e->getMessage(),
370 ['status' => 500]
371 );
372 }
373 }
374
375 /**
376 * Get performance diagnostics
377 *
378 * @since 1.0.0
379 *
380 * @param WP_REST_Request $request Request object
381 * @return WP_REST_Response|WP_Error Response object or error
382 */
383 public function get_diagnostics(WP_REST_Request $request) {
384 try {
385 // Get device type from request
386 $device_type = $request->get_param('device_type') ?? 'mobile';
387
388 $diagnostics = $this->get_performance_manager()->get_performance_diagnostics('', $device_type);
389
390 return new WP_REST_Response([
391 'success' => true,
392 'data' => $diagnostics,
393 'device_type' => $device_type,
394 'message' => __('Performance diagnostics retrieved successfully', 'thinkrank')
395 ], 200);
396
397 } catch (\Exception $e) {
398 return new WP_Error(
399 'diagnostics_failed',
400 'Failed to retrieve performance diagnostics: ' . $e->getMessage(),
401 ['status' => 500]
402 );
403 }
404 }
405
406 /**
407 * Manually collect performance data
408 *
409 * @since 1.0.0
410 *
411 * @param WP_REST_Request $request Request object
412 * @return WP_REST_Response|WP_Error Response object or error
413 */
414 public function collect_performance_data(WP_REST_Request $request) {
415 try {
416 $results = $this->get_data_collector()->manual_collect();
417
418 return new WP_REST_Response([
419 'success' => $results['success'],
420 'data' => $results,
421 'message' => $results['message']
422 ], $results['success'] ? 200 : 500);
423
424 } catch (\Exception $e) {
425 return new WP_Error(
426 'data_collection_failed',
427 'Failed to collect performance data: ' . $e->getMessage(),
428 ['status' => 500]
429 );
430 }
431 }
432
433
434
435 /**
436 * Check read permissions
437 *
438 * @since 1.0.0
439 *
440 * @return bool True if user can read
441 */
442 public function check_read_permissions(): bool {
443 // Performance data + settings are not subscriber-visible — require the
444 // same Performance management capability as the write routes.
445 return \ThinkRank\Core\Capability_Manager::current_user_can('thinkrank_performance');
446 }
447
448 /**
449 * Check manage permissions
450 *
451 * @since 1.0.0
452 *
453 * @return bool True if user can manage options
454 */
455 public function check_manage_permissions(): bool {
456 return \ThinkRank\Core\Capability_Manager::current_user_can('thinkrank_performance');
457 }
458
459 /**
460 * Get arguments for historical data endpoint
461 *
462 * @since 1.0.0
463 *
464 * @return array Arguments array
465 */
466 private function get_historical_data_args(): array {
467 return [
468 'days' => [
469 'required' => false,
470 'type' => 'integer',
471 'default' => 30,
472 'minimum' => 1,
473 'maximum' => 365,
474 'description' => 'Number of days of historical data to retrieve'
475 ],
476 'metric' => [
477 'required' => false,
478 'type' => 'string',
479 'default' => 'all',
480 'enum' => ['all', 'lcp', 'fid', 'cls', 'inp', 'score'],
481 'description' => 'Specific metric to retrieve'
482 ]
483 ];
484 }
485 }
486