Completed
Push — master ( c34cac...3c68a7 )
by Pieter
04:47
created

Apie::getIdentifierExtractor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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