Passed
Push — master ( ccffc6...318ccf )
by Dawid
02:48
created

DataCollector::isGetMethodWasOverridden()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiechu\SymfonyCommonsBundle\Service;
6
7
use Doctrine\Common\Annotations\Reader;
8
use Spiechu\SymfonyCommonsBundle\Annotation\Controller\ControllerAnnotationExtractorTrait;
9
use Spiechu\SymfonyCommonsBundle\Annotation\Controller\ResponseSchemaValidator;
10
use Spiechu\SymfonyCommonsBundle\Event\ApiVersion\ApiVersionSetEvent;
11
use Spiechu\SymfonyCommonsBundle\Event\ApiVersion\Events as ApiVersionEvents;
12
use Spiechu\SymfonyCommonsBundle\Event\ResponseSchemaCheck\CheckResult;
13
use Spiechu\SymfonyCommonsBundle\Event\ResponseSchemaCheck\Events as ResponseSchemaCheckEvents;
14
use Spiechu\SymfonyCommonsBundle\EventListener\GetMethodOverrideListener;
15
use Spiechu\SymfonyCommonsBundle\EventListener\RequestSchemaValidatorListener;
16
use Spiechu\SymfonyCommonsBundle\Service\SchemaValidator\ValidationViolation;
17
use Spiechu\SymfonyCommonsBundle\Twig\DataCollectorExtension;
18
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
22
use Symfony\Component\HttpKernel\DataCollector\DataCollector as BaseDataCollector;
23
use Symfony\Component\Routing\RouterInterface;
24
25
class DataCollector extends BaseDataCollector implements EventSubscriberInterface
26
{
27
    use ControllerAnnotationExtractorTrait;
28
29
    public const COLLECTOR_NAME = 'spiechu_symfony_commons.data_collector';
30
31
    /**
32
     * @var RouterInterface
33
     */
34
    protected $router;
35
36
    /**
37
     * @var Reader
38
     */
39
    protected $reader;
40
41
    /**
42
     * @var ControllerResolverInterface
43
     */
44
    protected $controllerResolver;
45
46
    /**
47
     * @var DataCollectorExtension
48
     */
49
    protected $dataCollectorExtension;
50
51
    /**
52
     * @param RouterInterface             $router
53
     * @param Reader                      $reader
54
     * @param ControllerResolverInterface $controllerResolver
55
     * @param DataCollectorExtension      $dataCollectorExtension
56
     */
57 12
    public function __construct(
58
        RouterInterface $router,
59
        Reader $reader,
60
        ControllerResolverInterface $controllerResolver,
61
        DataCollectorExtension $dataCollectorExtension
62
    ) {
63 12
        $this->router = $router;
64 12
        $this->reader = $reader;
65 12
        $this->controllerResolver = $controllerResolver;
66 12
        $this->dataCollectorExtension = $dataCollectorExtension;
67 12
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72 12
    public function collect(Request $request, Response $response, \Exception $exception = null): void
73
    {
74 12
        $this->data['known_route_response_schemas'] = $request->attributes->has(RequestSchemaValidatorListener::ATTRIBUTE_RESPONSE_SCHEMAS)
75 2
            ? $request->attributes->get(RequestSchemaValidatorListener::ATTRIBUTE_RESPONSE_SCHEMAS)
76 10
            : null;
77
78 12
        $this->data['get_method_override'] = $request->attributes->has(GetMethodOverrideListener::ATTRIBUTE_REQUEST_GET_METHOD_OVERRIDE)
79 1
            ? $request->attributes->get(GetMethodOverrideListener::ATTRIBUTE_REQUEST_GET_METHOD_OVERRIDE)
80 12
            : null;
81
82 12
        $this->extractRoutesData();
83 12
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 12
    public function getName(): string
89
    {
90 12
        return static::COLLECTOR_NAME;
91
    }
92
93
    /**
94
     * Forward compatibility with Symfony 3.4.
95
     */
96
    public function reset(): void
97
    {
98
        $this->data = [];
99
    }
100
101
    /**
102
     * @return array
103
     */
104
    public function getGlobalResponseSchemas(): array
105
    {
106
        return $this->data['global_response_schemas'];
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 6
    public static function getSubscribedEvents(): array
113
    {
114
        return [
115 6
            ResponseSchemaCheckEvents::CHECK_RESULT => ['onCheckResult', 100],
116 6
            ApiVersionEvents::API_VERSION_SET => ['onApiVersionSet', 100],
117
        ];
118
    }
119
120
    /**
121
     * @param CheckResult $checkResult
122
     */
123 2
    public function onCheckResult(CheckResult $checkResult): void
124
    {
125 2
        $this->data['validation_result'] = $checkResult->getValidationResult();
126 2
    }
127
128
    /**
129
     * @param ApiVersionSetEvent $apiVersionSetEvent
130
     */
131 9
    public function onApiVersionSet(ApiVersionSetEvent $apiVersionSetEvent): void
132
    {
133 9
        $this->data['api_version_set'] = $apiVersionSetEvent->getApiVersion();
134 9
    }
135
136
    /**
137
     * @return array
138
     */
139 9
    public function getKnownRouteResponseSchemas(): array
140
    {
141 9
        return empty($this->data['known_route_response_schemas']) ? [] : $this->data['known_route_response_schemas'];
142
    }
143
144
    /**
145
     * @return int
146
     */
147 9
    public function getKnownRouteResponseSchemaNumber(): int
148
    {
149 9
        $counter = 0;
150
151 9
        foreach ($this->getKnownRouteResponseSchemas() as $format) {
152
            $counter += \count($format);
153
        }
154
155 9
        return $counter;
156
    }
157
158
    /**
159
     * @return int
160
     */
161 9
    public function getAllPotentialErrorsCount(): int
162
    {
163 9
        return \count($this->getValidationErrors()) + $this->getGlobalNonExistingSchemaFiles();
164
    }
165
166
    /**
167
     * @return bool
168
     */
169 11
    public function responseWasChecked(): bool
170
    {
171 11
        return array_key_exists('validation_result', $this->data);
172
    }
173
174
    /**
175
     * @return bool
176
     */
177 9
    public function apiVersionWasSet(): bool
178
    {
179 9
        return array_key_exists('api_version_set', $this->data);
180
    }
181
182
    /**
183
     * @return null|string
184
     */
185 9
    public function getApiVersion(): ?string
186
    {
187 9
        return $this->apiVersionWasSet() ? $this->data['api_version_set'] : null;
188
    }
189
190
    /**
191
     * @return ValidationViolation[]
192
     */
193 9
    public function getValidationErrors(): array
194
    {
195 9
        if (!$this->responseWasChecked()) {
196 9
            return [];
197
        }
198
199
        return $this->data['validation_result']->getViolations();
200
    }
201
202
    /**
203
     * @return bool
204
     */
205
    public function isGetMethodWasOverridden(): bool
206
    {
207
        return empty($this->data['get_method_override']) ? false : true;
208
    }
209
210
    /**
211
     * @return null|string
212
     */
213
    public function getGetMethodOverriddenTo(): ?string
214
    {
215
        return $this->isGetMethodWasOverridden() ? $this->data['get_method_override'] : null;
216
    }
217
218 12
    protected function extractRoutesData(): void
219
    {
220 12
        $this->data['global_response_schemas'] = [];
221 12
        $this->data['global_non_existing_schema_files'] = 0;
222
223 12
        foreach ($this->router->getRouteCollection() as $name => $route) {
224 12
            if (empty($controllerDefinition = $route->getDefault('_controller'))) {
225
                continue;
226
            }
227
228 12
            $methodAnnotation = $this->extractControllerResponseValidator($controllerDefinition);
229 12
            if (!$methodAnnotation instanceof ResponseSchemaValidator) {
230 12
                continue;
231
            }
232
233 11
            $annotationSchemas = $methodAnnotation->getSchemas();
234
235 11
            $this->data['global_response_schemas'][] = [
236 11
                'path' => $route->getPath(),
237 11
                'name' => $name,
238 11
                'controller' => $controllerDefinition,
239 11
                'response_schemas' => $annotationSchemas,
240
            ];
241
242 11
            foreach ($annotationSchemas as $schemas) {
243 11
                foreach ($schemas as $schema) {
244 11
                    if (!$this->dataCollectorExtension->schemaFileExists($schema)) {
245 11
                        ++$this->data['global_non_existing_schema_files'];
246
                    }
247
                }
248
            }
249
        }
250 12
    }
251
252
    /**
253
     * @param string $controllerDefinition
254
     *
255
     * @throws \Exception
256
     *
257
     * @return null|ResponseSchemaValidator
258
     */
259 12
    protected function extractControllerResponseValidator(string $controllerDefinition): ?ResponseSchemaValidator
260
    {
261 12
        $resolvedController = $this->controllerResolver->getController(new Request(
262 12
            [],
263 12
            [],
264
            [
265 12
                '_controller' => $controllerDefinition,
266
            ]
267
        ));
268
269 12
        if (!\is_callable($resolvedController)) {
270
            return null;
271
        }
272
273 12
        return $this->getMethodAnnotationFromController($resolvedController, ResponseSchemaValidator::class);
274
    }
275
276
    /**
277
     * @return int
278
     */
279 9
    protected function getGlobalNonExistingSchemaFiles(): int
280
    {
281 9
        return empty($this->data['global_non_existing_schema_files']) ? 0 : \count($this->data['global_non_existing_schema_files']);
282
    }
283
284
    /**
285
     * {@inheritdoc}
286
     */
287 12
    protected function getAnnotationReader(): Reader
288
    {
289 12
        return $this->reader;
290
    }
291
}
292