Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#590)
by Daniel
22:44
created

ConfigParserPass::getConfigs()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 35
ccs 20
cts 20
cp 1
rs 9.3222
c 0
b 0
f 0
cc 5
nc 12
nop 1
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\DependencyInjection\Compiler;
6
7
use Overblog\GraphQLBundle\Config\Parser\AnnotationParser;
8
use Overblog\GraphQLBundle\Config\Parser\GraphQLParser;
9
use Overblog\GraphQLBundle\Config\Parser\PreParserInterface;
10
use Overblog\GraphQLBundle\Config\Parser\XmlParser;
11
use Overblog\GraphQLBundle\Config\Parser\YamlParser;
12
use Overblog\GraphQLBundle\DependencyInjection\TypesConfiguration;
13
use Overblog\GraphQLBundle\OverblogGraphQLBundle;
14
use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
15
use Symfony\Component\Config\Definition\Processor;
16
use Symfony\Component\Config\Resource\FileResource;
17
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\Finder\Finder;
20
use Symfony\Component\Finder\SplFileInfo;
21
22
class ConfigParserPass implements CompilerPassInterface
23
{
24
    public const SUPPORTED_TYPES_EXTENSIONS = [
25
        'yaml' => '{yaml,yml}',
26
        'xml' => 'xml',
27
        'graphql' => '{graphql,graphqls}',
28
        'annotation' => 'php',
29
    ];
30
31
    public const PARSERS = [
32
        'yaml' => YamlParser::class,
33
        'xml' => XmlParser::class,
34
        'graphql' => GraphQLParser::class,
35
        'annotation' => AnnotationParser::class,
36
    ];
37
38
    private static $defaultDefaultConfig = [
39
        'definitions' => [
40
            'mappings' => [
41
                'auto_discover' => [
42
                    'root_dir' => true,
43
                    'bundles' => true,
44
                ],
45
                'types' => [],
46
            ],
47
        ],
48
    ];
49
50
    private $treatedFiles = [];
51
    private $preTreatedFiles = [];
52
53
    public const DEFAULT_TYPES_SUFFIX = '.types';
54
55 39
    public function process(ContainerBuilder $container): void
56
    {
57 39
        $config = $this->processConfiguration([$this->getConfigs($container)]);
58 36
        $container->setParameter($this->getAlias().'.config', $config);
59 36
    }
60
61 47
    public function processConfiguration(array $configs): array
62
    {
63 47
        return (new Processor())->processConfiguration(new TypesConfiguration(), $configs);
64
    }
65
66 39
    private function getConfigs(ContainerBuilder $container): array
67
    {
68 39
        $config = $container->getParameterBag()->resolveValue($container->getParameter('overblog_graphql.config'));
69 39
        $container->getParameterBag()->remove('overblog_graphql.config');
70 39
        $container->setParameter($this->getAlias().'.classes_map', []);
71 39
        $typesMappings = $this->mappingConfig($config, $container);
72
        // reset treated files
73 39
        $this->treatedFiles = [];
74 39
        $typesMappings = \array_merge(...$typesMappings);
75 39
        $typeConfigs = [];
76
77
        // treats mappings
78
        // Pre-parse all files
79 39
        AnnotationParser::reset();
80 39
        $typesNeedPreParsing = $this->typesNeedPreParsing();
81 39
        foreach ($typesMappings as $params) {
82 39
            if ($typesNeedPreParsing[$params['type']]) {
83 1
                $this->parseTypeConfigFiles($params['type'], $params['files'], $container, $config, true);
84
            }
85
        }
86
87
        // Parse all files and get related config
88 39
        foreach ($typesMappings as $params) {
89 39
            $typeConfigs = \array_merge($typeConfigs, $this->parseTypeConfigFiles($params['type'], $params['files'], $container, $config));
90
        }
91
92 37
        $this->checkTypesDuplication($typeConfigs);
93
        // flatten config is a requirement to support inheritance
94 37
        $flattenTypeConfig = \array_merge(...$typeConfigs);
95
96 37
        if (!empty($typesCallback = $config['definitions']['mappings']['types_callback'] ?? null)) {
97 1
            $flattenTypeConfig = $typesCallback::processTypesConfiguration($flattenTypeConfig, $container, $config);
98
        }
99
100 37
        return $flattenTypeConfig;
101
    }
102
103 39
    private function typesNeedPreParsing(): array
104
    {
105 39
        $needPreParsing = [];
106 39
        foreach (self::PARSERS as $type => $className) {
107 39
            $needPreParsing[$type] = \is_a($className, PreParserInterface::class, true);
108
        }
109
110 39
        return $needPreParsing;
111
    }
112
113
    /**
114
     * @param $type
115
     * @param SplFileInfo[]    $files
116
     * @param ContainerBuilder $container
117
     * @param array            $configs
118
     * @param bool             $preParse
119
     *
120
     * @return array
121
     */
122 39
    private function parseTypeConfigFiles($type, $files, ContainerBuilder $container, array $configs, bool $preParse = false)
123
    {
124 39
        if ($preParse) {
125 1
            $method = 'preParse';
126 1
            $treatedFiles = &$this->preTreatedFiles;
127
        } else {
128 39
            $method = 'parse';
129 39
            $treatedFiles = &$this->treatedFiles;
130
        }
131
132 39
        $config = [];
133 39
        foreach ($files as $file) {
134 39
            $fileRealPath = $file->getRealPath();
135 39
            if (isset($treatedFiles[$fileRealPath])) {
136 1
                continue;
137
            }
138
139 39
            $config[] = \call_user_func([self::PARSERS[$type], $method], $file, $container, $configs);
140 37
            $treatedFiles[$file->getRealPath()] = true;
141
        }
142
143 37
        return $config;
144
    }
145
146 37
    private function checkTypesDuplication(array $typeConfigs): void
147
    {
148 37
        $types = \array_merge(...\array_map('array_keys', $typeConfigs));
149
        $duplications = \array_keys(\array_filter(\array_count_values($types), function ($count) {
150 37
            return $count > 1;
151 37
        }));
152 37
        if (!empty($duplications)) {
153
            throw new ForbiddenOverwriteException(\sprintf(
154
                'Types (%s) cannot be overwritten. See inheritance doc section for more details.',
155
                \implode(', ', \array_map('json_encode', $duplications))
156
            ));
157
        }
158 37
    }
159
160 39
    private function mappingConfig(array $config, ContainerBuilder $container)
161
    {
162
        // use default value if needed
163 39
        $config = \array_replace_recursive(self::$defaultDefaultConfig, $config);
164
165 39
        $mappingConfig = $config['definitions']['mappings'];
166 39
        $typesMappings = $mappingConfig['types'];
167
168
        // app only config files (yml or xml or graphql)
169 39
        if ($mappingConfig['auto_discover']['root_dir'] && $container->hasParameter('kernel.root_dir')) {
170 1
            $typesMappings[] = ['dir' => $container->getParameter('kernel.root_dir').'/config/graphql', 'types' => null];
171
        }
172 39
        if ($mappingConfig['auto_discover']['bundles']) {
173 5
            $mappingFromBundles = $this->mappingFromBundles($container);
174 5
            $typesMappings = \array_merge($typesMappings, $mappingFromBundles);
175
        } else {
176
            // enabled only for this bundle
177 34
            $typesMappings[] = [
178 34
                'dir' => $this->bundleDir(OverblogGraphQLBundle::class).'/Resources/config/graphql',
179
                'types' => ['yaml'],
180
            ];
181
        }
182
183
        // from config
184 39
        $typesMappings = $this->detectFilesFromTypesMappings($typesMappings, $container);
185
186 39
        return $typesMappings;
187
    }
188
189 39
    private function detectFilesFromTypesMappings(array $typesMappings, ContainerBuilder $container)
190
    {
191 39
        return \array_filter(\array_map(
192
            function (array $typeMapping) use ($container) {
193 39
                $suffix = $typeMapping['suffix'] ?? '';
194 39
                $types = $typeMapping['types'] ?? null;
195 39
                $params = $this->detectFilesByTypes($container, $typeMapping['dir'], $suffix, $types);
196
197 39
                return $params;
198 39
            },
199 39
            $typesMappings
200
        ));
201
    }
202
203 5
    private function mappingFromBundles(ContainerBuilder $container)
204
    {
205 5
        $typesMappings = [];
206 5
        $bundles = $container->getParameter('kernel.bundles');
207
208
        // auto detect from bundle
209 5
        foreach ($bundles as $name => $class) {
210 1
            $bundleDir = $this->bundleDir($class);
211
212
            // only config files (yml or xml)
213 1
            $typesMappings[] = ['dir' => $bundleDir.'/Resources/config/graphql', 'types' => null];
214
        }
215
216 5
        return $typesMappings;
217
    }
218
219 39
    private function detectFilesByTypes(ContainerBuilder $container, $path, $suffix, array $types = null)
220
    {
221
        // add the closest existing directory as a resource
222 39
        $resource = $path;
223 39
        while (!\is_dir($resource)) {
224 1
            $resource = \dirname($resource);
225
        }
226 39
        $container->addResource(new FileResource($resource));
227
228 39
        $stopOnFirstTypeMatching = empty($types);
229
230 39
        $types = $stopOnFirstTypeMatching ? \array_keys(self::SUPPORTED_TYPES_EXTENSIONS) : $types;
231 39
        $files = [];
232
233 39
        foreach ($types as $type) {
234 39
            $finder = Finder::create();
235
            try {
236 39
                $finder->files()->in($path)->name(\sprintf('*%s.%s', $suffix, self::SUPPORTED_TYPES_EXTENSIONS[$type]));
237 1
            } catch (\InvalidArgumentException $e) {
238 1
                continue;
239
            }
240 39
            if ($finder->count() > 0) {
241 39
                $files[] = [
242 39
                    'type' => $type,
243 39
                    'files' => $finder,
244
                ];
245 39
                if ($stopOnFirstTypeMatching) {
246 1
                    break;
247
                }
248
            }
249
        }
250
251 39
        return $files;
252
    }
253
254 35
    private function bundleDir($bundleClass)
255
    {
256 35
        $bundle = new \ReflectionClass($bundleClass);
257 35
        $bundleDir = \dirname($bundle->getFileName());
258
259 35
        return $bundleDir;
260
    }
261
262 39
    private function getAliasPrefix()
263
    {
264 39
        return 'overblog_graphql';
265
    }
266
267 39
    private function getAlias()
268
    {
269 39
        return $this->getAliasPrefix().'_types';
270
    }
271
}
272