Passed
Branch 3.0.0 (0ebb76)
by Pieter
02:27
created

Apie::getDynamicSchemaLogic()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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