Completed
Push — wip/steps ( 06abc1...278315 )
by Romain
02:29
created

StepMiddlewareService::getNextStep()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
/*
3
 * 2017 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 FormZ project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\Formz\Middleware\Item\Step\Service;
15
16
use Romm\Formz\Condition\Processor\ConditionProcessorFactory;
17
use Romm\Formz\Condition\Processor\DataObject\PhpConditionDataObject;
18
use Romm\Formz\Exceptions\InvalidArgumentTypeException;
19
use Romm\Formz\Form\Definition\Step\Step\Step;
20
use Romm\Formz\Form\Definition\Step\Step\StepDefinition;
21
use Romm\Formz\Form\FormObject\FormObject;
22
use Romm\Formz\Form\FormObject\FormObjectFactory;
23
use Romm\Formz\Form\FormObject\Service\Step\FormStepPersistence;
24
use Romm\Formz\Middleware\Request\Redirect;
25
use Romm\Formz\Service\Traits\SelfInstantiateTrait;
26
use Romm\Formz\Validation\Validator\Form\DataObject\FormValidatorDataObject;
27
use Romm\Formz\Validation\Validator\Form\FormValidatorExecutor;
28
use TYPO3\CMS\Core\SingletonInterface;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Extbase\Mvc\Web\Request;
31
32
/**
33
 * This service allows extended form steps manipulation.
34
 */
