Total Complexity | 57 |
Total Lines | 443 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like AbstractForm often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractForm, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
37 | abstract class AbstractForm implements FormInterface |
||
38 | { |
||
39 | use AccessesSimpleCacheTrait; |
||
40 | use UtilizesExtensionTrait; |
||
41 | use FluentlySetsPropertiesTrait; |
||
42 | use TranslatesTextTrait; |
||
43 | |||
44 | protected string $handle; |
||
45 | |||
46 | protected string $action; |
||
47 | |||
48 | /** |
||
49 | * Fields being handled for the current request |
||
50 | * |
||
51 | * @var array<string> |
||
52 | */ |
||
53 | protected array $processing; |
||
54 | |||
55 | protected WpExtensionInterface $extension; |
||
56 | |||
57 | public function __construct(WpExtensionInterface $extension) |
||
58 | { |
||
59 | $this->extension = $extension; |
||
60 | $this->init('construct'); |
||
61 | } |
||
62 | |||
63 | public function getHandle(): string |
||
64 | { |
||
65 | return $this->handle; |
||
66 | } |
||
67 | |||
68 | public function getAction(): string |
||
69 | { |
||
70 | return $this->action; |
||
71 | } |
||
72 | |||
73 | public function build(ServerRequestInterface $request): array |
||
74 | { |
||
75 | $this->init('build'); |
||
76 | $this->processing = $this->fields($request); |
||
77 | |||
78 | $data = [ |
||
79 | 'method' => $this->formMethod(), |
||
80 | 'action' => $this->formAction($request), |
||
81 | 'checks' => $this->formChecks($request), |
||
82 | 'fields' => $this->formFields($request), |
||
83 | 'errors' => $this->formErrors($request), |
||
84 | ]; |
||
85 | |||
86 | $this->resetStateProps(); |
||
87 | |||
88 | return $data; |
||
89 | } |
||
90 | |||
91 | public function process(ServerRequestInterface $request): void |
||
92 | { |
||
93 | $this->init('process'); |
||
94 | $this->processing = $this->fields($request); |
||
95 | |||
96 | $processed = $this->postProcess( |
||
97 | $this->processor($request)->process($request), |
||
98 | $request |
||
99 | ); |
||
100 | |||
101 | $this->redirect($this->redirectTo($request, $processed)); |
||
|
|||
102 | } |
||
103 | |||
104 | public function validate(ServerRequestInterface $request, string $field, $value): ValidationReportInterface |
||
105 | { |
||
106 | $validators = $this->validation($request); |
||
107 | |||
108 | return $validators[$field]->inspect($value); |
||
109 | } |
||
110 | |||
111 | protected function getExtension(): WpExtensionInterface |
||
112 | { |
||
113 | return $this->extension; |
||
114 | } |
||
115 | |||
116 | protected function processor(ServerRequestInterface $request): FormSubmissionManagerInterface |
||
117 | { |
||
118 | return new FormSubmissionManager( |
||
119 | $this->fieldControllers($request), |
||
120 | $this->processes($request), |
||
121 | $this->shield($request), |
||
122 | ); |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * @return array<FormFieldControllerInterface> |
||
127 | */ |
||
128 | protected function fieldControllers(ServerRequestInterface $request): array |
||
129 | { |
||
130 | $data = $this->dataManagers($request); |
||
131 | $validation = $this->validation($request); |
||
132 | $formatting = $this->formatting($request); |
||
133 | |||
134 | $controllers = []; |
||
135 | |||
136 | foreach ($this->processing as $field) { |
||
137 | $name = $this->fieldKeyAsName($field); |
||
138 | |||
139 | $controllers[] = FormFieldControllerBuilder::for($name) |
||
140 | ->dataManager($data[$field] ?? null) |
||
141 | ->formatter($formatting[$field] ?? null) |
||
142 | ->validator($validation[$field] ?? null) |
||
143 | ->get(); |
||
144 | } |
||
145 | |||
146 | return $controllers; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @return array<string|array|FormFieldInterface> |
||
151 | */ |
||
152 | protected function formFields(ServerRequestInterface $request): array |
||
153 | { |
||
154 | $attributes = $this->attributes($request); |
||
155 | $data = $this->dataManagers($request); |
||
156 | $formatting = $this->formatting($request); |
||
157 | |||
158 | $controllers = []; |
||
159 | |||
160 | foreach ($this->processing as $field) { |
||
161 | $name = $this->fieldKeyAsName($field); |
||
162 | |||
163 | $controller = FormFieldControllerBuilder::for($name) |
||
164 | ->dataManager($data[$field] ?? null) |
||
165 | ->formatter($formatting[$field] ?? null) |
||
166 | ->get(); |
||
167 | |||
168 | $definition = $attributes[$field] ?? []; |
||
169 | |||
170 | $definition['name'] = $name; |
||
171 | $definition['value'] = $controller->getPresetValue($request); |
||
172 | |||
173 | $controllers[$field] = $definition; |
||
174 | } |
||
175 | |||
176 | return $controllers; |
||
177 | } |
||
178 | |||
179 | /** |
||
180 | * @return array<string,array<int,string>> |
||
181 | */ |
||
182 | protected function formErrors(ServerRequestInterface $request): array |
||
183 | { |
||
184 | $errors = []; |
||
185 | $messages = $this->errorMessages($request); |
||
186 | $violations = $this->getCachedViolations($request); |
||
187 | |||
188 | foreach ($violations as $field => $violations) { |
||
189 | $field = $this->fieldNameAsKey($field); |
||
190 | $errors[$field] = []; |
||
191 | |||
192 | foreach ($violations as $violation) { |
||
193 | $message = $messages[$field][$violation]; |
||
194 | $errors[$field][$violation] = $this->translate($message); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | return $errors; |
||
199 | } |
||
200 | |||
201 | protected function getCachedViolations(ServerRequestInterface $request): array |
||
202 | { |
||
203 | $cache = $this->simpleCache(); |
||
204 | $key = $this->failedValidationCacheKey(); |
||
205 | |||
206 | $violations = $cache->get($key, []); |
||
207 | |||
208 | $cache->delete($key); |
||
209 | |||
210 | return $violations; |
||
211 | } |
||
212 | |||
213 | protected function fieldKeyAsName(string $key): string |
||
214 | { |
||
215 | return $this->prefix(str_replace('_', '-', $key), '-'); |
||
216 | } |
||
217 | |||
218 | protected function fieldKeysAsNames(array $keys): array |
||
219 | { |
||
220 | return array_map([$this, 'fieldKeyAsName'], $keys); |
||
221 | } |
||
222 | |||
223 | protected function fieldNameAsKey(string $name): string |
||
224 | { |
||
225 | $stripped = substr($name, strlen($this->extension->getPrefix()) + 1); |
||
226 | |||
227 | return str_replace('-', '_', $stripped); |
||
228 | } |
||
229 | |||
230 | protected function fieldNamesAsKeys(array $names): array |
||
231 | { |
||
232 | return array_map([$this, 'fieldNameAsKey'], $names); |
||
233 | } |
||
234 | |||
235 | protected function mappedResults(array $results): array |
||
236 | { |
||
237 | $mapped = []; |
||
238 | |||
239 | foreach ($results as $name => $value) { |
||
240 | $mapped[$this->fieldNameAsKey($name)] = $value; |
||
241 | } |
||
242 | |||
243 | return $mapped; |
||
244 | } |
||
245 | |||
246 | protected function formMethod(): string |
||
247 | { |
||
248 | return 'post'; |
||
249 | } |
||
250 | |||
251 | protected function formAction(ServerRequestInterface $request): string |
||
252 | { |
||
253 | return esc_url(admin_url('admin-post.php')); |
||
254 | } |
||
255 | |||
256 | protected function formChecks(ServerRequestInterface $request): string |
||
257 | { |
||
258 | return implode("\n", $this->checks($request)); |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * @return array<string> |
||
263 | */ |
||
264 | protected function checks(ServerRequestInterface $request): array |
||
265 | { |
||
266 | return [ |
||
267 | $this->actionFormCheck(), |
||
268 | $this->refererFormCheck(), |
||
269 | $this->csrfTokenFormCheck(), |
||
270 | ]; |
||
271 | } |
||
272 | |||
273 | protected function actionFormCheck(): string |
||
274 | { |
||
275 | return (new Hidden()) |
||
276 | ->setName('action') |
||
277 | ->setValue($this->action) |
||
278 | ->toHtml(); |
||
279 | } |
||
280 | |||
281 | protected function refererFormCheck(): string |
||
282 | { |
||
283 | return wp_referer_field(false); |
||
284 | } |
||
285 | |||
286 | protected function csrfTokenFormCheck(): string |
||
287 | { |
||
288 | return $this->csrfPrinter()->print($this->token()); |
||
289 | } |
||
290 | |||
291 | protected function shield(ServerRequestInterface $request): FormShieldInterface |
||
292 | { |
||
293 | return new FormShield($this->policies($request)); |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * @return array<string,ServerRequestPolicyInterface> |
||
298 | */ |
||
299 | protected function policies(ServerRequestInterface $request): array |
||
303 | ]; |
||
304 | } |
||
305 | |||
306 | protected function csrfAuthenticator(): ServerRequestPolicyInterface |
||
307 | { |
||
308 | return new CsrfCheck($this->token()); |
||
309 | } |
||
310 | |||
311 | protected function token(): CsrfManagerInterface |
||
312 | { |
||
313 | return new Nonce($action = $this->getAction(), $action, Nonce::EXP_12); |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * @return array<FormDataProcessorInterface> |
||
318 | */ |
||
319 | protected function processes(ServerRequestInterface $request): array |
||
320 | { |
||
321 | return [ |
||
322 | $this->failedValidationCache(), |
||
323 | $this->transientFieldCacheCleaner($request), |
||
324 | ]; |
||
325 | } |
||
326 | |||
327 | protected function failedValidationCache(): FormDataProcessorInterface |
||
328 | { |
||
329 | return new FailedValidationSimpleCache( |
||
330 | 'cached_violations', |
||
331 | $this->simpleCache(), |
||
332 | $this->failedValidationCacheKey() |
||
333 | ); |
||
334 | } |
||
335 | |||
336 | protected function transientFieldCacheCleaner(ServerRequestInterface $request): FormDataProcessorInterface |
||
337 | { |
||
338 | return new SimpleCacheSweeper( |
||
339 | 'transient_fields', |
||
340 | $this->simpleCache(), |
||
341 | array_map([$this, 'transientFieldCacheKey'], $this->processing) |
||
342 | ); |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * @return array<string,null|FieldDataManagerInterface> |
||
347 | */ |
||
348 | protected function dataManagers(ServerRequestInterface $request): array |
||
349 | { |
||
350 | $managers = []; |
||
351 | |||
352 | foreach ($this->processing as $key) { |
||
353 | $managers[$key] = $this->transientDataManager($key); |
||
354 | } |
||
355 | |||
356 | return $managers; |
||
357 | } |
||
358 | |||
359 | protected function transientDataManager(string $key): FieldDataManagerInterface |
||
360 | { |
||
361 | return new SimpleCacheDataManager( |
||
362 | $this->getSimpleCache(), |
||
363 | $this->transientFieldCacheKey($key) |
||
364 | ); |
||
365 | } |
||
366 | |||
367 | protected function redirect(?string $location = null): void |
||
368 | { |
||
369 | wp_safe_redirect($location ?? $this->redirectDefault()); |
||
370 | |||
371 | exit; |
||
372 | } |
||
373 | |||
374 | protected function redirectDefault(): string |
||
375 | { |
||
376 | return wp_get_referer() ?: home_url(); |
||
377 | } |
||
378 | |||
379 | protected function simpleCache(): CacheInterface |
||
380 | { |
||
381 | return $this->getService('transients_channel'); |
||
382 | } |
||
383 | |||
384 | protected function csrfPrinter(): CsrfFieldPrinterInterface |
||
385 | { |
||
386 | return new CsrfFieldPrinter(); |
||
387 | } |
||
388 | |||
389 | protected function redirectTo(ServerRequestInterface $request, ProcessedFormReportInterface $report): ?string |
||
392 | } |
||
393 | |||
394 | protected function postProcess(ProcessedFormReportInterface $report, ServerRequestInterface $request): ProcessedFormReportInterface |
||
395 | { |
||
396 | return $report; |
||
397 | } |
||
398 | |||
399 | protected function resetStateProps(): void |
||
400 | { |
||
401 | unset($this->processing); |
||
402 | } |
||
403 | |||
404 | protected function initiationContexts(): array |
||
405 | { |
||
406 | return [ |
||
407 | 'construct' => $this->constructInitiationContext(), |
||
408 | 'build' => $this->buildInitiationContext(), |
||
409 | 'process' => $this->processInitiationContext(), |
||
410 | ]; |
||
411 | } |
||
412 | |||
413 | protected function cacheKey(string $sub): string |
||
414 | { |
||
415 | return "form:{$this->handle}:{$sub}"; |
||
416 | } |
||
417 | |||
418 | protected function failedValidationCacheKey(): string |
||
419 | { |
||
420 | return $this->cacheKey('errors'); |
||
421 | } |
||
422 | |||
423 | protected function transientFieldCacheKey(string $key): string |
||
424 | { |
||
425 | return $this->cacheKey("submitted.{$key}"); |
||
426 | } |
||
427 | |||
428 | protected function constructInitiationContext(): array |
||
429 | { |
||
430 | return ['handle', 'action']; |
||
431 | } |
||
432 | |||
433 | protected function buildInitiationContext(): array |
||
434 | { |
||
435 | return ['simpleCache', 'localizer']; |
||
436 | } |
||
437 | |||
438 | protected function processInitiationContext(): array |
||
441 | } |
||
442 | |||
443 | /** |
||
444 | * @return array<string,null|ValidatorInterface> |
||
445 | */ |
||
446 | protected function validation(ServerRequestInterface $request): array |
||
447 | { |
||
448 | return []; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * @return array<string,null|DataFormatterInterface> |
||
453 | */ |
||
454 | protected function formatting(ServerRequestInterface $request): array |
||
455 | { |
||
456 | return []; |
||
457 | } |
||
458 | |||
459 | protected function action(): string |
||
460 | { |
||
461 | return $this->prefix($this->getHandle(), '_'); |
||
462 | } |
||
463 | |||
464 | abstract protected function handle(): string; |
||
465 | |||
466 | /** |
||
467 | * @return array<string> |
||
468 | */ |
||
469 | abstract protected function fields(ServerRequestInterface $request): array; |
||
470 | |||
471 | /** |
||
472 | * @return array<string,array<string,mixed>|FormFieldInterface> |
||
473 | */ |
||
474 | abstract protected function attributes(ServerRequestInterface $request): array; |
||
475 | |||
476 | /** |
||
477 | * @return array<string,array<string,string>> |
||
478 | */ |
||
479 | abstract protected function errorMessages(ServerRequestInterface $request): array; |
||
480 | } |
||
481 |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.