Apie   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 333
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 114
dl 0
loc 333
rs 8.64
c 1
b 0
f 0
wmc 47

35 Methods

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