35
class StepMiddlewareService implements SingletonInterface
36
{
37
    use SelfInstantiateTrait;
38
39
    /**
40
     * @var FormObject
41
     */
42
    protected $formObject;
43
44
    /**
45
     * @var Request
46
     */
47
    protected $request;
48
49
    /**
50
     * @var StepMiddlewareValidationService
51
     */
52
    protected $validationService;
53
54
    /**
55
     * @var FormStepPersistence
56
     */
57
    protected $persistence;
58
59
    /**
60
     * @param FormObject $formObject
61
     * @param Request $request
62
     */
63
    public function reset(FormObject $formObject, Request $request)
64
    {
65
        $this->formObject = $formObject;
66
        $this->request = $request;
67
68
        $this->persistence = FormObjectFactory::get()->getStepService($formObject)->getStepPersistence();
69
70
        $this->validationService = GeneralUtility::makeInstance(StepMiddlewareValidationService::class, $this);
71
    }
72
73
    /**
74
     * @param Step $currentStep
75
     * @return StepDefinition|null
76
     */
77
    public function getNextStep(Step $currentStep)
78
    {
79
        /*
80
         * The form was submitted, and no error was found, we can safely
81
         * dispatch the request to the next step.
82
         */
83
        $currentStepDefinition = $this->getStepDefinition($currentStep);
84
85
        // Saving submitted form data for further usage.
86
        $this->markStepAsValidated($currentStepDefinition, $this->getFormRawValues());
0 ignored issues
show
Bug introduced by
It seems like $currentStepDefinition defined by $this->getStepDefinition($currentStep) on line 83 can be null; however, Romm\Formz\Middleware\It...::markStepAsValidated() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
87
        $this->addValidatedFields($this->formObject->getFormResult()->getValidatedFields());
88
89
        $nextStep = null;
90
91
        if ($currentStepDefinition->hasNextStep()) {
92
            $nextStep = $this->getNextStepDefinition($currentStepDefinition, true);
0 ignored issues
show
Bug introduced by
It seems like $currentStepDefinition defined by $this->getStepDefinition($currentStep) on line 83 can be null; however, Romm\Formz\Middleware\It...getNextStepDefinition() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
93
        }
94
95
        return $nextStep;
96
    }
97
98
    /**
99
     * Fetches the raw values sent in the request.
100
     *
101
     * @return array
102
     * @throws InvalidArgumentTypeException
103
     */
104
    protected function getFormRawValues()
105
    {
106
        $formName = $this->getFormObject()->getName();
107
        $formArray = null;
0 ignored issues
show
Unused Code introduced by
$formArray is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
108
109
        if ($this->request->hasArgument($formName)) {
110
            /** @var array $formArray */
111
            $formArray = $this->request->getArgument($formName);
112
113
            if (false === is_array($formArray)) {
114
                throw InvalidArgumentTypeException::formArgumentNotArray($this->getFormObject(), $formArray);
115
            }
116
        } else {
117
            $formArray = [];
118
        }
119
120
        return $formArray;
121
    }
122
123
    /**
124
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::markStepAsValidated
125
     *
126
     * @param StepDefinition $stepDefinition
127
     * @param array          $formValues
128
     */
129
    public function markStepAsValidated(StepDefinition $stepDefinition, array $formValues)
130
    {
131
        $this->validationService->markStepAsValidated($stepDefinition, $formValues);
132
    }
133
134
    /**
135
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::addValidatedFields
136
     *
137
     * @param array $validatedFields
138
     */
139
    public function addValidatedFields(array $validatedFields)
140
    {
141
        $this->validationService->addValidatedFields($validatedFields);
142
    }
143
144
    /**
145
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::stepDefinitionIsValid
146
     *
147
     * @param StepDefinition $stepDefinition
148
     * @return bool
149
     */
150
    public function stepIsValid(StepDefinition $stepDefinition)
151
    {
152
        return $this->validationService->stepDefinitionIsValid($stepDefinition);
153
    }
154
155
    /**
156
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::getFirstInvalidStep
157
     *
158
     * @param Step $step
159
     * @return StepDefinition|null
160
     */
161
    public function getFirstInvalidStep(Step $step)
162
    {
163
        return $this->validationService->getFirstInvalidStep($step);
164
    }
165
166
    /**
167
     * @param StepDefinition $step
168
     * @param bool           $removeConditionalSteps
169
     * @return StepDefinition
170
     */
171
    public function getNextStepDefinition(StepDefinition $step, $removeConditionalSteps = false)
172
    {
173
        $nextStep = null;
174
175
        if ($step->hasDivergence()) {
176
            $divergenceSteps = $step->getDivergenceSteps();
177
178
            foreach ($divergenceSteps as $divergenceStep) {
179
                if (true === $this->getStepDefinitionConditionResult($divergenceStep)) {
180
                    $nextStep = $divergenceStep;
181
                    break;
182
                }
183
            }
184
        }
185
186
        if (null === $nextStep) {
187
            while ($step->hasNextStep()) {
188
                $step = $step->getNextStep();
189
190
                if ($step->hasActivation()) {
191
                    if (true === $this->getStepDefinitionConditionResult($step)) {
192
                        $nextStep = $step;
193
                        break;
194
                    } elseif (true === $removeConditionalSteps) {
195
                        $this->persistence->removeStep($step);
196
                    }
197
                } else {
198
                    $nextStep = $step;
199
                    break;
200
                }
201
            }
202
        }
203
204
        return $nextStep;
205
    }
206
207
    /**
208
     * @param StepDefinition $stepDefinition
209
     * @param Redirect       $redirect
210
     */
211
    public function moveForwardToStep(StepDefinition $stepDefinition, Redirect $redirect)
212
    {
213
        $this->persistence->setStepLevel($stepDefinition);
214
        $this->redirectToStep($stepDefinition->getStep(), $redirect);
215
    }
216
217
    /**
218
     * Redirects the current request to the given step.
219
     *
220
     * @param Step     $step
221
     * @param Redirect $redirect
222
     */
223
    public function redirectToStep(Step $step, Redirect $redirect)
224
    {
225
        $redirect->toPage($step->getPageUid())
226
            ->toExtension($step->getExtension())
227
            ->toController($step->getController())
228
            ->toAction($step->getAction())
229
            ->withArguments([
230
                'fz-hash' => [
231
                    $this->formObject->getName() => $this->formObject->getFormHash()
232
                ]
233
            ])
234
            ->dispatch();
235
    }
236
237
    /**
238
     * @param StepDefinition $stepDefinition
239
     * @return bool
240
     */
241
    public function getStepDefinitionConditionResult(StepDefinition $stepDefinition)
242
    {
243
        $conditionProcessor = ConditionProcessorFactory::getInstance()->get($this->getFormObject());
244
        $tree = $conditionProcessor->getActivationConditionTreeForStep($stepDefinition);
245
        $todo = new FormValidatorExecutor($this->getFormObject(), new FormValidatorDataObject()); // @todo
246
        $dataObject = new PhpConditionDataObject($this->getFormObject()->getForm(), $todo);
247
248
        return $tree->getPhpResult($dataObject);
249
    }
250
251
    /**
252
     * @param Step $step
253
     * @return StepDefinition|null
254
     */
255
    public function getStepDefinition(Step $step)
256
    {
257
        return $this->findStepDefinition($step, $this->getFirstStepDefinition());
258
    }
259
260
    /**
261
     * @param Step           $step
262
     * @param StepDefinition $stepDefinition
263
     * @return StepDefinition|null
264
     */
265
    protected function findStepDefinition(Step $step, StepDefinition $stepDefinition)
266
    {
267
        if ($stepDefinition->getStep() === $step) {
268
            return $stepDefinition;
269
        }
270
271
        if ($stepDefinition->hasNextStep()) {
272
            $result = $this->findStepDefinition($step, $stepDefinition->getNextStep());
273
274
            if ($result instanceof StepDefinition) {
275
                return $result;
276
            }
277
        }
278
279
        if ($stepDefinition->hasDivergence()) {
280
            foreach ($stepDefinition->getDivergenceSteps() as $divergenceStep) {
281
                $result = $this->findStepDefinition($step, $divergenceStep);
282
283
                if ($result instanceof StepDefinition) {
284
                    return $result;
285
                }
286
            }
287
        }
288
289
        return null;
290
    }
291
292
    /**
293
     * @return FormStepPersistence
294
     */
295
    public function getStepPersistence()
296
    {
297
        return $this->persistence;
298
    }
299
300
    /**
301
     * @return FormObject
302
     */
303
    public function getFormObject()
304
    {
305
        return $this->formObject;
306
    }
307
308
    /**
309
     * @return StepDefinition
310
     */
311
    public function getFirstStepDefinition()
312
    {
313
        return $this->formObject->getDefinition()->getSteps()->getFirstStepDefinition();
314
    }
315
}
316