PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.7.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.7.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-seo-analyzer-endpoint.php

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

174 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Site SEO Analyzer REST API Endpoint
4 *
5 * Exposes the site-wide SEO Analyzer:
6 * - GET /thinkrank/v1/seo-analyzer → last cached analysis (computes if empty)
7 * - POST /thinkrank/v1/seo-analyzer/run → force a fresh analysis (busts the cache)
8 *
9 * @package ThinkRank\API
10 * @since 1.18.0
11 */
12
13 declare(strict_types=1);
14
15 namespace ThinkRank\API;
16
17 use ThinkRank\SEO\SEO_Analyzer;
18 use WP_REST_Request;
19 use WP_REST_Response;
20
21 // Prevent direct access
22 if (!defined('ABSPATH')) {
23 exit;
24 }
25
26 /**
27 * SEO Analyzer Endpoint Class
28 *
29 * @since 1.18.0
30 */
31 class SEO_Analyzer_Endpoint {
32
33 /**
34 * API namespace.
35 */
36 private const NAMESPACE = 'thinkrank/v1';
37
38 /**
39 * Analyzer engine.
40 *
41 * @var SEO_Analyzer
42 */
43 private SEO_Analyzer $analyzer;
44
45 /**
46 * Constructor.
47 *
48 * @param SEO_Analyzer|null $analyzer Optional analyzer (injected in tests).
49 */
50 public function __construct(?SEO_Analyzer $analyzer = null) {
51 $this->analyzer = $analyzer ?? new SEO_Analyzer();
52 }
53
54 /**
55 * Register REST API routes.
56 *
57 * @return void
58 */
59 public function register_routes(): void {
60 register_rest_route(self::NAMESPACE, '/seo-analyzer', [
61 'methods' => 'GET',
62 'callback' => [$this, 'get_analysis'],
63 'permission_callback' => [$this, 'check_permissions'],
64 ]);
65
66 register_rest_route(self::NAMESPACE, '/seo-analyzer/run', [
67 'methods' => 'POST',
68 'callback' => [$this, 'run_analysis'],
69 'permission_callback' => [$this, 'check_permissions'],
70 ]);
71
72 register_rest_route(self::NAMESPACE, '/seo-analyzer/fix', [
73 'methods' => 'POST',
74 'callback' => [$this, 'apply_fix'],
75 'permission_callback' => [$this, 'check_permissions'],
76 'args' => [
77 'check_id' => [
78 'required' => true,
79 'type' => 'string',
80 'sanitize_callback' => 'sanitize_key',
81 ],
82 ],
83 ]);
84 }
85
86 /**
87 * Apply a one-click fix, then re-run the analysis so the caller gets the
88 * updated score in the same round trip.
89 *
90 * Re-running is the point: a fix the user cannot see land is
91 * indistinguishable from one that silently failed.
92 *
93 * @since 1.28.0
94 *
95 * @param WP_REST_Request $request Request object.
96 * @return WP_REST_Response
97 */
98 public function apply_fix(WP_REST_Request $request): WP_REST_Response {
99 $check_id = (string) $request->get_param('check_id');
100
101 try {
102 $outcome = (new \ThinkRank\SEO\SEO_Analyzer_Fixer())->fix($check_id);
103 } catch (\Throwable $e) {
104 return new WP_REST_Response([
105 'success' => false,
106 'message' => $e->getMessage(),
107 ], 400);
108 }
109
110 return new WP_REST_Response([
111 'success' => true,
112 'message' => $outcome['message'],
113 'data' => [
114 'check_id' => $check_id,
115 'fixed' => (bool) $outcome['fixed'],
116 'result' => $outcome['data'],
117 'analysis' => $this->analyzer->run(true),
118 ],
119 ], 200);
120 }
121
122 /**
123 * Return the last cached analysis (computing it if none is cached).
124 *
125 * @param WP_REST_Request $request Request object.
126 * @return WP_REST_Response
127 */
128 public function get_analysis(WP_REST_Request $request): WP_REST_Response {
129 return new WP_REST_Response([
130 'success' => true,
131 'data' => $this->analyzer->run(false),
132 ], 200);
133 }
134
135 /**
136 * Force a fresh analysis, busting the cache.
137 *
138 * @param WP_REST_Request $request Request object.
139 * @return WP_REST_Response
140 */
141 public function run_analysis(WP_REST_Request $request): WP_REST_Response {
142 return new WP_REST_Response([
143 'success' => true,
144 'data' => $this->analyzer->run(true),
145 ], 200);
146 }
147
148 /**
149 * Only administrators may run the site-wide analyzer.
150 *
151 * @param WP_REST_Request $request Request object.
152 * @return bool|\WP_Error
153 */
154 public function check_permissions(WP_REST_Request $request) {
155 if (!is_user_logged_in()) {
156 return new \WP_Error(
157 'rest_forbidden',
158 __('You must be logged in to access this endpoint.', 'thinkrank'),
159 ['status' => 401]
160 );
161 }
162
163 if (!current_user_can('manage_options')) {
164 return new \WP_Error(
165 'rest_forbidden',
166 __('You do not have permission to run the site SEO analyzer.', 'thinkrank'),
167 ['status' => 403]
168 );
169 }
170
171 return true;
172 }
173 }
174