Passed
Push — master ( 8214a5...a64949 )
by Gerrit
01:53
created

GenericExceptionResponseController::__invoke()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 9
Ratio 100 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 9
loc 9
ccs 4
cts 4
cp 1
rs 9.9666
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 *
8
 * @license GPL-3.0
9
 *
10
 * @author Gerrit Addiks <[email protected]>
11
 */
12
13
namespace Addiks\SymfonyGenerics\Controllers;
14
15
use Addiks\SymfonyGenerics\Controllers\ControllerHelperInterface;
16
use Webmozart\Assert\Assert;
17
use Exception;
18
use Throwable;
19
use Symfony\Component\HttpFoundation\Response;
20
use Addiks\SymfonyGenerics\Services\ArgumentCompilerInterface;
21
use Symfony\Component\HttpFoundation\Request;
22
use ReflectionMethod;
23
24
final class GenericExceptionResponseController
25
{
26
27
    /**
28
     * @var ControllerHelperInterface
29
     */
30
    private $controllerHelper;
31
32
    /**
33
     * @var ArgumentCompilerInterface
34
     */
35
    private $argumentBuilder;
36
37
    /**
38
     * @var object
39
     */
40
    private $innerController;
41
42
    /**
43
     * @var string
44
     */
45
    private $innerControllerMethod;
46
47
    /**
48
     * @var array
49
     */
50
    private $innerControllerArgumentsConfiguration;
51
52
    /**
53
     * @var string|null
54
     */
55
    private $successResponse;
56
57
    /**
58
     * @var int
59
     */
60
    private $successResponseCode;
61
62
    /**
63
     * @var string
64
     */
65
    private $successFlashMessage;
66
67
    /**
68
     * @var array<string, array<string, mixed>>
69
     */
70
    private $exceptionResponses = array();
71
72 16
    public function __construct(
73
        ControllerHelperInterface $controllerHelper,
74
        ArgumentCompilerInterface $argumentBuilder,
75
        array $options
76
    ) {
77
        /** @var int $defaultResponseCode */
78 16
        $defaultResponseCode = 200;
79
80
        /** @var array<string, mixed> $defaults */
81
        $defaults = array(
82 16
            'inner-controller-method' => '__invoke',
83
            'arguments' => [],
84
            'exception-responses' => [],
85
            'success-response' => null,
86 16
            'success-response-code' => $defaultResponseCode,
87 16
            'success-flash-message' => "",
88
        );
89
90
        /** @var mixed $options */
91 16
        $options = array_merge($defaults, $options);
92
93 16
        Assert::null($this->controllerHelper);
94 16
        Assert::true(is_object($options['inner-controller']));
95 15
        Assert::isArray($options['arguments']);
96
97 14
        $this->controllerHelper = $controllerHelper;
98 14
        $this->argumentBuilder = $argumentBuilder;
99 14
        $this->innerController = $options['inner-controller'];
100 14
        $this->innerControllerMethod = $options['inner-controller-method'];
101 14
        $this->innerControllerArgumentsConfiguration = $options['arguments'];
102 14
        $this->successResponse = $options['success-response'];
103 14
        $this->successResponseCode = $options['success-response-code'];
104 14
        $this->successFlashMessage = $options['success-flash-message'];
105
106 14
        foreach ($options['exception-responses'] as $exceptionClass => $responseData) {
107
            /** @var array<string, mixed> $responseData */
108
109
            /** @var string $responseCode */
110 6
            $responseCode = '500';
111
112 6
            if (isset($responseData['redirect-route'])) {
113 2
                $responseCode = '301';
114
            }
115
116 6
            $responseData = array_merge([
117 6
                'exception-class' => is_int($exceptionClass) ?null :$exceptionClass,
118 6
                'message' => '', # empty => exception message used
119 6
                'code' => $responseCode,
120 6
                'flash-type' => '', # empty => no message triggered
121 6
                'flash-message' => '%s', # empty => exception message used
122
                'redirect-route' => null,
123
                'redirect-route-parameters' => [],
124 6
                'filter' => '',
125 6
            ], $responseData);
126
127 6
            Assert::true(
128 6
                is_a($responseData['exception-class'], Throwable::class, true) ||
129 6
                empty($responseData['exception-class'])
130
            );
131
132 5
            $this->exceptionResponses[] = $responseData;
133
        }
134 13
    }
135
136 2 View Code Duplication
    public function __invoke(): Response
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137
    {
138
        /** @var Request $request */
139 2
        $request = $this->controllerHelper->getCurrentRequest();
140
141 2
        Assert::isInstanceOf($request, Request::class, "Cannot use controller outside of request-scope!");
142
143 1
        return $this->executeInnerControllerSafely($request);
144
    }
145
146 11
    public function executeInnerControllerSafely(Request $request): Response
147
    {
148
        /** @var Response|null $response */
149 11
        $response = null;
150
151
        /** @var Response|null $innerResponse */
152 11
        $innerResponse = null;
153
154
        try {
155
            /** @var array<int, mixed> $arguments */
156 11
            $arguments = array();# TODO
157
158 11
            $methodReflection = new ReflectionMethod($this->innerController, $this->innerControllerMethod);
159
160
            /** @var array<int, mixed> $arguments */
161 11
            $arguments = $this->argumentBuilder->buildCallArguments(
162 11
                $methodReflection,
163 11
                $this->innerControllerArgumentsConfiguration,
164 11
                $request
165
            );
166
167 11
            $innerResponse = call_user_func_array([$this->innerController, $this->innerControllerMethod], $arguments);
168
169 5
            Assert::isInstanceOf($innerResponse, Response::class, "Controller did not return an Response object!");
170
171 4
            if (!empty($this->successFlashMessage)) {
172 1
                $this->controllerHelper->addFlashMessage($this->successFlashMessage, "success");
173
            }
174
175 4
            if (!is_null($this->successResponse)) {
176 2
                $response = new Response($this->successResponse, $this->successResponseCode);
177
178
            } else {
179 4
                $response = $innerResponse;
180
            }
181
182 7
        } catch (Throwable $exception) {
183 7
            $this->controllerHelper->handleException($exception);
184
185 7
            foreach ($this->exceptionResponses as $responseData) {
186
187 5
                if (!empty($responseData['exception-class']) && !is_a($exception, $responseData['exception-class'])) {
188 1
                    continue;
189
                }
190
191 5
                if (!empty($responseData['filter'])) {
192 1
                    if (!preg_match("/" . $responseData['filter'] . "/is", $exception->getMessage())) {
193 1
                        continue;
194
                    }
195
                }
196
197 5
                if (!empty($responseData['flash-type'])) {
198
                    /** @var string $flashMessage */
199 3
                    $flashMessage = sprintf($responseData['flash-message'], $exception->getMessage());
200
201 3
                    $this->controllerHelper->addFlashMessage($flashMessage, $responseData['flash-type']);
202
                }
203
204 5
                if (!empty($responseData['redirect-route'])) {
205
                    /** @var array $redirectRouteParameters */
206 2
                    $redirectRouteParameters = array_merge(
207 2
                        $request->attributes->get('_route_params'),
208 2
                        $this->argumentBuilder->buildArguments(
209 2
                            $responseData['redirect-route-parameters'],
210 2
                            $request
211
                        )
212
                    );
213
214 2
                    $response = $this->controllerHelper->redirectToRoute(
215 2
                        $responseData['redirect-route'],
216 2
                        $redirectRouteParameters,
217 2
                        $responseData['code']
218
                    );
219
220
                } else {
221
                    /** @var string $responseMessage */
222 3
                    $responseMessage = $responseData['message'];
223
224 3
                    if (empty($responseMessage)) {
225 3
                        $responseMessage = $exception->getMessage();
226
                    }
227
228 3
                    $response = new Response($responseMessage, $responseData['code']);
229
                }
230
231 5
                break;
232
            }
233
234 7
            if (is_null($response)) {
235 2
                throw $exception;
236
            }
237
        }
238
239 9
        return $response;
240
    }
241
242
}
243