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

Completed
Pull Request — annotations (#407)
by Vincent
126:18
created

OverblogGraphQLTypesExtension::bundleDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
ccs 4
cts 4
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\DependencyInjection;
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\OverblogGraphQLBundle;
13
use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
14
use Symfony\Component\Config\Resource\FileResource;
15
use Symfony\Component\DependencyInjection\ContainerBuilder;
16
use Symfony\Component\Finder\Finder;
17
use Symfony\Component\Finder\SplFileInfo;
18
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
19
20
class OverblogGraphQLTypesExtension extends Extension
21
{
22
    public const SUPPORTED_TYPES_EXTENSIONS = [
23
        'yaml' => '{yaml,yml}',
24
        'xml' => 'xml',
25
        'graphql' => '{graphql,graphqls}',
26
        'annotation' => 'php',
27
    ];
28
29
    public const PARSERS = [
30
        'yaml' => YamlParser::class,
31
        'xml' => XmlParser::class,
32
        'graphql' => GraphQLParser::class,
33
        'annotation' => AnnotationParser::class,
34
    ];
35
36
    private static $defaultDefaultConfig = [
37
        'definitions' => [
38
            'mappings' => [
39
                'auto_discover' => [
40
                    'root_dir' => true,
41
                    'bundles' => true,
42
                ],
43
                'types' => [],
44
            ],
45
        ],
46
    ];
47
48
    private $treatedFiles = [];
49
    private $preTreatedFiles = [];
50
51
    public const DEFAULT_TYPES_SUFFIX = '.types';
52
53 38
    public function load(array $configs, ContainerBuilder $container): void
54
    {
55 38
        $configs = \array_filter($configs);
56 38
        if (\count($configs) > 1) {
57 1
            throw new \InvalidArgumentException('Configs type should never contain more than one config to deal with inheritance.');
58
        }
59 37
        $configuration = $this->getConfiguration($configs, $container);
60 37
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 59 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
61
62 32
        $container->setParameter($this->getAlias().'.config', $config);
63 32
    }
64
65 33
    public function containerPrependExtensionConfig(array $configs, ContainerBuilder $container): void
66
    {
67 33
        $typesMappings = $this->mappingConfig($configs, $container);
68
        // reset treated files
69 33
        $this->treatedFiles = [];
70 33
        $typesMappings = \call_user_func_array('array_merge', $typesMappings);
71 33
        $typeConfigs = [];
72
        // treats mappings
73
        // Pre-parse all files
74 33
        foreach ($typesMappings as $params) {
75 33
            $this->preParseTypeConfigFiles($params['type'], $params['files'], $container);
76
        }
77
        // Parse all files and get related config
78 33
        foreach ($typesMappings as $params) {
79 33
            $typeConfigs = \array_merge($typeConfigs, $this->parseTypeConfigFiles($params['type'], $params['files'], $container));
80
        }
81
82 31
        $this->checkTypesDuplication($typeConfigs);
83
        // flatten config is a requirement to support inheritance
84 31
        $flattenTypeConfig = \call_user_func_array('array_merge', $typeConfigs);
85
86 31
        $container->prependExtensionConfig($this->getAlias(), $flattenTypeConfig);
87 31
    }
88
89
    /**
90
     * @param $type
91
     * @param SplFileInfo[]    $files
92
     * @param ContainerBuilder $container
93
     */
94 33
    private function preParseTypeConfigFiles($type, $files, ContainerBuilder $container): void
95
    {
96 33
        if (self::PARSERS[$type] instanceof PreParserInterface) {
97
            foreach ($files as $file) {
98
                $fileRealPath = $file->getRealPath();
99
                if (isset($this->preTreatedFiles[$fileRealPath])) {
100
                    continue;
101
                }
102
103
                \call_user_func(self::PARSERS[$type].'::preParse', $file, $container);
104
                $this->preTreatedFiles[$file->getRealPath()] = true;
105
            }
106
        }
107 33
    }
108
109
    /**
110
     * @param $type
111
     * @param SplFileInfo[]    $files
112
     * @param ContainerBuilder $container
113
     *
114
     * @return array
115
     */
116 33
    private function parseTypeConfigFiles($type, $files, ContainerBuilder $container)
117
    {
118 33
        $config = [];
119 33
        foreach ($files as $file) {
120 33
            $fileRealPath = $file->getRealPath();
121 33
            if (isset($this->treatedFiles[$fileRealPath])) {
122 1
                continue;
123
            }
124
125 33
            $config[] = \call_user_func(self::PARSERS[$type].'::parse', $file, $container);
126 31
            $this->treatedFiles[$file->getRealPath()] = true;
127
        }
128
129 31
        return $config;
130
    }
131
132 31
    private function checkTypesDuplication(array $typeConfigs): void
133
    {
134 31
        $types = \call_user_func_array('array_merge', \array_map('array_keys', $typeConfigs));
135
        $duplications = \array_keys(\array_filter(\array_count_values($types), function ($count) {
136 31
            return $count > 1;
137 31
        }));
138 31
        if (!empty($duplications)) {
139
            throw new ForbiddenOverwriteException(\sprintf(
140
                'Types (%s) cannot be overwritten. See inheritance doc section for more details.',
141
                \implode(', ', \array_map('json_encode', $duplications))
142
            ));
143
        }
144 31
    }
145
146 33
    private function mappingConfig(array $config, ContainerBuilder $container)
147
    {
148
        // use default value if needed
149 33
        $config = \array_replace_recursive(self::$defaultDefaultConfig, $config);
150
151 33
        $mappingConfig = $config['definitions']['mappings'];
152 33
        $typesMappings = $mappingConfig['types'];
153
154
        // app only config files (yml or xml or graphql)
155 33
        if ($mappingConfig['auto_discover']['root_dir'] && $container->hasParameter('kernel.root_dir')) {
156 1
            $typesMappings[] = ['dir' => $container->getParameter('kernel.root_dir').'/config/graphql', 'types' => null];
157
        }
158 33
        if ($mappingConfig['auto_discover']['bundles']) {
159 3
            $mappingFromBundles = $this->mappingFromBundles($container);
160 3
            $typesMappings = \array_merge($typesMappings, $mappingFromBundles);
161
        } else {
162
            // enabled only for this bundle
163 30
            $typesMappings[] = [
164 30
                'dir' => $this->bundleDir(OverblogGraphQLBundle::class).'/Resources/config/graphql',
165
                'types' => ['yaml'],
166
            ];
167
        }
168
169
        // from config
170 33
        $typesMappings = $this->detectFilesFromTypesMappings($typesMappings, $container);
171
172 33
        return $typesMappings;
173
    }
174
175 33
    private function detectFilesFromTypesMappings(array $typesMappings, ContainerBuilder $container)
176
    {
177 33
        return \array_filter(\array_map(
178
            function (array $typeMapping) use ($container) {
179 33
                $suffix = $typeMapping['suffix'] ?? '';
180 33
                $types = $typeMapping['types'] ?? null;
181 33
                $params = $this->detectFilesByTypes($container, $typeMapping['dir'], $suffix, $types);
182
183 33
                return $params;
184 33
            },
185 33
            $typesMappings
186
        ));
187
    }
188
189 3
    private function mappingFromBundles(ContainerBuilder $container)
190
    {
191 3
        $typesMappings = [];
192 3
        $bundles = $container->getParameter('kernel.bundles');
193
194
        // auto detect from bundle
195 3
        foreach ($bundles as $name => $class) {
196 1
            $bundleDir = $this->bundleDir($class);
197
198
            // only config files (yml or xml)
199 1
            $typesMappings[] = ['dir' => $bundleDir.'/Resources/config/graphql', 'types' => null];
200
        }
201
202 3
        return $typesMappings;
203
    }
204
205 33
    private function detectFilesByTypes(ContainerBuilder $container, $path, $suffix, array $types = null)
206
    {
207
        // add the closest existing directory as a resource
208 33
        $resource = $path;
209 33
        while (!\is_dir($resource)) {
210 1
            $resource = \dirname($resource);
211
        }
212 33
        $container->addResource(new FileResource($resource));
213
214 33
        $stopOnFirstTypeMatching = empty($types);
215
216 33
        $types = $stopOnFirstTypeMatching ? \array_keys(self::SUPPORTED_TYPES_EXTENSIONS) : $types;
217 33
        $files = [];
218
219 33
        foreach ($types as $type) {
220 33
            $finder = Finder::create();
221
            try {
222 33
                $finder->files()->in($path)->name(\sprintf('*%s.%s', $suffix, self::SUPPORTED_TYPES_EXTENSIONS[$type]));
223 1
            } catch (\InvalidArgumentException $e) {
224 1
                continue;
225
            }
226 33
            if ($finder->count() > 0) {
227 33
                $files[] = [
228 33
                    'type' => $type,
229 33
                    'files' => $finder,
230
                ];
231 33
                if ($stopOnFirstTypeMatching) {
232 33
                    break;
233
                }
234
            }
235
        }
236
237 33
        return $files;
238
    }
239
240 31
    private function bundleDir($bundleClass)
241
    {
242 31
        $bundle = new \ReflectionClass($bundleClass);
243 31
        $bundleDir = \dirname($bundle->getFileName());
244
245 31
        return $bundleDir;
246
    }
247
248 32
    public function getAliasPrefix()
249
    {
250 32
        return 'overblog_graphql';
251
    }
252
253 32
    public function getAlias()
254
    {
255 32
        return $this->getAliasPrefix().'_types';
256
    }
257
258 37
    public function getConfiguration(array $config, ContainerBuilder $container)
259
    {
260 37
        return new TypesConfiguration();
261
    }
262
}
263