Passed
Push — master ( 4925eb...ef22f5 )
by Dawid
02:45
created

SchemaFilesExistenceChecker::getAnnotationReader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiechu\SymfonyCommonsBundle\Service\SchemaValidator;
6
7
use Doctrine\Common\Annotations\Reader;
8
use Spiechu\SymfonyCommonsBundle\Annotation\Controller\ControllerAnnotationExtractorTrait;
9
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
10
use Symfony\Component\Config\FileLocatorInterface;
11
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
12
use Symfony\Component\Routing\RouterInterface;
13
use Spiechu\SymfonyCommonsBundle\Annotation\Controller\ResponseSchemaValidator;
14
use Symfony\Component\HttpFoundation\Request;
15
16
class SchemaFilesExistenceChecker
17
{
18
    use ControllerAnnotationExtractorTrait;
19
20
    /**
21
     * @var Reader
22
     */
23
    protected $reader;
24
25
    /**
26
     * @var RouterInterface
27
     */
28
    protected $router;
29
30
    /**
31
     * @var ControllerResolverInterface
32
     */
33
    protected $controllerResolver;
34
35
    /**
36
     * @var FileLocatorInterface
37
     */
38
    protected $fileLocator;
39
40
    /**
41
     * @param Reader $reader
42
     * @param RouterInterface $router
43
     * @param ControllerResolverInterface $controllerResolver
44
     * @param FileLocatorInterface $fileLocator
45
     */
46 6
    public function __construct(
47
        Reader $reader,
48
        RouterInterface $router,
49
        ControllerResolverInterface $controllerResolver,
50
        FileLocatorInterface $fileLocator
51
    ) {
52 6
        $this->reader = $reader;
53 6
        $this->router = $router;
54 6
        $this->controllerResolver = $controllerResolver;
55 6
        $this->fileLocator = $fileLocator;
56 6
    }
57
58
    /**
59
     * @throws \LogicException If any of controllers can't be found by controller resolver
60
     * @throws \RuntimeException When any of defined schema files in ResponseSchemaValidator not exist
61
     */
62 6
    public function checkControllerResponseSchemaValidatorFiles(): void
63
    {
64 6
        $corruptedFiles = [];
65
66 6
        foreach ($this->router->getRouteCollection()->all() as $route) {
67 5
            $controllerAttribute = $route->getDefault('_controller');
68 5
            $resolvedController = $this->controllerResolver->getController(new Request(
69 5
                [],
70 5
                [],
71
                [
72 5
                    '_controller' => $controllerAttribute,
73
                ]
74
            ));
75
76 5
            if (!\is_callable($resolvedController)) {
77
                continue;
78
            }
79
80 5
            $annotation = $this->getMethodAnnotationFromController($resolvedController, ResponseSchemaValidator::class);
81 5
            if (!$annotation instanceof ResponseSchemaValidator) {
82 5
                continue;
83
            }
84
85 4
            if ($checkError = $this->checkResponseSchema($annotation->getSchemas())) {
86
                if (!array_key_exists($routePath = $route->getPath(), $corruptedFiles)) {
87
                    $corruptedFiles[$routePath] = [];
88
                }
89 4
                $corruptedFiles[$routePath][] = $checkError;
90
            }
91
        }
92
93 6
        if ($corruptedFiles) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $corruptedFiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
94
            throw $this->createExceptionFromCorruptedFiles($corruptedFiles);
95
        }
96 6
    }
97
98
    /**
99
     * @param array $schemas
100
     * @return null|string
101
     */
102 4
    protected function checkResponseSchema(array $schemas): ?string
103
    {
104 4
        foreach ($schemas as $schema) {
105 4
            foreach ($schema as $schemaLocation) {
106
                try {
107 4
                    $this->fileLocator->locate($schemaLocation);
108
                } catch (FileLocatorFileNotFoundException $e) {
109 4
                    return $schemaLocation;
110
                }
111
            }
112
        }
113
114 4
        return null;
115
    }
116
117
    /**
118
     * @param array $corruptedFiles
119
     * @return \RuntimeException
120
     */
121
    protected function createExceptionFromCorruptedFiles(array $corruptedFiles): \RuntimeException
122
    {
123
        $routePaths = [];
124
125
        foreach ($corruptedFiles as $routePath => $files) {
126
            $routePaths[] = sprintf('%s[%s]', $routePath, implode(', ', $files));
127
        }
128
129
        return new \RuntimeException(sprintf('Non existing response schema files set: "%s"', implode(', ', $routePaths)));
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 5
    protected function getAnnotationReader(): Reader
136
    {
137 5
        return $this->reader;
138
    }
139
}
140