Passed
Push — master ( f87053...c5da41 )
by Pieter
04:26
created

Apie::getDescriptionExtractors()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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