Completed
Push — middleware-wip ( be7c81...ea6aa8 )
by Romain
02:46
created

FormController::injectState()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
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\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
     * Will fetch every form argument for this request, and dispatch every
95
     * middleware that was registered in its TypoScript configuration.
96
     */
97
    protected function invokeMiddlewares()
98
    {
99
        foreach ($this->getRequestForms() as $formObject) {
100
            $formObject->reset();
101
102
            /*
103
             * If the configuration contains errors, then the middlewares
104
             * configuration may be corrupted as well, so the risk to dispatch
105
             * middlewares can not be taken.
106
             */
107
            if (false === $formObject->getConfigurationValidationResult()->hasErrors()) {
108
                /** @var MiddlewareState $middlewareState */
109
                $middlewareState = Core::instantiate(MiddlewareState::class, $formObject, $this->state);
110
111
                $middlewareState->run();
112
            }
113
        }
114
    }
115
116
    /**
117
     * Will check if the request result contains error; if errors are found, the
118
     * request is forwarded to the referring request, with the arguments of the
119
     * current request.
120
     */
121
    protected function manageRequestResult()
122
    {
123
        $request = $this->state->getRequest();
124
        $result = $request->getOriginalRequestMappingResults();
125
        $this->request->setOriginalRequestMappingResults($result);
126
127
        if ($result->hasErrors()) {
128
            $this->forwardToReferrer();
129
        }
130
    }
131
132
    /**
133
     * Forwards the request to the referrer request. It will also fill the
134
     * arguments of the action with the one from the source request.
135
     */
136
    protected function forwardToReferrer()
137
    {
138
        $request = $this->state->getRequest();
139
        $arguments = $request->getArguments();
140
        $referringRequest = $request->getReferringRequest();
141
        $this->state->setRequest($referringRequest);
0 ignored issues
show
Bug introduced by
It seems like $referringRequest defined by $request->getReferringRequest() on line 140 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...
142
143
        foreach ($arguments as $name => $value) {
144
            $referringRequest->setArgument($name, $value);
145
        }
146
    }
147
148
    /**
149
     * Loops on the request arguments, and pick up each one that is a form
150
     * instance (it implements `FormInterface`).
151
     *
152
     * @return FormObject[]
153
     */
154
    protected function getRequestForms()
155
    {
156
        if (empty($this->formArguments)) {
157
            /** @var Argument $argument */
158
            foreach ($this->state->getArguments() as $argument) {
159
                $type = $argument->getDataType();
160
161
                if (class_exists($type)
162
                    && in_array(FormInterface::class, class_implements($type))
163
                ) {
164
                    $formClassName = $argument->getDataType();
165
                    $formName = $argument->getName();
166
167
                    $formObject = $this->formObjectFactory->getInstanceFromClassName($formClassName, $formName);
168
                    $this->formArguments[$formName] = $formObject;
169
                }
170
            }
171
        }
172
173
        return $this->formArguments;
174
    }
175
176
    /**
177
     * @todo
178
     *
179
     * @return array
180
     */
181
    protected function getRequestData()
182
    {
183
        $requestData = [];
184
185
        if ($this->request->hasArgument('formz')) {
186
            $requestData = $this->request->getArgument('formz');
187
            $requestData = $this->hashService->validateAndStripHmac($requestData);
188
            $requestData = unserialize(base64_decode($requestData));
189
        }
190
191
        return $requestData;
192
    }
193
194
    /**
195
     * @param RedirectException $redirectException
196
     */
197
    protected function redirectFromException(RedirectException $redirectException)
198
    {
199
        $this->redirect(
200
            $redirectException->getActionName(),
201
            $redirectException->getControllerName(),
202
            $redirectException->getExtensionName(),
203
            $redirectException->getArguments(),
204
            $redirectException->getPageUid(),
205
            $redirectException->getDelay(),
206
            $redirectException->getStatusCode()
207
        );
208
    }
209
210
    /**
211
     * @param ControllerState $state
212
     */
213
    public function injectState(ControllerState $state)
214
    {
215
        $this->state = $state;
216
    }
217
218
    /**
219
     * @param HashService $hashService
220
     */
221
    public function injectHashService(HashService $hashService)
222
    {
223
        $this->hashService = $hashService;
224
    }
225
226
    /**
227
     * @param FormObjectFactory $formObjectFactory
228
     */
229
    public function injectFormObjectFactory(FormObjectFactory $formObjectFactory)
230
    {
231
        $this->formObjectFactory = $formObjectFactory;
232
    }
233
}
234