Issues (3641)

EventListener/ApiControllerEventListener.php (1 issue)

1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Zed\Api\Communication\EventListener;
9
10
use Generated\Shared\Transfer\ApiRequestTransfer;
11
use Generated\Shared\Transfer\ApiResponseTransfer;
12
use JsonException;
13
use Spryker\Shared\Log\LoggerTrait;
14
use Spryker\Zed\Api\ApiConfig;
15
use Spryker\Zed\Api\Business\ApiFacadeInterface;
16
use Spryker\Zed\Api\Business\Http\HttpConstants;
17
use Spryker\Zed\Api\Communication\Controller\AbstractApiController;
18
use Spryker\Zed\Api\Communication\Transformer\TransformerInterface;
19
use Spryker\Zed\Api\Dependency\Service\ApiToUtilEncodingServiceInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\HttpKernel\Event\ControllerEvent;
23
use Throwable;
24
25
class ApiControllerEventListener implements ApiControllerEventListenerInterface
26
{
27
    use LoggerTrait;
28
29
    /**
30
     * @var string
31
     */
32
    protected const REQUEST_URI = 'REQUEST_URI';
33
34
    /**
35
     * @var \Spryker\Zed\Api\Communication\Transformer\TransformerInterface
36
     */
37
    protected $transformer;
38
39
    /**
40
     * @var \Spryker\Zed\Api\Business\ApiFacadeInterface
41
     */
42
    protected $apiFacade;
43
44
    /**
45
     * @var \Spryker\Zed\Api\Dependency\Service\ApiToUtilEncodingServiceInterface
46
     */
47
    protected $utilEncodingService;
48
49
    /**
50
     * @param \Spryker\Zed\Api\Communication\Transformer\TransformerInterface $transformer
51
     * @param \Spryker\Zed\Api\Business\ApiFacadeInterface $apiFacade
52
     * @param \Spryker\Zed\Api\Dependency\Service\ApiToUtilEncodingServiceInterface $utilEncodingService
53
     */
54
    public function __construct(
55
        TransformerInterface $transformer,
56
        ApiFacadeInterface $apiFacade,
57
        ApiToUtilEncodingServiceInterface $utilEncodingService
58
    ) {
59
        $this->transformer = $transformer;
60
        $this->apiFacade = $apiFacade;
61
        $this->utilEncodingService = $utilEncodingService;
62
    }
63
64
    /**
65
     * @param \Symfony\Component\HttpKernel\Event\ControllerEvent $controllerEvent
66
     *
67
     * @return void
68
     */
69
    public function onKernelControllerEvent(ControllerEvent $controllerEvent): void
70
    {
71
        $request = $controllerEvent->getRequest();
72
73
        if (!$request->server->has(static::REQUEST_URI) || strpos($request->server->get(static::REQUEST_URI), ApiConfig::ROUTE_PREFIX_API_REST) !== 0) {
74
            return;
75
        }
76
77
        /** @var array $currentController */
78
        $currentController = $controllerEvent->getController();
79
        [$controller, $action] = $currentController;
80
81
        if (!$controller instanceof AbstractApiController) {
82
            return;
83
        }
84
85
        $request = $controllerEvent->getRequest();
86
        try {
87
            $apiController = function () use ($controller, $action, $request) {
88
                return $this->executeControllerAction($request, $controller, $action);
89
            };
90
        } catch (JsonException $e) {
0 ignored issues
show
catch (\JsonException $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
91
            $apiController = $this->transformer->transformBadRequest(new ApiResponseTransfer(), new Response(), $e->getMessage());
92
        }
93
94
        $controllerEvent->setController($apiController);
95
    }
96
97
    /**
98
     * @param \Symfony\Component\HttpFoundation\Request $request
99
     * @param \Spryker\Zed\Api\Communication\Controller\AbstractApiController $controller
100
     * @param string $action
101
     *
102
     * @return \Symfony\Component\HttpFoundation\Response
103
     */
104
    protected function executeControllerAction(Request $request, AbstractApiController $controller, string $action): Response
105
    {
106
        $apiRequestTransfer = $this->getApiRequestTransfer($request);
107
        $this->logRequest($apiRequestTransfer);
108
109
        try {
110
            $responseTransfer = $controller->$action($apiRequestTransfer);
111
        } catch (Throwable $exception) {
112
            $responseTransfer = new ApiResponseTransfer();
113
            $responseTransfer->setCode($this->resolveStatusCode((int)$exception->getCode()));
114
            $responseTransfer->setMessage($exception->getMessage());
115
            $responseTransfer->setStackTrace(sprintf(
116
                '%s (%s, line %d): %s',
117
                get_class($exception),
118
                $exception->getFile(),
119
                $exception->getLine(),
120
                $exception->getTraceAsString(),
121
            ));
122
        }
123
124
        $this->logResponse($responseTransfer);
125
126
        return $this->transformer->transform($apiRequestTransfer, $responseTransfer, new Response());
127
    }
128
129
    /**
130
     * @param int $code
131
     *
132
     * @return int
133
     */
134
    protected function resolveStatusCode(int $code): int
135
    {
136
        if ($code < ApiConfig::HTTP_CODE_SUCCESS || $code > ApiConfig::HTTP_CODE_INTERNAL_ERROR) {
137
            return ApiConfig::HTTP_CODE_INTERNAL_ERROR;
138
        }
139
140
        return $code;
141
    }
142
143
    /**
144
     * @param \Symfony\Component\HttpFoundation\Request $request
145
     *
146
     * @throws \JsonException
147
     *
148
     * @return \Generated\Shared\Transfer\ApiRequestTransfer
149
     */
150
    protected function getApiRequestTransfer(Request $request): ApiRequestTransfer
151
    {
152
        $requestTransfer = new ApiRequestTransfer();
153
154
        $requestTransfer->setRequestType($request->getMethod());
155
        $requestTransfer->setQueryData($request->query->all());
156
        $requestTransfer->setHeaderData($request->headers->all());
157
158
        $serverData = $request->server->all();
159
        $requestTransfer->setServerData($serverData);
160
        $requestTransfer->setRequestUri($serverData[static::REQUEST_URI]);
161
162
        if (strpos((string)$request->headers->get(HttpConstants::HEADER_CONTENT_TYPE), 'application/json') === 0) {
163
            /**
164
             * @var string|resource $content
165
             */
166
            $content = $request->getContent();
167
            if (is_resource($content)) {
168
                $content = stream_get_contents($content);
169
                $content = $content ?: '';
170
            }
171
172
            try {
173
                $data = $this->utilEncodingService->decodeJson($content, true, 512, JSON_THROW_ON_ERROR);
174
            } catch (JsonException $exception) {
175
                $this->logRequest($requestTransfer);
176
177
                throw $exception;
178
            }
179
            $request->request->replace(is_array($data) && isset($data['data']) ? $data['data'] : []);
180
        }
181
182
        return $requestTransfer->setRequestData($request->request->all());
183
    }
184
185
    /**
186
     * @param \Generated\Shared\Transfer\ApiRequestTransfer $apiRequestTransfer
187
     *
188
     * @return void
189
     */
190
    protected function logRequest(ApiRequestTransfer $apiRequestTransfer): void
191
    {
192
        $filteredApiRequestTransfer = $this->apiFacade->filterApiRequestTransfer($apiRequestTransfer);
193
194
        $this->getLogger()->info(sprintf(
195
            'API request [%s %s]: %s',
196
            $apiRequestTransfer->getRequestTypeOrFail(),
197
            $apiRequestTransfer->getRequestUriOrFail(),
198
            $this->utilEncodingService->encodeJson($filteredApiRequestTransfer->toArray()),
199
        ));
200
    }
201
202
    /**
203
     * @param \Generated\Shared\Transfer\ApiResponseTransfer $responseTransfer
204
     *
205
     * @return void
206
     */
207
    protected function logResponse(ApiResponseTransfer $responseTransfer): void
208
    {
209
        $responseTransferData = $responseTransfer->toArray();
210
        unset($responseTransferData['request']);
211
212
        $this->getLogger()->info(sprintf(
213
            'API response [code %s]: %s',
214
            $responseTransfer->getCodeOrFail(),
215
            $this->utilEncodingService->encodeJson($responseTransferData),
216
        ));
217
    }
218
}
219