• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 8658338405

12 Apr 2024 07:07AM UTC coverage: 58.893% (-0.004%) from 58.897%
8658338405

push

github

soyuka
chore: doctrine deprecations (#6237)

0 of 1 new or added line in 1 file covered. (0.0%)

6 existing lines in 3 files now uncovered.

10900 of 18508 relevant lines covered (58.89%)

19.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.55
/src/Metadata/Extractor/YamlResourceExtractor.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Metadata\Extractor;
15

16
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
17
use ApiPlatform\Metadata\GetCollection;
18
use ApiPlatform\Metadata\Post;
19
use ApiPlatform\Elasticsearch\State\Options;
20
use ApiPlatform\OpenApi\Model\ExternalDocumentation;
21
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
22
use ApiPlatform\OpenApi\Model\Parameter;
23
use ApiPlatform\OpenApi\Model\RequestBody;
24
use ApiPlatform\State\OptionsInterface;
25
use Symfony\Component\Yaml\Exception\ParseException;
26
use Symfony\Component\Yaml\Yaml;
27

28
/**
29
 * Extracts an array of metadata from a list of YAML files.
30
 *
31
 * @author Antoine Bluchet <soyuka@gmail.com>
32
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
33
 * @author Kévin Dunglas <dunglas@gmail.com>
34
 * @author Vincent Chalamon <vincentchalamon@gmail.com>
35
 */
36
final class YamlResourceExtractor extends AbstractResourceExtractor
37
{
38
    use ResourceExtractorTrait;
39

40
    /**
41
     * {@inheritdoc}
42
     */
43
    protected function extractPath(string $path): void
44
    {
45
        try {
46
            $resourcesYaml = Yaml::parse((string) file_get_contents($path), Yaml::PARSE_CONSTANT);
8✔
47
        } catch (ParseException $e) {
×
48
            $e->setParsedFile($path);
×
49

50
            throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
×
51
        }
52

53
        if (null === $resourcesYaml = $resourcesYaml['resources'] ?? $resourcesYaml) {
8✔
54
            return;
×
55
        }
56

57
        if (!\is_array($resourcesYaml)) {
8✔
58
            throw new InvalidArgumentException(sprintf('"resources" setting is expected to be null or an array, %s given in "%s".', \gettype($resourcesYaml), $path));
×
59
        }
60

61
        $this->buildResources($resourcesYaml, $path);
8✔
62
    }
63

64
    private function buildResources(array $resourcesYaml, string $path): void
65
    {
66
        foreach ($resourcesYaml as $resourceName => $resourceYaml) {
8✔
67
            $resourceName = $this->resolve($resourceName);
8✔
68

69
            if (null === $resourceYaml) {
8✔
70
                $resourceYaml = [[]];
8✔
71
            }
72

73
            if (!\array_key_exists(0, $resourceYaml)) {
8✔
74
                $resourceYaml = [$resourceYaml];
8✔
75
            }
76

77
            foreach ($resourceYaml as $key => $resourceYamlDatum) {
8✔
78
                if (null === $resourceYamlDatum) {
8✔
79
                    $resourceYamlDatum = [];
8✔
80
                }
81

82
                try {
83
                    $base = $this->buildExtendedBase($resourceYamlDatum);
8✔
84
                    $this->resources[$resourceName][$key] = array_merge($base, [
8✔
85
                        'operations' => $this->buildOperations($resourceYamlDatum, $base),
8✔
86
                        'graphQlOperations' => $this->buildGraphQlOperations($resourceYamlDatum, $base),
8✔
87
                    ]);
8✔
88
                } catch (InvalidArgumentException $exception) {
×
89
                    throw new InvalidArgumentException(sprintf('%s in "%s" (%s).', $exception->getMessage(), $resourceName, $path));
×
90
                }
91
            }
92
        }
93
    }
94

95
    private function buildExtendedBase(array $resource): array
96
    {
97
        return array_merge($this->buildBase($resource), [
8✔
98
            'uriTemplate' => $this->phpize($resource, 'uriTemplate', 'string'),
8✔
99
            'routePrefix' => $this->phpize($resource, 'routePrefix', 'string'),
8✔
100
            'stateless' => $this->phpize($resource, 'stateless', 'bool'),
8✔
101
            'sunset' => $this->phpize($resource, 'sunset', 'string'),
8✔
102
            'acceptPatch' => $this->phpize($resource, 'acceptPatch', 'string'),
8✔
103
            'host' => $this->phpize($resource, 'host', 'string'),
8✔
104
            'condition' => $this->phpize($resource, 'condition', 'string'),
8✔
105
            'controller' => $this->phpize($resource, 'controller', 'string'),
8✔
106
            'queryParameterValidationEnabled' => $this->phpize($resource, 'queryParameterValidationEnabled', 'bool'),
8✔
107
            'types' => $this->buildArrayValue($resource, 'types'),
8✔
108
            'cacheHeaders' => $this->buildArrayValue($resource, 'cacheHeaders'),
8✔
109
            'hydraContext' => $this->buildArrayValue($resource, 'hydraContext'),
8✔
110
            'openapiContext' => $this->buildArrayValue($resource, 'openapiContext'), // TODO Remove in 4.0
8✔
111
            'openapi' => $this->buildOpenapi($resource),
8✔
112
            'paginationViaCursor' => $this->buildArrayValue($resource, 'paginationViaCursor'),
8✔
113
            'exceptionToStatus' => $this->buildArrayValue($resource, 'exceptionToStatus'),
8✔
114
            'defaults' => $this->buildArrayValue($resource, 'defaults'),
8✔
115
            'requirements' => $this->buildArrayValue($resource, 'requirements'),
8✔
116
            'options' => $this->buildArrayValue($resource, 'options'),
8✔
117
            'status' => $this->phpize($resource, 'status', 'integer'),
8✔
118
            'schemes' => $this->buildArrayValue($resource, 'schemes'),
8✔
119
            'formats' => $this->buildArrayValue($resource, 'formats'),
8✔
120
            'uriVariables' => $this->buildUriVariables($resource),
8✔
121
            'inputFormats' => $this->buildArrayValue($resource, 'inputFormats'),
8✔
122
            'outputFormats' => $this->buildArrayValue($resource, 'outputFormats'),
8✔
123
            'stateOptions' => $this->buildStateOptions($resource),
8✔
124
        ]);
8✔
125
    }
126

127
    private function buildBase(array $resource): array
128
    {
129
        return [
8✔
130
            'shortName' => $this->phpize($resource, 'shortName', 'string'),
8✔
131
            'description' => $this->phpize($resource, 'description', 'string'),
8✔
132
            'urlGenerationStrategy' => $this->phpize($resource, 'urlGenerationStrategy', 'integer'),
8✔
133
            'deprecationReason' => $this->phpize($resource, 'deprecationReason', 'string'),
8✔
134
            'elasticsearch' => $this->phpize($resource, 'elasticsearch', 'bool'),
8✔
135
            'fetchPartial' => $this->phpize($resource, 'fetchPartial', 'bool'),
8✔
136
            'forceEager' => $this->phpize($resource, 'forceEager', 'bool'),
8✔
137
            'paginationClientEnabled' => $this->phpize($resource, 'paginationClientEnabled', 'bool'),
8✔
138
            'paginationClientItemsPerPage' => $this->phpize($resource, 'paginationClientItemsPerPage', 'bool'),
8✔
139
            'paginationClientPartial' => $this->phpize($resource, 'paginationClientPartial', 'bool'),
8✔
140
            'paginationEnabled' => $this->phpize($resource, 'paginationEnabled', 'bool'),
8✔
141
            'paginationFetchJoinCollection' => $this->phpize($resource, 'paginationFetchJoinCollection', 'bool'),
8✔
142
            'paginationUseOutputWalkers' => $this->phpize($resource, 'paginationUseOutputWalkers', 'bool'),
8✔
143
            'paginationItemsPerPage' => $this->phpize($resource, 'paginationItemsPerPage', 'integer'),
8✔
144
            'paginationMaximumItemsPerPage' => $this->phpize($resource, 'paginationMaximumItemsPerPage', 'integer'),
8✔
145
            'paginationPartial' => $this->phpize($resource, 'paginationPartial', 'bool'),
8✔
146
            'paginationType' => $this->phpize($resource, 'paginationType', 'string'),
8✔
147
            'processor' => $this->phpize($resource, 'processor', 'string'),
8✔
148
            'provider' => $this->phpize($resource, 'provider', 'string'),
8✔
149
            'security' => $this->phpize($resource, 'security', 'string'),
8✔
150
            'securityMessage' => $this->phpize($resource, 'securityMessage', 'string'),
8✔
151
            'securityPostDenormalize' => $this->phpize($resource, 'securityPostDenormalize', 'string'),
8✔
152
            'securityPostDenormalizeMessage' => $this->phpize($resource, 'securityPostDenormalizeMessage', 'string'),
8✔
153
            'securityPostValidation' => $this->phpize($resource, 'securityPostValidation', 'string'),
8✔
154
            'securityPostValidationMessage' => $this->phpize($resource, 'securityPostValidationMessage', 'string'),
8✔
155
            'input' => $this->phpize($resource, 'input', 'bool|string'),
8✔
156
            'output' => $this->phpize($resource, 'output', 'bool|string'),
8✔
157
            'normalizationContext' => $this->buildArrayValue($resource, 'normalizationContext'),
8✔
158
            'denormalizationContext' => $this->buildArrayValue($resource, 'denormalizationContext'),
8✔
159
            'collectDenormalizationErrors' => $this->phpize($resource, 'collectDenormalizationErrors', 'bool'),
8✔
160
            'validationContext' => $this->buildArrayValue($resource, 'validationContext'),
8✔
161
            'filters' => $this->buildArrayValue($resource, 'filters'),
8✔
162
            'order' => $this->buildArrayValue($resource, 'order'),
8✔
163
            'extraProperties' => $this->buildArrayValue($resource, 'extraProperties'),
8✔
164
            'mercure' => $this->buildMercure($resource),
8✔
165
            'messenger' => $this->buildMessenger($resource),
8✔
166
            'read' => $this->phpize($resource, 'read', 'bool'),
8✔
167
            'write' => $this->phpize($resource, 'write', 'bool'),
8✔
168
        ];
8✔
169
    }
170

171
    private function buildUriVariables(array $resource): ?array
172
    {
173
        if (!\array_key_exists('uriVariables', $resource)) {
8✔
174
            return null;
8✔
175
        }
176

177
        $uriVariables = [];
8✔
178
        foreach ($resource['uriVariables'] as $parameterName => $data) {
8✔
179
            if (\is_string($data)) {
8✔
180
                $uriVariables[$data] = $data;
×
181
                continue;
×
182
            }
183

184
            if (2 === (is_countable($data) ? \count($data) : 0) && isset($data[0]) && isset($data[1])) {
8✔
185
                $data['fromClass'] = $data[0];
8✔
186
                $data['fromProperty'] = $data[1];
8✔
187
                unset($data[0], $data[1]);
8✔
188
            }
189
            if (isset($data['fromClass'])) {
8✔
190
                $uriVariables[$parameterName]['from_class'] = $data['fromClass'];
8✔
191
            }
192
            if (isset($data['fromProperty'])) {
8✔
193
                $uriVariables[$parameterName]['from_property'] = $data['fromProperty'];
8✔
194
            }
195
            if (isset($data['toClass'])) {
8✔
196
                $uriVariables[$parameterName]['to_class'] = $data['toClass'];
×
197
            }
198
            if (isset($data['toProperty'])) {
8✔
199
                $uriVariables[$parameterName]['to_property'] = $data['toProperty'];
8✔
200
            }
201
            if (isset($data['identifiers'])) {
8✔
202
                $uriVariables[$parameterName]['identifiers'] = $data['identifiers'];
×
203
            }
204
            if (isset($data['compositeIdentifier'])) {
8✔
205
                $uriVariables[$parameterName]['composite_identifier'] = $data['compositeIdentifier'];
×
206
            }
207
        }
208

209
        return $uriVariables;
8✔
210
    }
211

212
    private function buildOpenapi(array $resource): bool|OpenApiOperation|null
213
    {
214
        if (!\array_key_exists('openapi', $resource)) {
8✔
215
            return null;
8✔
216
        }
217

218
        if (!\is_array($resource['openapi'])) {
×
219
            return $this->phpize($resource, 'openapi', 'bool');
×
220
        }
221

222
        $allowedProperties = array_map(fn (\ReflectionProperty $reflProperty): string => $reflProperty->getName(), (new \ReflectionClass(OpenApiOperation::class))->getProperties());
×
223
        foreach ($resource['openapi'] as $key => $value) {
×
224
            $resource['openapi'][$key] = match ($key) {
×
225
                'externalDocs' => new ExternalDocumentation(description: $value['description'] ?? '', url: $value['url'] ?? ''),
×
226
                'requestBody' => new RequestBody(description: $value['description'] ?? '', content: isset($value['content']) ? new \ArrayObject($value['content'] ?? []) : null, required: $value['required'] ?? false),
×
227
                'callbacks' => new \ArrayObject($value ?? []),
×
228
                default => $value,
×
229
            };
×
230

231
            if (\in_array($key, $allowedProperties, true)) {
×
232
                continue;
×
233
            }
234

235
            $resource['openapi']['extensionProperties'][$key] = $value;
×
236
            unset($resource['openapi'][$key]);
×
237
        }
238

239
        if (\array_key_exists('parameters', $resource['openapi']) && \is_array($openapiParameters = $resource['openapi']['parameters'] ?? [])) {
×
240
            $parameters = [];
×
241
            foreach ($openapiParameters as $parameter) {
×
242
                $parameters[] = new Parameter(
×
243
                    name: $parameter['name'],
×
244
                    in: $parameter['in'],
×
245
                    description: $parameter['description'] ?? '',
×
246
                    required: $parameter['required'] ?? false,
×
247
                    deprecated: $parameter['deprecated'] ?? false,
×
248
                    allowEmptyValue: $parameter['allowEmptyValue'] ?? false,
×
249
                    schema: $parameter['schema'] ?? [],
×
250
                    style: $parameter['style'] ?? null,
×
251
                    explode: $parameter['explode'] ?? false,
×
252
                    allowReserved: $parameter['allowReserved '] ?? false,
×
253
                    example: $parameter['example'] ?? null,
×
254
                    examples: isset($parameter['examples']) ? new \ArrayObject($parameter['examples']) : null,
×
255
                    content: isset($parameter['content']) ? new \ArrayObject($parameter['content']) : null
×
256
                );
×
257
            }
258
            $resource['openapi']['parameters'] = $parameters;
×
259
        }
260

261
        return new OpenApiOperation(...$resource['openapi']);
×
262
    }
263

264
    /**
265
     * @return bool|string|string[]|null
266
     */
267
    private function buildMercure(array $resource): array|bool|string|null
268
    {
269
        if (!\array_key_exists('mercure', $resource)) {
8✔
270
            return null;
8✔
271
        }
272

273
        if (\is_string($resource['mercure'])) {
×
274
            return $this->phpize($resource, 'mercure', 'bool|string');
×
275
        }
276

277
        return $resource['mercure'];
×
278
    }
279

280
    private function buildMessenger(array $resource): bool|array|string|null
281
    {
282
        if (!\array_key_exists('messenger', $resource)) {
8✔
283
            return null;
8✔
284
        }
285

286
        return $this->phpize($resource, 'messenger', 'bool|string');
×
287
    }
288

289
    private function buildOperations(array $resource, array $root): ?array
290
    {
291
        if (!\array_key_exists('operations', $resource)) {
8✔
292
            return null;
8✔
293
        }
294

295
        $data = [];
8✔
296
        foreach ($resource['operations'] as $class => $operation) {
8✔
297
            if (null === $operation) {
8✔
298
                $operation = [];
8✔
299
            }
300

301
            if (\array_key_exists('class', $operation)) {
8✔
302
                if (!\array_key_exists('name', $operation) && \is_string($class)) {
×
303
                    $operation['name'] = $class;
×
304
                }
305
                $class = $operation['class'];
×
306
            }
307

308
            if (empty($class)) {
8✔
309
                throw new InvalidArgumentException('Missing "class" attribute');
×
310
            }
311

312
            if (!class_exists($class)) {
8✔
313
                throw new InvalidArgumentException(sprintf('Operation class "%s" does not exist', $class));
×
314
            }
315

316
            $datum = $this->buildExtendedBase($operation);
8✔
317
            foreach ($datum as $key => $value) {
8✔
318
                if (null === $value) {
8✔
319
                    $datum[$key] = $root[$key];
8✔
320
                }
321
            }
322

323
            if (\in_array((string) $class, [GetCollection::class, Post::class], true)) {
8✔
324
                $datum['itemUriTemplate'] = $this->phpize($operation, 'itemUriTemplate', 'string');
8✔
325
            }
326

327
            $data[] = array_merge($datum, [
8✔
328
                'read' => $this->phpize($operation, 'read', 'bool'),
8✔
329
                'deserialize' => $this->phpize($operation, 'deserialize', 'bool'),
8✔
330
                'validate' => $this->phpize($operation, 'validate', 'bool'),
8✔
331
                'write' => $this->phpize($operation, 'write', 'bool'),
8✔
332
                'serialize' => $this->phpize($operation, 'serialize', 'bool'),
8✔
333
                'queryParameterValidate' => $this->phpize($operation, 'queryParameterValidate', 'bool'),
8✔
334
                'priority' => $this->phpize($operation, 'priority', 'integer'),
8✔
335
                'name' => $this->phpize($operation, 'name', 'string'),
8✔
336
                'class' => (string) $class,
8✔
337
            ]);
8✔
338
        }
339

340
        return $data;
8✔
341
    }
342

343
    private function buildGraphQlOperations(array $resource, array $root): ?array
344
    {
345
        if (!\array_key_exists('graphQlOperations', $resource) || !\is_array($resource['graphQlOperations'])) {
8✔
346
            return null;
8✔
347
        }
348

349
        $data = [];
8✔
350
        foreach ($resource['graphQlOperations'] as $class => $operation) {
8✔
351
            if (null === $operation) {
×
352
                $operation = [];
×
353
            }
354

355
            if (\array_key_exists('class', $operation)) {
×
356
                if (!\array_key_exists('name', $operation) && \is_string($class)) {
×
357
                    $operation['name'] = $class;
×
358
                }
359
                $class = $operation['class'];
×
360
            }
361

362
            if (empty($class)) {
×
363
                throw new InvalidArgumentException('Missing "class" attribute');
×
364
            }
365

366
            if (!class_exists($class)) {
×
367
                throw new InvalidArgumentException(sprintf('Operation class "%s" does not exist', $class));
×
368
            }
369

370
            $datum = $this->buildBase($operation);
×
371
            foreach ($datum as $key => $value) {
×
372
                if (null === $value) {
×
373
                    $datum[$key] = $root[$key];
×
374
                }
375
            }
376

377
            $data[] = array_merge($datum, [
×
378
                'resolver' => $this->phpize($operation, 'resolver', 'string'),
×
379
                'args' => $operation['args'] ?? null,
×
380
                'class' => (string) $class,
×
381
                'read' => $this->phpize($operation, 'read', 'bool'),
×
382
                'deserialize' => $this->phpize($operation, 'deserialize', 'bool'),
×
383
                'validate' => $this->phpize($operation, 'validate', 'bool'),
×
384
                'write' => $this->phpize($operation, 'write', 'bool'),
×
385
                'serialize' => $this->phpize($operation, 'serialize', 'bool'),
×
386
                'priority' => $this->phpize($operation, 'priority', 'integer'),
×
387
                'name' => $this->phpize($operation, 'name', 'string'),
×
388
            ]);
×
389
        }
390

391
        return $data ?: null;
8✔
392
    }
393

394
    private function buildStateOptions(array $resource): ?OptionsInterface
395
    {
396
        $stateOptions = $resource['stateOptions'] ?? [];
8✔
397
        if (!\is_array($stateOptions)) {
8✔
398
            return null;
×
399
        }
400

401
        if (!$stateOptions) {
8✔
402
            return null;
8✔
403
        }
404

405
        $configuration = reset($stateOptions);
×
406
        switch (key($stateOptions)) {
×
407
            case 'elasticsearchOptions':
×
408
                if (class_exists(Options::class)) {
×
UNCOV
409
                    return new Options($configuration['index'] ?? null, $configuration['type'] ?? null);
×
410
                }
411
        }
412

UNCOV
413
        return null;
×
414
    }
415
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc