Passed
Branch 3.3 (0d0269)
by Pieter
02:33
created

Apie::getObjectAccess()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 9
nc 4
nop 0
dl 0
loc 14
rs 9.6111
c 0
b 0
f 0
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\ClassResourceConverter;
20
use W2w\Lib\Apie\Core\Encodings\ChainableFormatRetriever;
21
use W2w\Lib\Apie\Core\IdentifierExtractor;
22
use W2w\Lib\Apie\Core\ResourceFactories\ChainableFactory;
23
use W2w\Lib\Apie\Exceptions\BadConfigurationException;
24
use W2w\Lib\Apie\Exceptions\NotAnApiePluginException;
25
use W2w\Lib\Apie\Interfaces\ApiResourceFactoryInterface;
26
use W2w\Lib\Apie\Interfaces\FormatRetrieverInterface;
27
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface;
28
use W2w\Lib\Apie\OpenApiSchema\OpenApiSpecGenerator;
29
use W2w\Lib\Apie\OpenApiSchema\SchemaGenerator;
30
use W2w\Lib\Apie\PluginInterfaces\AnnotationReaderProviderInterface;
31
use W2w\Lib\Apie\PluginInterfaces\ApieAwareInterface;
32
use W2w\Lib\Apie\PluginInterfaces\ApieConfigInterface;
33
use W2w\Lib\Apie\PluginInterfaces\ApiResourceFactoryProviderInterface;
34
use W2w\Lib\Apie\PluginInterfaces\CacheItemPoolProviderInterface;
35
use W2w\Lib\Apie\PluginInterfaces\EncoderProviderInterface;
36
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface;
37
use W2w\Lib\Apie\PluginInterfaces\ObjectAccessProviderInterface;
38
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface;
39
use W2w\Lib\Apie\PluginInterfaces\OpenApiInfoProviderInterface;
40
use W2w\Lib\Apie\PluginInterfaces\PropertyInfoExtractorProviderInterface;
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\ApieObjectAccessNormalizer\ObjectAccess\CachedObjectAccess;
47
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\GroupedObjectAccess;
48
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccess;
49
use W2w\Lib\ApieObjectAccessNormalizer\ObjectAccess\ObjectAccessInterface;
50
51
final class Apie implements SerializerProviderInterface,
52
    ResourceProviderInterface,
53
    NormalizerProviderInterface,
54
    EncoderProviderInterface,
55
    SymfonyComponentProviderInterface,
56
    CacheItemPoolProviderInterface,
57
    AnnotationReaderProviderInterface,
58
    ApiResourceFactoryProviderInterface,
59
    OpenApiInfoProviderInterface,
60
    ApieConfigInterface,
61
    SchemaProviderInterface,
62
    OpenApiEventProviderInterface,
63
    PropertyInfoExtractorProviderInterface
0 ignored issues
show
Deprecated Code introduced by
The interface W2w\Lib\Apie\PluginInter...ractorProviderInterface has been deprecated: use ObjectAccessProviderInterface instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

63
    /** @scrutinizer ignore-deprecated */ PropertyInfoExtractorProviderInterface

This interface has been deprecated. The supplier of the interface has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.

