Passed
Branch v4 (c30cb0)
by Pieter
03:55
created

Apie   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 309
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 111
c 1
b 0
f 0
dl 0
loc 309
rs 9.0399
wmc 42

30 Methods

Rating   Name   Duplication   Size   Complexity  
A getApiResourceFactory() 0 8 2
A getCacheFolder() 0 3 1
A getResourceSerializer() 0 7 1
A getPropertyAccessor() 0 6 1
A getResources() 0 3 1
A getPluginsWithInterface() 0 3 1
A getPlugin() 0 3 1
A getNormalizers() 0 3 1
A isDebug() 0 3 1
A getEncoders() 0 3 1
A getDynamicSchemaLogic() 0 9 3
A getAnnotationReader() 0 6 1
A getDefinedStaticData() 0 9 3
A getPropertyConverter() 0 6 1
A onOpenApiDocGenerated() 0 6 1
A getOpenApiSpecGenerator() 0 3 1
A getBaseUrl() 0 3 1
A getClassMetadataFactory() 0 6 1
A getFormatRetriever() 0 8 2
A getSchemaGenerator() 0 3 1
A createContext() 0 5 1
A getObjectAccess() 0 15 5
A getApiResourceFacade() 0 3 1
A getIdentifierExtractor() 0 3 1
A createInfo() 0 8 2
A getApiResourceMetadataFactory() 0 3 1
A getClassResourceConverter() 0 3 1
A getPropertyTypeExtractor() 0 6 1
A __construct() 0 16 2
A getCacheItemPool() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like Apie often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Apie, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace W2w\Lib\Apie;
4
5
use Doctrine\Common\Annotations\Reader;
6
use erasys\OpenApi\Spec\v3\Document;
7
use erasys\OpenApi\Spec\v3\Info;
8
use Psr\Cache\CacheItemPoolInterface;
9
use Symfony\Component\PropertyAccess\PropertyAccessor;
10
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
11
use Symfony\Component\Serializer\Encoder\DecoderInterface;
12
use Symfony\Component\Serializer\Encoder\EncoderInterface;
13
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
14
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
15
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
16
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
17
use W2w\Lib\Apie\Core\ApieCore;
18
use W2w\Lib\Apie\Core\ApiResourceFacade;
19
use W2w\Lib\Apie\Core\ApiResourceMetadataFactory;
20
use W2w\Lib\Apie\Core\ClassResourceConverter;
21
use W2w\Lib\Apie\Core\Encodings\ChainableFormatRetriever;
22
use W2w\Lib\Apie\Core\IdentifierExtractor;
23
use W2w\Lib\Apie\Core\PluginContainer;
24
use W2w\Lib\Apie\Core\ResourceFactories\ChainableFactory;
25
use W2w\Lib\Apie\Exceptions\BadConfigurationException;
26
use W2w\Lib\Apie\Interfaces\ApiResourceFactoryInterface;
27
use W2w\Lib\Apie\Interfaces\FormatRetrieverInterface;
28
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface;
29
use W2w\Lib\Apie\OpenApiSchema\OpenApiSchemaGenerator;
30
use W2w\Lib\Apie\OpenApiSchema\OpenApiSpecGenerator;
31
use W2w\Lib\Apie\PluginInterfaces\AnnotationReaderProviderInterface;
32
use W2w\Lib\Apie\PluginInterfaces\ApieAwareInterface;
33
use W2w\Lib\Apie\PluginInterfaces\ApieConfigInterface;
34
use W2w\Lib\Apie\PluginInterfaces\ApiResourceFactoryProviderInterface;
35
use W2w\Lib\Apie\PluginInterfaces\CacheItemPoolProviderInterface;
36
use W2w\Lib\Apie\PluginInterfaces\EncoderProviderInterface;
37
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface;
38
use W2w\Lib\Apie\PluginInterfaces\ObjectAccessProviderInterface;
39
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface;
40
use W2w\Lib\Apie\PluginInterfaces\OpenApiInfoProviderInterface;
41
use W2w\Lib\Apie\PluginInterfaces\ResourceProviderInterface;
42
use W2w\Lib\Apie\PluginInterfaces\SchemaProviderInterface;
43
use W2w\Lib\Apie\PluginInterfaces\SerializerProviderInterface;
44
use W2w\Lib\Apie\PluginInterfaces\SymfonyComponentProviderInterface;
45
use W2w\Lib\Apie\Plugins\Core\CorePlugin;
46
use W2w\Lib\Apie\Plugins\PrimaryKey\PrimaryKeyPlugin;
47
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\CachedObjectAccess;
48
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\GroupedObjectAccess;
49
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccess;
50
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccessInterface;
51
52
final class Apie implements SerializerProviderInterface,
53
    ResourceProviderInterface,
54
    NormalizerProviderInterface,
55
    EncoderProviderInterface,
56
    SymfonyComponentProviderInterface,
57
    CacheItemPoolProviderInterface,
58
    AnnotationReaderProviderInterface,
59
    ApiResourceFactoryProviderInterface,
60
    OpenApiInfoProviderInterface,
