PluginProbe ʕ •ᴥ•ʔ
FAPI Member / 2.2.30
FAPI Member v2.2.30
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / src / Api / V2 / ApiController.php
fapi-member / src / Api / V2 Last commit date
Endpoints 2 months ago ApiController.php 2 months ago
ApiController.php
282 lines
1 <?php declare(strict_types=1);
2
3 namespace FapiMember\Api\V2;
4
5 use FapiMember\FapiMemberPlugin;
6 use FapiMember\Library\SmartEmailing\Types\Arrays;
7 use FapiMember\Library\SmartEmailing\Types\IntType;
8 use FapiMember\Library\SmartEmailing\Types\StringType;
9 use FapiMember\Model\Enums\Alert;
10 use FapiMember\Model\Enums\Keys\OptionKey;
11 use FapiMember\Model\Enums\Types\RequestMethodType;
12 use FapiMember\Utils\AlertProvider;
13 use Throwable;
14 use WP_REST_Request;
15
16 class ApiController
17 {
18 private array $freeAccessEndpoints = [
19 'memberships' => ['unlockLevelForLoggedInUser'],
20 ];
21
22 public function handleRequest(WP_REST_Request $request): void
23 {
24 $params = $request->get_query_params();
25 $controllerName = trim(str_replace('/fapi/v2/', '', $params['rest_route']), '/');
26 $route = 'FapiMember\\Api\\V2\\Endpoints\\' . ucfirst($controllerName) . 'Controller';
27
28 if (isset($params['action'])) {
29 $action = $params['action'];
30 } elseif (isset($params['id'])) {
31 $action = 'get';
32 } else {
33 $action = 'list';
34 }
35
36 $actionFunction = 'handle' . ucfirst($action);
37
38 $this->authenticate($controllerName, $action, $request);
39
40 try {
41 $controller = new $route();
42 } catch (Throwable) {
43 $this->callbackError([
44 'class'=> self::class,
45 'description' => "Specified endpoint doesn't exist.",
46 ]);
47 }
48
49 if (!is_callable([$controller, $action])) {
50 $this->callbackError([
51 'class'=> $controller::class,
52 'description' => "Specified action doesn't exist. Action: " . $action,
53 ]);
54 }
55
56
57 if (is_callable([$this, $actionFunction])) {
58 $data = $this->$actionFunction($request, $controller, $action);
59 } else {
60 $data = $controller->$action($request);
61 }
62
63 wp_send_json($data);
64 }
65
66 private function handleList(WP_REST_Request $request, mixed $controller, string $action): mixed
67 {
68 if ($request->get_method() !== RequestMethodType::GET) {
69 $this->wrongMethodError(RequestMethodType::GET);
70 }
71
72 return $controller->$action();
73 }
74
75
76 private function handleGet(WP_REST_Request $request, mixed $controller, string $action): mixed
77 {
78 if ($request->get_method() !== RequestMethodType::GET) {
79 $this->wrongMethodError(RequestMethodType::GET);
80 }
81
82 $params = $request->get_params();
83
84 if (!isset($params['id'])) {
85 $this->missingParameterError('id');
86 }
87
88 try {
89 $id = IntType::extract($params, 'id');
90 } catch (Throwable) {
91 $this->invalidParameterError('id');
92 }
93
94 return $controller->$action($id);
95 }
96
97 private function handleDelete(WP_REST_Request $request, mixed $controller, string $action): bool
98 {
99 if ($request->get_method() !== RequestMethodType::POST) {
100 $this->wrongMethodError(RequestMethodType::POST);
101 }
102
103 $body = json_decode($request->get_body(), true);
104
105 $id = $this->extractParam($body, 'id', IntType::class);
106
107 return $controller->$action($id);
108 }
109
110 private function handleCreate(WP_REST_Request $request, mixed $controller, string $action): bool
111 {
112 if ($request->get_method() !== RequestMethodType::POST) {
113 $this->wrongMethodError(RequestMethodType::POST);
114 }
115
116 $body = json_decode($request->get_body(), true);
117
118 return $controller->$action($body);
119 }
120
121 private function handleUpdate(WP_REST_Request $request, mixed $controller, string $action): bool
122 {
123 if ($request->get_method() !== RequestMethodType::POST) {
124 $this->wrongMethodError(RequestMethodType::POST);
125 }
126
127 $body = json_decode($request->get_body(), true);
128
129 $id = $this->extractParam($body, 'id', IntType::class);
130 $data = $this->extractParam($body, 'data', Arrays::class);
131
132 return $controller->$action($id, $data);
133 }
134
135
136 public function extractParam(array $array, string $key, string $type): mixed
137 {
138 if (!isset($array[$key])) {
139 $this->missingParameterError($key);
140 }
141
142
143 $param = $this->extractParamOrNull($array, $key, $type);
144
145 if ($param === null) {
146 $this->invalidParameterError($key);
147
148 } else {
149 return $param;
150 }
151 }
152
153 public function extractParamOrNull(array $array, string $key, string $type): mixed
154 {
155 if (!isset($array[$key])) {
156 return null;
157 }
158
159 try {
160 return $type::extractOrNull($array, $key);
161 } catch (Throwable) {
162 return null;
163 }
164 }
165
166 public function callbackSettingsSaved(mixed $data = []): never
167 {
168 $this->callbackResponse($data, Alert::SETTINGS_SAVED);
169 }
170
171 public function callbackError(array $data, string $alert = Alert::INTERNAL_ERROR): never
172 {
173 $data[FapiMemberPlugin::FAPI_MEMBER_PLUGIN_VERSION_KEY ] = FAPI_MEMBER_PLUGIN_VERSION;
174 $data['alert'] = AlertProvider::getError($alert);
175
176 wp_send_json_error(
177 $data,
178 400,
179 );
180
181 die;
182 }
183
184 /**
185 * @param array $data
186 * @return never
187 */
188 public function callbackSuccess($data = []): never
189 {
190 wp_send_json_success(
191 $data,
192 200,
193 );
194
195 die;
196 }
197
198 public function callbackResponse(array $data, string $alert): never
199 {
200 $data[FapiMemberPlugin::FAPI_MEMBER_PLUGIN_VERSION_KEY ] = FAPI_MEMBER_PLUGIN_VERSION;
201 $data['alert'] = AlertProvider::getError($alert);
202
203 wp_send_json_success(
204 $data,
205 200,
206 );
207
208 die;
209 }
210
211
212 public function wrongMethodError(string $method): never
213 {
214 $this->callbackError([
215 'class'=> ApiController::class,
216 'description' => "wrong request method. Expecting: " . $method,
217 ]);
218 }
219
220 public function missingParameterError(string $parameter): never
221 {
222 $this->callbackError([
223 'class'=> self::class,
224 'description' => "Missing parameter '" . $parameter . "'",
225 ]);
226 }
227
228 public function invalidParameterError(string $parameter): never
229 {
230 $this->callbackError([
231 'class'=> self::class,
232 'description' => "Invalid parameter '" . $parameter . "'",
233 ]);
234 }
235
236 public function checkRequestMethod(\WP_REST_Request $request, string $method): void
237 {
238 if ($request->get_method() !== $method) {
239 $this->wrongMethodError($method);
240 }
241 }
242
243 private function authenticate(array|string $controllerName, mixed $action, WP_REST_Request $request): void
244 {
245 if (
246 isset($this->freeAccessEndpoints[$controllerName]) &&
247 in_array($action, $this->freeAccessEndpoints[$controllerName])
248 ) {
249 return;
250 }
251
252 try {
253 $body = json_decode($request->get_body(), true) ?? [];
254 } catch (Throwable) {
255 $body = [];
256 }
257
258 $token = $this->extractParamOrNull($body, 'token', StringType::class);
259
260 if ($token !== null) {
261 if ($token !== get_option(OptionKey::TOKEN, null)) {
262 $this->callbackError([
263 'class' => self::class,
264 'description' => "Permission denied. Invalid token provided.",
265 ]);
266 }
267
268 return;
269 }
270
271 $nonce = $request->get_header('X-WP-Nonce');
272
273 if (!wp_verify_nonce($nonce, 'wp_rest')) {
274 $this->callbackError([
275 'class' => self::class,
276 'description' => "Permission denied. Invalid Nonce provided.",
277 ]);
278 }
279 }
280
281 }
282