Loading history...
64
{
65
    const VERSION = "3.0";
66
67
    /**
68
     * @var bool
69
     */
70
    private $debug;
71
72
    /**
73
     * @var string|null
74
     */
75
    private $cacheFolder;
76
77
    /**
78
     * @var SerializerProviderInterface[]
79
     */
80
    private $serializers = [];
81
82
    /**
83
     * @var ResourceProviderInterface[]
84
     */
85
    private $resources = [];
86
87
    /**
88
     * @var NormalizerProviderInterface[]
89
     */
90
    private $normalizers = [];
91
92
    /**
93
     * @var EncoderProviderInterface[]
94
     */
95
    private $encoders = [];
96
97
    /**
98
     * @var SymfonyComponentProviderInterface[]
99
     */
100
    private $symfonyComponents = [];
101
102
    /**
103
     * @var CacheItemPoolProviderInterface[]
104
     */
105
    private $cacheItemPools = [];
106
107
    /**
108
     * @var object[]
109
     */
110
    private $plugins = [];
111
112
    /**
113
     * @var AnnotationReaderProviderInterface[]
114
     */
115
    private $annotationReaders = [];
116
117
    /**
118
     * @var ApiResourceFactoryProviderInterface[]
119
     */
120
    private $apiResourceFactories = [];
121
122
    /**
123
     * @var OpenApiInfoProviderInterface[]
124
     */
125
    private $openApiInfoProviders = [];
126
127
    /**
128
     * @var ApieConfigInterface[]
129
     */
130
    private $configs = [];
131
132
    /**
133
     * @var SchemaProviderInterface[]
134
     */
135
    private $schemaDefinitions = [];
136
137
    /**
138
     * @var ApieCore
139
     */
140
    private $apieCore;
141
142
    /**
143
     * @var ChainableFactory|null
144
     */
145
    private $chainableFactory;
146
147
    /**
148
     * @var ChainableFormatRetriever|null
149
     */
150
    private $chainableFormatRetriever;
151
152
    /**
153
     * @var OpenApiEventProviderInterface[]
154
     */
155
    private $openApiEventProviders = [];
156
157
    /**
158
     * @var ObjectAccessProviderInterface[]
159
     */
160
    private $objectAccesses = [];
161
162
    /**
163
     * @var PropertyInfoExtractorProviderInterface[]
164
     */
165
    private $propertyInfoExtractors = [];
166
167
    /**
168
169
     * @param object[] $plugins
170
     * @param bool $debug
171
     * @param string|null $cacheFolder
172
     * @param bool $addCorePlugin
173
     */
174
    public function __construct(array $plugins, bool $debug = false, ?string $cacheFolder = null, bool $addCorePlugin = true)
175
    {
176
        $this->debug = $debug;
177
        $this->cacheFolder = $cacheFolder;
178
        static $mapping = [
179
            SerializerProviderInterface::class => 'serializers',
180
            ResourceProviderInterface::class => 'resources',
181
            NormalizerProviderInterface::class => 'normalizers',
182
            EncoderProviderInterface::class => 'encoders',
183
            SymfonyComponentProviderInterface::class => 'symfonyComponents',
184
            CacheItemPoolProviderInterface::class => 'cacheItemPools',
185
            AnnotationReaderProviderInterface::class => 'annotationReaders',
186
            ApiResourceFactoryProviderInterface::class => 'apiResourceFactories',
187
            OpenApiInfoProviderInterface::class => 'openApiInfoProviders',
188
            ApieConfigInterface::class => 'configs',
189
            SchemaProviderInterface::class => 'schemaDefinitions',
190
            OpenApiEventProviderInterface::class => 'openApiEventProviders',
191
            ObjectAccessProviderInterface::class => 'objectAccesses',
192
            PropertyInfoExtractorProviderInterface::class => 'propertyInfoExtractors',
193
        ];
194
        if ($addCorePlugin) {
195
            $plugins[] = new CorePlugin();
196
        }
197
        $this->plugins = $plugins;
198
        foreach ($plugins as $plugin) {
199
            $isUsed = false;
200
            foreach ($mapping as $className => $propertyName) {
201
                if ($plugin instanceof $className) {
202
                    $this->$propertyName[] = $plugin;
203
                    if (!$isUsed && $plugin instanceof ApieAwareInterface) {
204
                        $plugin->setApie($this);
205
                    }
206
                    $isUsed = true;
207
                }
208
            }
209
            if (!$isUsed) {
210
                throw new NotAnApiePluginException($plugin);
211
            }
212
        }
213
        $this->apieCore = new ApieCore($this);
214
    }
215
216
    /**
217
     * Creates a new instance of Apie reusing the services/instances of the current Apie instance.
218
     *
219
     * @param array $plugins
220
     * @return Apie
221
     */
222
    public function createContext(array $plugins = []): self
223
    {
224
        $plugins[] = $this;
225
        return new Apie($plugins, $this->debug, $this->cacheFolder, false);
226
    }
227
228
    /**
229
     * @return bool
230
     */
231
    public function isDebug(): bool
232
    {
233
        return $this->debug;
234
    }
235
236
    /**
237
     * @return string|null
238
     */
239
    public function getCacheFolder(): ?string
240
    {
241
        return $this->cacheFolder;
242
    }
243
244
    public function getPlugin(string $pluginClass): object
245
    {
246
        $last = null;
247
        foreach ($this->plugins as $plugin) {
248
            if ($plugin instanceof $pluginClass) {
249
                return $plugin;
250
            }
251
            $last = $plugin;
252
        }
253
        if ($last instanceof self) {
254
            return $last->getPlugin($pluginClass);
255
        }
256
        throw new BadConfigurationException('Plugin ' . $pluginClass . ' not found!');
257
    }
258
259
    /**
260
     * @return ResourceSerializerInterface
261
     */
262
    public function getResourceSerializer(): ResourceSerializerInterface
263
    {
264
        if (empty($this->serializers)) {
265
            throw new BadConfigurationException('I have no resource serializer set up');
266
        }
267
        return reset($this->serializers)->getResourceSerializer();
268
    }
269
270
    /**
271
     * Returns a list of Api resources.
272
     *
273
     * @return string[]
274
     */
275
    public function getResources(): array
276
    {
277
        $resources = [];
278
        foreach ($this->resources as $resourceProvider) {
279
            $resources = array_merge($resources, $resourceProvider->getResources());
280
        }
281
        return array_values(array_unique($resources));
282
    }
283
284
    /**
285
     * @return NormalizerInterface[]|DenormalizerInterface[]
286
     */
287
    public function getNormalizers(): array
288
    {
289
        $normalizers = [];
290
        foreach ($this->normalizers as $normalizerProvider) {
291
            $normalizers = array_merge($normalizers, $normalizerProvider->getNormalizers());
292
        }
293
        return $normalizers;
294
    }
295
296
    /**
297
     * @return EncoderInterface[]|DecoderInterface[]
298
     */
299
    public function getEncoders(): array
300
    {
301
        $encoders = [];
302
        foreach ($this->encoders as $encoderProvider) {
303
            $encoders = array_merge($encoders, $encoderProvider->getEncoders());
304
        }
305
        return $encoders;
306
    }
307
308
    public function getClassMetadataFactory(): ClassMetadataFactoryInterface
309
    {
310
        if (empty($this->symfonyComponents)) {
311
            throw new BadConfigurationException('I have no symfony component provider set up');
312
        }
313
        return reset($this->symfonyComponents)->getClassMetadataFactory();
314
    }
315
316
    public function getPropertyConverter(): NameConverterInterface
317
    {
318
        if (empty($this->symfonyComponents)) {
319
            throw new BadConfigurationException('I have no symfony component provider set up');
320
        }
321
        return reset($this->symfonyComponents)->getPropertyConverter();
322
    }
323
324
    public function getPropertyAccessor(): PropertyAccessor
325
    {
326
        if (empty($this->symfonyComponents)) {
327
            throw new BadConfigurationException('I have no symfony component provider set up');
328
        }
329
        return reset($this->symfonyComponents)->getPropertyAccessor();
330
    }
331
332
    public function getPropertyTypeExtractor(): PropertyTypeExtractorInterface
333
    {
334
        if (empty($this->symfonyComponents)) {
335
            throw new BadConfigurationException('I have no symfony component provider set up');
336
        }
337
        return reset($this->symfonyComponents)->getPropertyTypeExtractor();
338
    }
339
340
    public function getCacheItemPool(): CacheItemPoolInterface
341
    {
342
        if (empty($this->cacheItemPools)) {
343
            throw new BadConfigurationException('I have no cache item pool provider set up');
344
        }
345
        return reset($this->cacheItemPools)->getCacheItemPool();
346
    }
347
348
    public function getAnnotationReader(): Reader
349
    {
350
        if (empty($this->annotationReaders)) {
351
            throw new BadConfigurationException('I have no annotation reader set up');
352
        }
353
        return reset($this->annotationReaders)->getAnnotationReader();
354
    }
355
356
    public function getFormatRetriever(): FormatRetrieverInterface
357
    {
358
        if (!$this->chainableFormatRetriever) {
359
            $this->chainableFormatRetriever = new ChainableFormatRetriever(
360
                array_map(
361
                    function (EncoderProviderInterface $encoderProvider) {
362
                        return $encoderProvider->getFormatRetriever();
363
                    },
364
                    $this->encoders
365
                )
366
            );
367
        }
368
        return $this->chainableFormatRetriever;
369
    }
370
371
    public function getIdentifierExtractor(): IdentifierExtractor
372
    {
373
        return $this->apieCore->getIdentifierExtractor();
374
    }
375
376
    public function getApiResourceFacade(): ApiResourceFacade
377
    {
378
        return $this->apieCore->getApiResourceFacade();
379
    }
380
381
    public function getOpenApiSpecGenerator(): OpenApiSpecGenerator
382
    {
383
        return $this->apieCore->getOpenApiSpecGenerator();
384
    }
385
386
    public function getSchemaGenerator(): SchemaGenerator
387
    {
388
        return $this->apieCore->getSchemaGenerator();
389
    }
390
391
    public function getApiResourceFactory(): ApiResourceFactoryInterface
392
    {
393
        if (!$this->chainableFactory) {
394
            $this->chainableFactory = new ChainableFactory(
395
                array_map(
396
                    function (ApiResourceFactoryProviderInterface $factoryProvider) {
397
                        return $factoryProvider->getApiResourceFactory();
398
                    },
399
                    $this->apiResourceFactories
400
                )
401
            );
402
        }
403
        return $this->chainableFactory;
404
    }
405
406
    public function createInfo(): Info
407
    {
408
        if (empty($this->openApiInfoProviders)) {
409
            return new Info('Apie', Apie::VERSION);
410
        }
411
        return reset($this->openApiInfoProviders)->createInfo();
412
    }
413
414
    public function getBaseUrl(): string
415
    {
416
        if (empty($this->configs)) {
417
            throw new BadConfigurationException('I have no config set up');
418
        }
419
        return reset($this->configs)->getBaseUrl();
420
    }
421
422
    public function getDefinedStaticData(): array
423
    {
424
        $result = [];
425
        foreach (array_reverse($this->schemaDefinitions) as $schemaDefinition) {
426
            foreach ($schemaDefinition->getDefinedStaticData() as $className => $schema) {
427
                $result[$className] = $schema;
428
            }
429
        }
430
        return $result;
431
    }
432
433
    public function getDynamicSchemaLogic(): array
434
    {
435
        $result = [];
436
        foreach (array_reverse($this->schemaDefinitions) as $schemaDefinition) {
437
            foreach ($schemaDefinition->getDynamicSchemaLogic() as $className => $callable) {
438
                $result[$className] = $callable;
439
            }
440
        }
441
        return $result;
442
    }
443
444
    public function getClassResourceConverter(): ClassResourceConverter
445
    {
446
        return $this->apieCore->getClassResourceConverter();
447
    }
448
449
    public function onOpenApiDocGenerated(Document $document): Document
450
    {
451
        foreach ($this->openApiEventProviders as $openApiEventProvider) {
452
            $document = $openApiEventProvider->onOpenApiDocGenerated($document);
453
        }
454
        return $document;
455
    }
456
457
    public function getListExtractors(): array
458
    {
459
        $result = [];
460
        foreach ($this->propertyInfoExtractors as $extractor) {
461
            $result  = $result + $extractor->getListExtractors();
462
        }
463
        return $result;
464
    }
465
466
    public function getTypeExtractors(): array
467
    {
468
        $result = [];
469
        foreach ($this->propertyInfoExtractors as $extractor) {
470
            $result  = $result + $extractor->getTypeExtractors();
471
        }
472
        return $result;
473
    }
474
475
    public function getDescriptionExtractors(): array
476
    {
477
        $result = [];
478
        foreach ($this->propertyInfoExtractors as $extractor) {
479
            $result  = $result + $extractor->getDescriptionExtractors();
480
        }
481
        return $result;
482
    }
483
484
    public function getAccessExtractors(): array
485
    {
486
        $result = [];
487
        foreach ($this->propertyInfoExtractors as $extractor) {
488
            $result  = $result + $extractor->getAccessExtractors();
489
        }
490
        return $result;
491
    }
492
493
    public function getInitializableExtractors(): array
494
    {
495
        $result = [];
496
        foreach ($this->propertyInfoExtractors as $extractor) {
497
            $result  = $result + $extractor->getInitializableExtractors();
498
        }
499
        return $result;
500
    }
501
502
    public function getObjectAccess(): ObjectAccessInterface
503
    {
504
        $objectAccess = new ObjectAccess();
505
        if (!empty($this->objectAccesses)) {
506
            $list = [];
507
            foreach ($this->objectAccesses as $objectAccess) {
508
                $list = array_merge($list, $objectAccess->getObjectAccesses());
509
            }
510
            $objectAccess = new GroupedObjectAccess($objectAccess, $list);
511
        }
512
        if (!$this->debug && $this->cacheFolder) {
513
            return new CachedObjectAccess($objectAccess, $this->getCacheItemPool());
514
        }
515
        return $objectAccess;
516
    }
517
}
518