61
    ApieConfigInterface,
62
    SchemaProviderInterface,
63
    OpenApiEventProviderInterface
64
{
65
    const VERSION = "4.0";
66
67
    /**
68
     * @var bool
69
     */
70
    private $debug;
71
72
    /**
73
     * @var string|null
74
     */
75
    private $cacheFolder;
76
77
    /**
78
     * @var PluginContainer
79
     */
80
    private $pluginContainer;
81
82
    /**
83
     * @var ApieCore
84
     */
85
    private $apieCore;
86
87
    /**
88
     * @var ChainableFactory|null
89
     */
90
    private $chainableFactory;
91
92
    /**
93
     * @var ChainableFormatRetriever|null
94
     */
95
    private $chainableFormatRetriever;
96
97
    /**
98
99
     * @param object[] $plugins
100
     * @param bool $debug
101
     * @param string|null $cacheFolder
102
     * @param bool $addCorePlugin
103
     */
104
    public function __construct(array $plugins, bool $debug = false, ?string $cacheFolder = null, bool $addCorePlugin = true)
105
    {
106
        $this->debug = $debug;
107
        $this->cacheFolder = $cacheFolder;
108
        if ($addCorePlugin) {
109
            $plugins[] = new PrimaryKeyPlugin();
110
            $plugins[] = new CorePlugin();
111
        }
112
        $this->pluginContainer = new PluginContainer($plugins);
113
        $this->pluginContainer->each(
114
            ApieAwareInterface::class,
115
            function (ApieAwareInterface $plugin) {
116
                $plugin->setApie($this);
117
            }
118
        );
119
        $this->apieCore = new ApieCore($this, $this->pluginContainer);
120
    }
121
122
    /**
123
     * Creates a new instance of Apie reusing the services/instances of the current Apie instance.
124
     *
125
     * @param array $plugins
126
     * @return Apie
127
     */
128
    public function createContext(array $plugins = []): self
129
    {
130
        $plugins[] = new PrimaryKeyPlugin();
131
        $plugins[] = $this;
132
        return new Apie($plugins, $this->debug, $this->cacheFolder, false);
133
    }
134
135
    /**
136
     * @return bool
137
     */
138
    public function isDebug(): bool
139
    {
140
        return $this->debug;
141
    }
142
143
    /**
144
     * @return string|null
145
     */
146
    public function getCacheFolder(): ?string
147
    {
148
        return $this->cacheFolder;
149
    }
150
151
    public function getPlugin(string $pluginClass): object
152
    {
153
        return $this->pluginContainer->getPlugin($pluginClass);
154
    }
155
156
    /**
157
     * @return ResourceSerializerInterface
158
     */
159
    public function getResourceSerializer(): ResourceSerializerInterface
160
    {
161
        $serializer = $this->pluginContainer->first(
162
            SerializerProviderInterface::class,
163
            new BadConfigurationException('I have no resource serializer set up')
164
        )->getResourceSerializer();
165
        return $serializer;
166
    }
167
168
    /**
169
     * Returns a list of Api resources.
170
     *
171
     * @return string[]
172
     */
173
    public function getResources(): array
174
    {
175
        return array_values(array_unique($this->pluginContainer->merge(ResourceProviderInterface::class, 'getResources')));
176
    }
177
178
    /**
179
     * @return NormalizerInterface[]|DenormalizerInterface[]
180
     */
181
    public function getNormalizers(): array
182
    {
183
        return $this->pluginContainer->merge(NormalizerProviderInterface::class, 'getNormalizers');
184
    }
185
186
    /**
187
     * @return EncoderInterface[]|DecoderInterface[]
188
     */
189
    public function getEncoders(): array
190
    {
191
        return $this->pluginContainer->merge(EncoderProviderInterface::class, 'getEncoders');
192
    }
193
194
    /**
195
     * @param string $interface
196
     * @return mixed[]
197
     */
198
    public function getPluginsWithInterface(string $interface): array
199
    {
200
        return $this->pluginContainer->getPluginsWithInterface($interface);
201
    }
202
203
    public function getClassMetadataFactory(): ClassMetadataFactoryInterface
204
    {
205
        return $this->pluginContainer->first(
206
            SymfonyComponentProviderInterface::class,
207
            new BadConfigurationException('I have no symfony component provider set up')
208
        )->getClassMetadataFactory();
209
    }
210
211
    public function getPropertyConverter(): NameConverterInterface
212
    {
213
        return $this->pluginContainer->first(
214
            SymfonyComponentProviderInterface::class,
215
            new BadConfigurationException('I have no symfony component provider set up')
216
        )->getPropertyConverter();
217
    }
218
219
    public function getPropertyAccessor(): PropertyAccessor
220
    {
221
        return $this->pluginContainer->first(
222
            SymfonyComponentProviderInterface::class,
223
            new BadConfigurationException('I have no symfony component provider set up')
224
        )->getPropertyAccessor();
225
    }
226
227
    public function getPropertyTypeExtractor(): PropertyTypeExtractorInterface
228
    {
229
        return $this->pluginContainer->first(
230
            SymfonyComponentProviderInterface::class,
231
            new BadConfigurationException('I have no symfony component provider set up')
232
        )->getPropertyTypeExtractor();
233
    }
234
235
    public function getCacheItemPool(): CacheItemPoolInterface
236
    {
237
        return $this->pluginContainer->first(
238
            CacheItemPoolProviderInterface::class,
239
            new BadConfigurationException('I have no cache item pool provider set up')
240
        )->getCacheItemPool();
241
    }
242
243
    public function getAnnotationReader(): Reader
244
    {
245
        return $this->pluginContainer->first(
246
            AnnotationReaderProviderInterface::class,
247
            new BadConfigurationException('I have no annotation reader set up')
248
        )->getAnnotationReader();
249
    }
250
251
    public function getFormatRetriever(): FormatRetrieverInterface
252
    {
253
        if (!$this->chainableFormatRetriever) {
254
            $this->chainableFormatRetriever = new ChainableFormatRetriever(
255
                iterator_to_array($this->pluginContainer->combine(EncoderProviderInterface::class, 'getFormatRetriever'))
256
            );
257
        }
258
        return $this->chainableFormatRetriever;
259
    }
260
261
    public function getIdentifierExtractor(): IdentifierExtractor
262
    {
263
        return $this->apieCore->getIdentifierExtractor();
264
    }
265
266
    public function getApiResourceMetadataFactory(): ApiResourceMetadataFactory
267
    {
268
        return $this->apieCore->getApiResourceMetadataFactory();
269
    }
270
271
    public function getApiResourceFacade(): ApiResourceFacade
272
    {
273
        return $this->apieCore->getApiResourceFacade();
274
    }
275
276
    public function getOpenApiSpecGenerator(): OpenApiSpecGenerator
277
    {
278
        return $this->apieCore->getOpenApiSpecGenerator();
279
    }
280
281
    public function getSchemaGenerator(): OpenApiSchemaGenerator
282
    {
283
        return $this->apieCore->getSchemaGenerator();
284
    }
285
286
    public function getApiResourceFactory(): ApiResourceFactoryInterface
287
    {
288
        if (!$this->chainableFactory) {
289
            $this->chainableFactory = new ChainableFactory(
290
                iterator_to_array($this->pluginContainer->combine(ApiResourceFactoryProviderInterface::class, 'getApiResourceFactory'))
291
            );
292
        }
293
        return $this->chainableFactory;
294
    }
295
296
    public function createInfo(): Info
297
    {
298
        $res = $this->pluginContainer->first(OpenApiInfoProviderInterface::class, null);
299
300
        if (empty($res)) {
301
            return new Info('Apie', Apie::VERSION);
302
        }
303
        return $res->createInfo();
304
    }
305
306
    public function getBaseUrl(): string
307
    {
308
        return $this->pluginContainer->first(ApieConfigInterface::class, new BadConfigurationException('I have no config set up'))->getBaseUrl();
309
    }
310
311
    public function getDefinedStaticData(): array
312
    {
313
        $result = [];
314
        foreach (array_reverse($this->pluginContainer->getPluginsWithInterface(SchemaProviderInterface::class)) as $schemaDefinition) {
315
            foreach ($schemaDefinition->getDefinedStaticData() as $className => $schema) {
316
                $result[$className] = $schema;
317
            }
318
        }
319
        return $result;
320
    }
321
322
    public function getDynamicSchemaLogic(): array
323
    {
324
        $result = [];
325
        foreach (array_reverse($this->pluginContainer->getPluginsWithInterface(SchemaProviderInterface::class)) as $schemaDefinition) {
326
            foreach ($schemaDefinition->getDynamicSchemaLogic() as $className => $callable) {
327
                $result[$className] = $callable;
328
            }
329
        }
330
        return $result;
331
    }
332
333
    public function getClassResourceConverter(): ClassResourceConverter
334
    {
335
        return $this->apieCore->getClassResourceConverter();
336
    }
337
338
    public function onOpenApiDocGenerated(Document $document): Document
339
    {
340
        $this->pluginContainer->each(OpenApiEventProviderInterface::class, function (OpenApiEventProviderInterface $plugin) use (&$document) {
341
            $document = $plugin->onOpenApiDocGenerated($document);
342
        });
343
        return $document;
344
    }
345
346
     public function getObjectAccess(): ObjectAccessInterface
347
    {
348
        $objectAccess = new ObjectAccess();
349
        $objectAccesses = $this->pluginContainer->getPluginsWithInterface(ObjectAccessProviderInterface::class);
350
        if (!empty($objectAccesses)) {
351
            $list = [];
352
            foreach ($objectAccesses as $objectAccessPlugin) {
353
                $list = array_merge($list, $objectAccessPlugin->getObjectAccesses());
354
            }
355
            $objectAccess = new GroupedObjectAccess($objectAccess, $list);
356
        }
357
        if (!$this->debug && $this->cacheFolder) {
358
            return new CachedObjectAccess($objectAccess, $this->getCacheItemPool());
359
        }
360
        return $objectAccess;
361
    }
362
}
363