FormSubmissionManager::runProcessors()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 2
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 3
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace WebTheory\Saveyour\Controller;
4
5
use Psr\Http\Message\ServerRequestInterface;
6
use WebTheory\Saveyour\Auth\HolyShield;
7
use WebTheory\Saveyour\Contracts\Auth\FormShieldInterface;
8
use WebTheory\Saveyour\Contracts\Controller\FormFieldControllerInterface;
9
use WebTheory\Saveyour\Contracts\Controller\FormSubmissionManagerInterface;
10
use WebTheory\Saveyour\Contracts\Processor\FormDataProcessorInterface;
11
use WebTheory\Saveyour\Contracts\Report\Builder\ProcessedFormReportBuilderInterface;
12
use WebTheory\Saveyour\Contracts\Report\FormShieldReportInterface;
13
use WebTheory\Saveyour\Contracts\Report\ProcessedFormReportInterface;
14
use WebTheory\Saveyour\Contracts\Report\ProcessedInputReportInterface;
15
use WebTheory\Saveyour\Http\Request;
16
use WebTheory\Saveyour\Report\Builder\ProcessedFormReportBuilder;
17
use WebTheory\Saveyour\Report\FormProcessReport;
18
use WebTheory\Saveyour\Report\ProcessedInputReport;
19
20
class FormSubmissionManager implements FormSubmissionManagerInterface
21
{
22
    /**
23
     * @var array<int,FormFieldControllerInterface>
24
     */
25
    protected array $fields = [];
26
27
    /**
28
     * @var array<int,FormDataProcessorInterface>
29
     */
30
    protected array $processors = [];
31
32
    protected FormShieldInterface $shield;
33
34
    protected ProcessedFormReportBuilderInterface $reportBuilder;
35
36
    /**
37
     * @param array<FormFieldControllerInterface> $fields
38
     * @param array<FormDataProcessorInterface> $processors
39
     */
40 72
    public function __construct(array $fields, array $processors = [], FormShieldInterface $shield = null)
41
    {
42 72
        $this->fields = $fields;
43 72
        $this->processors = $processors;
44 72
        $this->shield = $shield ?? new HolyShield();
45
    }
46
47 6
    public function verify(ServerRequestInterface $request): bool
48
    {
49 6
        return $this->shield->approvesRequest($request);
50
    }
51
52 6
    public function validate(ServerRequestInterface $request): bool
53
    {
54 6
        foreach ($this->fields as $field) {
55 6
            if (!$field->validate($request)) {
56 3
                return false;
57
            }
58
        }
59
60 3
        return true;
61
    }
62
63 9
    public function validated(ServerRequestInterface $request): array
64
    {
65 9
        return $this->mapValidated($request);
66
    }
67
68 3
    public function processed(ServerRequestInterface $request): array
69
    {
70 3
        return $this->mapValidated(
71
            $request,
72 3
            fn (FormFieldControllerInterface $field) => $field->getUpdatedValue($request)
73
        );
74
    }
75
76 45
    public function process(ServerRequestInterface $request): ProcessedFormReportInterface
77
    {
78 45
        $this->initReportBuilder();
79
80 45
        $report = $this->analyzeRequest($request);
81
82 45
        if (true === $report->verificationStatus()) {
83 39
            $inputReports = $this->processFields($request);
84 39
            $this->runProcessors($request, $inputReports);
85 39
            $this->processResults($request);
86
        }
87
88 45
        return $this->reportBuilder->withShieldReport($report)->build();
89
    }
90
91 12
    protected function mapValidated(ServerRequestInterface $request, callable $callback = null): array
92
    {
93 12
        $mapped = [];
94
95 12
        foreach ($this->fields as $field) {
96 12
            if (!$this->fieldPresentInRequest($field, $request)) {
97 3
                continue;
98
            }
99
100 9
            $name = $field->getRequestVar();
101 9
            $value = $this->getFieldInputValue($field, $request);
102
103 9
            if ($field->validate($value)) {
104 6
                $mapped[$name] = $callback ? $callback($field) : $value;
105
            }
106
        }
107
108 12
        return $mapped;
109
    }
110
111 51
    protected function fieldPresentInRequest(FormFieldControllerInterface $field, ServerRequestInterface $request): bool
112
    {
113 51
        return Request::has($request, $field->getRequestVar());
114
    }
115
116 45
    protected function getFieldInputValue(FormFieldControllerInterface $field, ServerRequestInterface $request): string
117
    {
118 45
        return Request::var($request, $field->getRequestVar());
0 ignored issues
show
Bug Best Practice introduced by
The expression return WebTheory\Saveyou...field->getRequestVar()) could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
119
    }
