Completed
Push — master ( 677e94...463d73 )
by Gerrit
02:18
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 15
    public function __construct(
73
        ControllerHelperInterface $controllerHelper,
74
        ArgumentCompilerInterface $argumentBuilder,
75
        array $options
76
    ) {
77
        /** @var int $defaultResponseCode */
78 15
        $defaultResponseCode = 200;
79
80
        /** @var array<string, mixed> $defaults */
81
        $defaults = array(
82 15
            'inner-controller-method' => '__invoke',
83
            'arguments' => [],
84
            'exception-responses' => [],
85
            'success-response' => null,
86 15
            'success-response-code' => $defaultResponseCode,
87 15
            'success-flash-message' => "",
88
        );
89
90
        /** @var mixed $options */
91 15
        $options = array_merge($defaults, $options);
92
93 15
        Assert::null($this->controllerHelper);
94 15
        Assert::true(is_object($options['inner-controller']));
95 14
        Assert::isArray($options['arguments']);
96
97 13
        $this->controllerHelper = $controllerHelper;
98 13
        $this->argumentBuilder = $argumentBuilder;
99 13
        $this->innerController = $options['inner-controller'];
100 13
        $this->innerControllerMethod = $options['inner-controller-method'];
101 13
        $this->innerControllerArgumentsConfiguration = $options['arguments'];
102 13
        $this->successResponse = $options['success-response'];
103 13
        $this->successResponseCode = $options['success-response-code'];
104 13
        $this->successFlashMessage = $options['success-flash-message'];
105
106 13
        foreach ($options['exception-responses'] as $exceptionClass => $responseData) {
107
            /** @var array<string, mixed> $responseData */
108
109 5
            Assert::true(
110 5
                is_a($exceptionClass, Exception::class, true) ||
111 5
                is_a($exceptionClass, Throwable::class, true)
112
            );
113
114
            /** @var string $responseCode */
115 4
            $responseCode = '500';
116
117 4
            if (isset($responseData['redirect-route'])) {
118 2
                $responseCode = '301';
119
            }
120
121 4
            $responseData = array_merge([
122 4
                'message' => '', # empty => exception message used
123 4
                'code' => $responseCode,
124 4
                'flash-type' => '', # empty => no message triggered
125 4
                'flash-message' => '%s', # empty => exception message used
126
                'redirect-route' => null,
127
                'redirect-route-parameters' => [],
128 4
                'filter' => '',
129 4
            ], $responseData);
130
131 4
            $this->exceptionResponses[$exceptionClass] = $responseData;
132
        }
133 12
    }
134
135 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...
136
    {
137
        /** @var Request $request */
138 2
        $request = $this->controllerHelper->getCurrentRequest();
139
140 2
        Assert::isInstanceOf($request, Request::class, "Cannot use controller outside of request-scope!");
141
142 1
        return $this->executeInnerControllerSafely($request);
143
    }
144
145 10
    public function executeInnerControllerSafely(Request $request): Response
146
    {
147
        /** @var Response|null $response */
148 10
        $response = null;
149
150
        /** @var Response|null $innerResponse */
151 10
        $innerResponse = null;
152
153
        try {
154
            /** @var array<int, mixed> $arguments */
155 10
            $arguments = array();# TODO
156
157 10
            $methodReflection = new ReflectionMethod($this->innerController, $this->innerControllerMethod);
158
159
            /** @var array<int, mixed> $arguments */
160 10
            $arguments = $this->argumentBuilder->buildCallArguments(
161 10
                $methodReflection,
162 10
                $this->innerControllerArgumentsConfiguration,
163 10
                $request
164
            );
165
166 10
            $innerResponse = call_user_func_array([$this->innerController, $this->innerControllerMethod], $arguments);
167
168 5
            Assert::isInstanceOf($innerResponse, Response::class, "Controller did not return an Response object!");
169
170 4
            if (!empty($this->successFlashMessage)) {
171 1
                $this->controllerHelper->addFlashMessage($this->successFlashMessage, "success");
172
            }
173
174 4
            if (!is_null($this->successResponse)) {
175 2
                $response = new Response($this->successResponse, $this->successResponseCode);
176
177
            } else {
178 4
                $response = $innerResponse;
179
            }
180
181 6
        } catch (Throwable $exception) {
182 6
            $this->controllerHelper->handleException($exception);
183
184 6
            foreach ($this->exceptionResponses as $exceptionClass => $responseData) {
185 4
                if (is_a($exception, $exceptionClass)) {
186 4
                    if (!empty($responseData['filter'])) {
187
                        if (!preg_match("/" . $responseData['filter'] . "/is", $exception->getMessage())) {
188
                            continue;
189
                        }
190
                    }
191
192
                    /** @var string $responseMessage */
193 4
                    $responseMessage = $responseData['message'];
194
195 4
                    if (empty($responseMessage)) {
196 4
                        $responseMessage = $exception->getMessage();
197
                    }
198
199 4
                    if (!empty($responseData['flash-type'])) {
200
                        /** @var string $flashMessage */
201 2
                        $flashMessage = sprintf($responseData['flash-message'], $exception->getMessage());
202
203 2
                        $this->controllerHelper->addFlashMessage($flashMessage, $responseData['flash-type']);
204
                    }
205
206 4
                    if (!empty($responseData['redirect-route'])) {
207
                        /** @var array $redirectRouteParameters */
208 2
                        $redirectRouteParameters = array_merge(
209 2
                            $request->attributes->get('_route_params'),
210 2
                            $this->argumentBuilder->buildArguments(
211 2
                                $responseData['redirect-route-parameters'],
212 2
                                $request
213
                            )
214
                        );
215
216 2
                        $response = $this->controllerHelper->redirectToRoute(
217 2
                            $responseData['redirect-route'],
218 2
                            $redirectRouteParameters,
219 2
                            $responseData['code']
220
                        );
221
222
                    } else {
223 2
                        $response = new Response($responseMessage, $responseData['code']);
224
                    }
225
226 4
                    break;
227
                }
228
            }
229
230 6
            if (is_null($response)) {
231 2
                throw $exception;
232
            }
233
        }
234
235 8
        return $response;
236
    }
237
238
}
239