Completed
Push — middleware-wip ( 17e892...bf36ad )
by Romain
02:41
created

FormController::invokeMiddlewares()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 12
nc 3
nop 0
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\Controller;
15
16
use Romm\Formz\Core\Core;
17
use Romm\Formz\Form\FormInterface;
18
use Romm\Formz\Form\FormObject;
19
use Romm\Formz\Form\FormObjectFactory;
20
use Romm\Formz\Middleware\Request\Exception\ForwardException;
21
use Romm\Formz\Middleware\Request\Exception\RedirectException;
22
use Romm\Formz\Middleware\Request\Exception\StopPropagationException;
23
use Romm\Formz\Middleware\State\MiddlewareState;
24
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
25
use TYPO3\CMS\Extbase\Mvc\Controller\Argument;
26
use TYPO3\CMS\Extbase\Security\Cryptography\HashService;
27
28
class FormController extends ActionController
29
{
30
    /**
31
     * @var FormObject[]
32
     */
33
    protected $formArguments = [];
34
35
    /**
36
     * @var ControllerState
37
     */
38
    protected $state;
39
40
    /**
41
     * @var HashService
42
     */
43
    protected $hashService;
44
45
    /**
46
     * @var FormObjectFactory
47
     */
48
    protected $formObjectFactory;
49
50
    /**
51
     * Main action used to dispatch the request properly, depending on FormZ
52
     * configuration.
53
     *
54
     * The request is based on the previously called controller action, and is
55
     * used to list which forms are handled (every argument of the action method
56
     * that implements the interface `FormInterface`).
57
     *
58
     * Middlewares will be called for each form argument, and may modify the
59
     * request, which is then dispatched again with modified data.
60
     */
61
    public function processFormAction()
62
    {
63
        $tt = new \TYPO3\CMS\Core\TimeTracker\TimeTracker();
64
        $tt->start();
65
        $originalRequest = clone $this->state->getRequest();
66
67
        try {
68
            $this->invokeMiddlewares();
69
            $this->manageRequestResult();
70
        } catch (StopPropagationException $exception) {
71
            if ($exception instanceof RedirectException) {
72
                $this->redirectFromException($exception);
73
            } elseif (false === $exception instanceof ForwardException) {
74
                $this->forwardToReferrer();
75
            }
76
        }
77
78
        $this->state->setDispatched(true);
79
80
        $request = $this->state->getRequest();
81
82
        $this->request->setControllerVendorName($this->state->getVendorName());
83
        $this->request->setOriginalRequest($originalRequest);
84
85
        $this->forward(
86
            $request->getControllerActionName(),
87
            $request->getControllerName(),
88
            $request->getControllerExtensionName(),
89
            $request->getArguments()
90
        );
91
    }
92
93
    /**
94
     * @param FormObject $formObject
95
     */
96
    public function formObjectErrorAction(FormObject $formObject)
97
    {
98
        $this->view->assign('formObject', $formObject);
99
    }
100
101
    /**
102
     * Will fetch every form argument for this request, and dispatch every
103
     * middleware that was registered in its TypoScript configuration.
104
     */
105
    protected function invokeMiddlewares()
106
    {
107
        foreach ($this->getRequestForms() as $formObject) {
108
            $formObject->reset();
109
110
            /*
111
             * If the configuration contains errors, then the middlewares
112
             * configuration may be corrupted as well, so the risk to dispatch
113
             * middlewares can not be taken.
114
             */
115
            if ($formObject->getConfigurationValidationResult()->hasErrors()) {
116
                $this->forward(
117
                    'formObjectError',
118
                    null,
119
                    null,
120
                    ['formObject' => $formObject]
121
                );
122
            } else {
123
                /** @var MiddlewareState $middlewareState */
124
                $middlewareState = Core::instantiate(MiddlewareState::class, $formObject, $this->state);
125
126
                $middlewareState->run();
127
            }
128
        }
129
    }
130
131
    /**
132
     * Will check if the request result contains error; if errors are found, the
133
     * request is forwarded to the referring request, with the arguments of the
134
     * current request.
135
     */
136
    protected function manageRequestResult()
137
    {
138
        $request = $this->state->getRequest();
139
        $result = $request->getOriginalRequestMappingResults();
140
        $this->request->setOriginalRequestMappingResults($result);
141
142
        if ($result->hasErrors()) {
143
            $this->forwardToReferrer();
144
        }
145
    }
146
147
    /**
148
     * Forwards the request to the referrer request. It will also fill the
149
     * arguments of the action with the one from the source request.
150
     */
151
    protected function forwardToReferrer()
152
    {
153
        $request = $this->state->getRequest();
154
        $arguments = $request->getArguments();
155
        $referringRequest = $request->getReferringRequest();
156
        $this->state->setRequest($referringRequest);
0 ignored issues
show
Bug introduced by
It seems like $referringRequest defined by $request->getReferringRequest() on line 155 can be null; however, Romm\Formz\Controller\Co...llerState::setRequest() 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...
157
158
        foreach ($arguments as $name => $value) {
159
            $referringRequest->setArgument($name, $value);
160
        }
161
    }
162
163
    /**
164
     * Loops on the request arguments, and pick up each one that is a form
165
     * instance (it implements `FormInterface`).
166
     *
167
     * @return FormObject[]
168
     */
169
    protected function getRequestForms()
170
    {
171
        if (empty($this->formArguments)) {
172
            /** @var Argument $argument */
173
            foreach ($this->state->getArguments() as $argument) {
174
                $type = $argument->getDataType();
175
176
                if (class_exists($type)
177
                    && in_array(FormInterface::class, class_implements($type))
178
                ) {
179
                    $formClassName = $argument->getDataType();
180
                    $formName = $argument->getName();
181
182
                    $formObject = $this->formObjectFactory->getInstanceFromClassName($formClassName, $formName);
183
                    $this->formArguments[$formName] = $formObject;
184
                }
185
            }
186
        }
187
188
        return $this->formArguments;
189
    }
190
191
    /**
192
     * @todo
193
     *
194
     * @return array
195
     */
196
    protected function getRequestData()
197
    {
198
        $requestData = [];
199
200
        if ($this->request->hasArgument('formz')) {
201
            $requestData = $this->request->getArgument('formz');
202
            $requestData = $this->hashService->validateAndStripHmac($requestData);
203
            $requestData = unserialize(base64_decode($requestData));
204
        }
205
206
        return $requestData;
207
    }
208
209
    /**
210
     * @param RedirectException $redirectException
211
     */
212
    protected function redirectFromException(RedirectException $redirectException)
213
    {
214
        $this->redirect(
215
            $redirectException->getActionName(),
216
            $redirectException->getControllerName(),
217
            $redirectException->getExtensionName(),
218
            $redirectException->getArguments(),
219
            $redirectException->getPageUid(),
220
            $redirectException->getDelay(),
221
            $redirectException->getStatusCode()
222
        );
223
    }
224
225
    /**
226
     * @param ControllerState $state
227
     */
228
    public function injectState(ControllerState $state)
229
    {
230
        $this->state = $state;
231
    }
232
233
    /**
234
     * @param HashService $hashService
235
     */
236
    public function injectHashService(HashService $hashService)
237
    {
238
        $this->hashService = $hashService;
239
    }
240
241
    /**
242
     * @param FormObjectFactory $formObjectFactory
243
     */
244
    public function injectFormObjectFactory(FormObjectFactory $formObjectFactory)
245
    {
246
        $this->formObjectFactory = $formObjectFactory;
247
    }
248
}
249