120
121 45
    protected function initReportBuilder()
122
    {
123 45
        $this->reportBuilder = new ProcessedFormReportBuilder();
124
    }
125
126 45
    protected function analyzeRequest(ServerRequestInterface $request): FormShieldReportInterface
127
    {
128 45
        return $this->shield->analyzeRequest($request);
129
    }
130
131 39
    protected function processFields(ServerRequestInterface $request): array
132
    {
133 39
        $this->sortFieldQueue();
134
135 39
        $fields = [];
136
137 39
        foreach ($this->fields as $field) {
138 39
            $name = $field->getRequestVar();
139 39
            $report = $this->processField($field, $request);
140
141 39
            $this->reportBuilder->withInputReport($name, $report);
142 39
            $fields[$name] = $report;
143
        }
144
145 39
        return $fields;
146
    }
147
148 39
    protected function processField(FormFieldControllerInterface $field, ServerRequestInterface $request): ProcessedInputReportInterface
149
    {
150 39
        if (!$this->fieldPresentInRequest($field, $request)) {
151 3
            return ProcessedInputReport::voided();
152
        }
153
154 36
        $input = $this->getFieldInputValue($field, $request);
155 36
        $validation = $field->inspect($input);
156
157 36
        if (false === $validation->validationStatus()) {
158 3
            return ProcessedInputReport::invalid($input, $validation->ruleViolations());
159
        }
160
161 33
        if (!$field->isPermittedToProcess()) {
162 3
            return ProcessedInputReport::unprocessed($input);
163
        }
164
165 30
        return ProcessedInputReport::processed($input, $field->process($request));
166
    }
167
168 39
    protected function sortFieldQueue()
169
    {
170 39
        usort(
171 39
            $this->fields,
172
            /**
173
             * Because usort will not compare values that it infers from
174
             * previous comparisons to be equal, 0 should never be returned. all
175
             * that matters is that dependent entries are positioned after their
176
             * dependencies.
177
             */
178
            fn (
179
                FormFieldControllerInterface $a,
180
                FormFieldControllerInterface $b
181 3
            ) => in_array($a->getRequestVar(), $b->mustAwait()) ? -1 : 1
182
        );
183
    }
184
185
    /**
186
     * @param ServerRequestInterface $request
187
     * @param array<string,ProcessedInputReportInterface> $fields
188
     *
189
     * @return $this
190
     */
191 39
    protected function runProcessors(ServerRequestInterface $request, array $fields): FormSubmissionManager
192
    {
193 39
        $all = array_keys($fields);
194
195 39
        foreach ($this->processors as $processor) {
196 24
            $results = [];
197
198 24
            foreach ($processor->getFields() ?? $all as $field) {
199 24
                $results[$field] = $fields[$field] ?? null;
200
            }
201
202 24
            $this->reportBuilder->withProcessReport(
203 24
                $processor->getName(),
204 24
                $processor->process($request, $results) ?? new FormProcessReport()
205
            );
206
        }
207
208 39
        return $this;
209
    }
210
211
    protected function processResults(ServerRequestInterface $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

211
    protected function processResults(/** @scrutinizer ignore-unused */ ServerRequestInterface $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
212
    {
213
        //
214
    }
215
}
216