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\ApiResourceMetadataFactory; |
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\Exceptions\BadConfigurationException; |
26
|
|
|
use W2w\Lib\Apie\Interfaces\ApiResourceFactoryInterface; |
27
|
|
|
use W2w\Lib\Apie\Interfaces\FormatRetrieverInterface; |
28
|
|
|
use W2w\Lib\Apie\Interfaces\ResourceSerializerInterface; |
29
|
|
|
use W2w\Lib\Apie\OpenApiSchema\OpenApiSpecGenerator; |
30
|
|
|
use W2w\Lib\Apie\OpenApiSchema\SchemaGenerator; |
31
|
|
|
use W2w\Lib\Apie\PluginInterfaces\AnnotationReaderProviderInterface; |
32
|
|
|
use W2w\Lib\Apie\PluginInterfaces\ApieAwareInterface; |
33
|
|
|
use W2w\Lib\Apie\PluginInterfaces\ApieConfigInterface; |
34
|
|
|
use W2w\Lib\Apie\PluginInterfaces\ApiResourceFactoryProviderInterface; |
35
|
|
|
use W2w\Lib\Apie\PluginInterfaces\CacheItemPoolProviderInterface; |
36
|
|
|
use W2w\Lib\Apie\PluginInterfaces\EncoderProviderInterface; |
37
|
|
|
use W2w\Lib\Apie\PluginInterfaces\NormalizerProviderInterface; |
38
|
|
|
use W2w\Lib\Apie\PluginInterfaces\ObjectAccessProviderInterface; |
39
|
|
|
use W2w\Lib\Apie\PluginInterfaces\OpenApiEventProviderInterface; |
40
|
|
|
use W2w\Lib\Apie\PluginInterfaces\OpenApiInfoProviderInterface; |
41
|
|
|
use W2w\Lib\Apie\PluginInterfaces\PropertyInfoExtractorProviderInterface; |
42
|
|
|
use W2w\Lib\Apie\PluginInterfaces\ResourceLifeCycleInterface; |
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
|
|
|
PropertyInfoExtractorProviderInterface |
|
|
|
|
67
|
|
|
{ |
68
|
|
|
const VERSION = "3.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
|
|
|
if (!is_callable([$serializer, 'decodeRequestBody'])) { |
169
|
|
|
@trigger_error( |
170
|
|
|
'Class ' . get_class($serializer) . ' has no method decodeRequestBody and this will be required in Apie version 4', |
171
|
|
|
E_USER_DEPRECATED |
172
|
|
|
); |
173
|
|
|
} |
174
|
|
|
return $serializer; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @internal |
179
|
|
|
* @deprecated is only added in CorePlugin, will be removed in 4.0. |
180
|
|
|
* @return iterable |
181
|
|
|
*/ |
182
|
|
|
public function getResourceLifecycles(): iterable |
183
|
|
|
{ |
184
|
|
|
return $this->pluginContainer->getPluginsWithInterface(ResourceLifeCycleInterface::class); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Returns a list of Api resources. |
189
|
|
|
* |
190
|
|
|
* @return string[] |
191
|
|
|
*/ |
192
|
|
|
public function getResources(): array |
193
|
|
|
{ |
194
|
|
|
return array_values(array_unique($this->pluginContainer->merge(ResourceProviderInterface::class, 'getResources'))); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @return NormalizerInterface[]|DenormalizerInterface[] |
199
|
|
|
*/ |
200
|
|
|
public function getNormalizers(): array |
201
|
|
|
{ |
202
|
|
|
return $this->pluginContainer->merge(NormalizerProviderInterface::class, 'getNormalizers'); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* @return EncoderInterface[]|DecoderInterface[] |
207
|
|
|
*/ |
208
|
|
|
public function getEncoders(): array |
209
|
|
|
{ |
210
|
|
|
return $this->pluginContainer->merge(EncoderProviderInterface::class, 'getEncoders'); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
public function getClassMetadataFactory(): ClassMetadataFactoryInterface |
214
|
|
|
{ |
215
|
|
|
return $this->pluginContainer->first( |
216
|
|
|
SymfonyComponentProviderInterface::class, |
217
|
|
|
new BadConfigurationException('I have no symfony component provider set up') |
218
|
|
|
)->getClassMetadataFactory(); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
public function getPropertyConverter(): NameConverterInterface |
222
|
|
|
{ |
223
|
|
|
return $this->pluginContainer->first( |
224
|
|
|
SymfonyComponentProviderInterface::class, |
225
|
|
|
new BadConfigurationException('I have no symfony component provider set up') |
226
|
|
|
)->getPropertyConverter(); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
public function getPropertyAccessor(): PropertyAccessor |
230
|
|
|
{ |
231
|
|
|
return $this->pluginContainer->first( |
232
|
|
|
SymfonyComponentProviderInterface::class, |
233
|
|
|
new BadConfigurationException('I have no symfony component provider set up') |
234
|
|
|
)->getPropertyAccessor(); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
public function getPropertyTypeExtractor(): PropertyTypeExtractorInterface |
238
|
|
|
{ |
239
|
|
|
return $this->pluginContainer->first( |
240
|
|
|
SymfonyComponentProviderInterface::class, |
241
|
|
|
new BadConfigurationException('I have no symfony component provider set up') |
242
|
|
|
)->getPropertyTypeExtractor(); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
public function getCacheItemPool(): CacheItemPoolInterface |
246
|
|
|
{ |
247
|
|
|
return $this->pluginContainer->first( |
248
|
|
|
CacheItemPoolProviderInterface::class, |
249
|
|
|
new BadConfigurationException('I have no cache item pool provider set up') |
250
|
|
|
)->getCacheItemPool(); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
public function getAnnotationReader(): Reader |
254
|
|
|
{ |
255
|
|
|
return $this->pluginContainer->first( |
256
|
|
|
AnnotationReaderProviderInterface::class, |
257
|
|
|
new BadConfigurationException('I have no annotation reader set up') |
258
|
|
|
)->getAnnotationReader(); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
public function getFormatRetriever(): FormatRetrieverInterface |
262
|
|
|
{ |
263
|
|
|
if (!$this->chainableFormatRetriever) { |
264
|
|
|
$this->chainableFormatRetriever = new ChainableFormatRetriever( |
265
|
|
|
iterator_to_array($this->pluginContainer->combine(EncoderProviderInterface::class, 'getFormatRetriever')) |
266
|
|
|
); |
267
|
|
|
} |
268
|
|
|
return $this->chainableFormatRetriever; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
public function getIdentifierExtractor(): IdentifierExtractor |
272
|
|
|
{ |
273
|
|
|
return $this->apieCore->getIdentifierExtractor(); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
public function getApiResourceMetadataFactory(): ApiResourceMetadataFactory |
277
|
|
|
{ |
278
|
|
|
return $this->apieCore->getApiResourceMetadataFactory(); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
public function getApiResourceFacade(): ApiResourceFacade |
282
|
|
|
{ |
283
|
|
|
return $this->apieCore->getApiResourceFacade(); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
public function getOpenApiSpecGenerator(): OpenApiSpecGenerator |
287
|
|
|
{ |
288
|
|
|
return $this->apieCore->getOpenApiSpecGenerator(); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
public function getSchemaGenerator(): SchemaGenerator |
292
|
|
|
{ |
293
|
|
|
return $this->apieCore->getSchemaGenerator(); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
public function getApiResourceFactory(): ApiResourceFactoryInterface |
297
|
|
|
{ |
298
|
|
|
if (!$this->chainableFactory) { |
299
|
|
|
$this->chainableFactory = new ChainableFactory( |
300
|
|
|
iterator_to_array($this->pluginContainer->combine(ApiResourceFactoryProviderInterface::class, 'getApiResourceFactory')) |
301
|
|
|
); |
302
|
|
|
} |
303
|
|
|
return $this->chainableFactory; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
public function createInfo(): Info |
307
|
|
|
{ |
308
|
|
|
$res = $this->pluginContainer->first(OpenApiInfoProviderInterface::class, null); |
309
|
|
|
|
310
|
|
|
if (empty($res)) { |
311
|
|
|
return new Info('Apie', Apie::VERSION); |
312
|
|
|
} |
313
|
|
|
return $res->createInfo(); |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
public function getBaseUrl(): string |
317
|
|
|
{ |
318
|
|
|
return $this->pluginContainer->first(ApieConfigInterface::class, new BadConfigurationException('I have no config set up'))->getBaseUrl(); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
public function getDefinedStaticData(): array |
322
|
|
|
{ |
323
|
|
|
$result = []; |
324
|
|
|
foreach (array_reverse($this->pluginContainer->getPluginsWithInterface(SchemaProviderInterface::class)) as $schemaDefinition) { |
325
|
|
|
foreach ($schemaDefinition->getDefinedStaticData() as $className => $schema) { |
326
|
|
|
$result[$className] = $schema; |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
return $result; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
public function getDynamicSchemaLogic(): array |
333
|
|
|
{ |
334
|
|
|
$result = []; |
335
|
|
|
foreach (array_reverse($this->pluginContainer->getPluginsWithInterface(SchemaProviderInterface::class)) as $schemaDefinition) { |
336
|
|
|
foreach ($schemaDefinition->getDynamicSchemaLogic() as $className => $callable) { |
337
|
|
|
$result[$className] = $callable; |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
return $result; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
public function getClassResourceConverter(): ClassResourceConverter |
344
|
|
|
{ |
345
|
|
|
return $this->apieCore->getClassResourceConverter(); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
public function onOpenApiDocGenerated(Document $document): Document |
349
|
|
|
{ |
350
|
|
|
$this->pluginContainer->each(OpenApiEventProviderInterface::class, function (OpenApiEventProviderInterface $plugin) use (&$document) { |
351
|
|
|
$document = $plugin->onOpenApiDocGenerated($document); |
352
|
|
|
}); |
353
|
|
|
return $document; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* @deprecated use getObjectAccess instead |
358
|
|
|
*/ |
359
|
|
|
public function getListExtractors(): array |
360
|
|
|
{ |
361
|
|
|
return $this->pluginContainer->merge(PropertyInfoExtractorProviderInterface::class, 'getListExtractors'); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* @deprecated use getObjectAccess instead |
366
|
|
|
*/ |
367
|
|
|
public function getTypeExtractors(): array |
368
|
|
|
{ |
369
|
|
|
return $this->pluginContainer->merge(PropertyInfoExtractorProviderInterface::class, 'getTypeExtractors'); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* @deprecated use getObjectAccess instead |
374
|
|
|
*/ |
375
|
|
|
public function getDescriptionExtractors(): array |
376
|
|
|
{ |
377
|
|
|
return $this->pluginContainer->merge(PropertyInfoExtractorProviderInterface::class, 'getDescriptionExtractors'); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* @deprecated use getObjectAccess instead |
382
|
|
|
*/ |
383
|
|
|
public function getAccessExtractors(): array |
384
|
|
|
{ |
385
|
|
|
return $this->pluginContainer->merge(PropertyInfoExtractorProviderInterface::class, 'getAccessExtractors'); |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* @deprecated use getObjectAccess instead |
390
|
|
|
*/ |
391
|
|
|
public function getInitializableExtractors(): array |
392
|
|
|
{ |
393
|
|
|
return $this->pluginContainer->merge(PropertyInfoExtractorProviderInterface::class, 'getInitializableExtractors'); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
public function getObjectAccess(): ObjectAccessInterface |
397
|
|
|
{ |
398
|
|
|
$objectAccess = new ObjectAccess(); |
399
|
|
|
$objectAccesses = $this->pluginContainer->getPluginsWithInterface(ObjectAccessProviderInterface::class); |
400
|
|
|
if (!empty($objectAccesses)) { |
401
|
|
|
$list = []; |
402
|
|
|
foreach ($objectAccesses as $objectAccessPlugin) { |
403
|
|
|
$list = array_merge($list, $objectAccessPlugin->getObjectAccesses()); |
404
|
|
|
} |
405
|
|
|
$objectAccess = new GroupedObjectAccess($objectAccess, $list); |
406
|
|
|
} |
407
|
|
|
if (!$this->debug && $this->cacheFolder) { |
408
|
|
|
return new CachedObjectAccess($objectAccess, $this->getCacheItemPool()); |
409
|
|
|
} |
410
|
|
|
return $objectAccess; |
411
|
|
|
} |
412
|
|
|
} |
413
|
|
|
|
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.