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

api-platform / core / 7582976395

19 Jan 2024 11:09AM UTC coverage: 61.988% (+0.03%) from 61.96%
7582976395

push

github

web-flow
feat(metadata): headers configuration (#6074)

27 of 40 new or added lines in 14 files covered. (67.5%)

46 existing lines in 8 files now uncovered.

17483 of 28204 relevant lines covered (61.99%)

32.43 hits per line

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

53.6
/src/Metadata/Extractor/XmlResourceExtractor.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\Metadata\Tests\Fixtures\StateOptions;
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\Config\Util\XmlUtils;
26
use Symfony\Component\WebLink\Link;
27

28
/**
29
 * Extracts an array of metadata from a list of XML files.
30
 *
31
 * @author Vincent Chalamon <vincentchalamon@gmail.com>
32
 */
33
final class XmlResourceExtractor extends AbstractResourceExtractor
34
{
35
    use ResourceExtractorTrait;
36

37
    public const SCHEMA = __DIR__.'/schema/resources.xsd';
38

39
    /**
40
     * {@inheritdoc}
41
     */
42
    protected function extractPath(string $path): void
43
    {
44
        try {
45
            /** @var \SimpleXMLElement $xml */
46
            $xml = simplexml_import_dom(XmlUtils::loadFile($path, self::SCHEMA));
32✔
47
        } catch (\InvalidArgumentException $e) {
32✔
48
            // Ensure it's not a resource
49
            try {
50
                simplexml_import_dom(XmlUtils::loadFile($path, XmlPropertyExtractor::SCHEMA));
32✔
51
            } catch (\InvalidArgumentException) {
×
52
                throw new InvalidArgumentException(sprintf('Error while parsing %s: %s', $path, $e->getMessage()), $e->getCode(), $e);
×
53
            }
54

55
            // It's a property: ignore error
56
            return;
32✔
57
        }
58

59
        foreach ($xml->resource as $resource) {
32✔
60
            $base = $this->buildExtendedBase($resource);
32✔
61
            $this->resources[$this->resolve((string) $resource['class'])][] = array_merge($base, [
32✔
62
                'class' => $this->phpize($resource, 'class', 'string'),
32✔
63
                'operations' => $this->buildOperations($resource, $base),
32✔
64
                'graphQlOperations' => $this->buildGraphQlOperations($resource, $base),
32✔
65
            ]);
32✔
66
        }
67
    }
68

69
    private function buildExtendedBase(\SimpleXMLElement $resource): array
70
    {
71
        return array_merge($this->buildBase($resource), [
32✔
72
            'uriTemplate' => $this->phpize($resource, 'uriTemplate', 'string'),
32✔
73
            'routePrefix' => $this->phpize($resource, 'routePrefix', 'string'),
32✔
74
            'stateless' => $this->phpize($resource, 'stateless', 'bool'),
32✔
75
            'sunset' => $this->phpize($resource, 'sunset', 'string'),
32✔
76
            'acceptPatch' => $this->phpize($resource, 'acceptPatch', 'string'),
32✔
77
            'status' => $this->phpize($resource, 'status', 'integer'),
32✔
78
            'host' => $this->phpize($resource, 'host', 'string'),
32✔
79
            'condition' => $this->phpize($resource, 'condition', 'string'),
32✔
80
            'controller' => $this->phpize($resource, 'controller', 'string'),
32✔
81
            'types' => $this->buildArrayValue($resource, 'type'),
32✔
82
            'formats' => $this->buildFormats($resource, 'formats'),
32✔
83
            'inputFormats' => $this->buildFormats($resource, 'inputFormats'),
32✔
84
            'outputFormats' => $this->buildFormats($resource, 'outputFormats'),
32✔
85
            'uriVariables' => $this->buildUriVariables($resource),
32✔
86
            'defaults' => isset($resource->defaults->values) ? $this->buildValues($resource->defaults->values) : null,
32✔
87
            'requirements' => $this->buildRequirements($resource),
32✔
88
            'options' => isset($resource->options->values) ? $this->buildValues($resource->options->values) : null,
32✔
89
            'schemes' => $this->buildArrayValue($resource, 'scheme'),
32✔
90
            'cacheHeaders' => $this->buildCacheHeaders($resource),
32✔
91
            'hydraContext' => isset($resource->hydraContext->values) ? $this->buildValues($resource->hydraContext->values) : null,
32✔
92
            'openapiContext' => isset($resource->openapiContext->values) ? $this->buildValues($resource->openapiContext->values) : null, // TODO Remove in 4.0
32✔
93
            'openapi' => $this->buildOpenapi($resource),
32✔
94
            'paginationViaCursor' => $this->buildPaginationViaCursor($resource),
32✔
95
            'exceptionToStatus' => $this->buildExceptionToStatus($resource),
32✔
96
            'queryParameterValidationEnabled' => $this->phpize($resource, 'queryParameterValidationEnabled', 'bool'),
32✔
97
            'stateOptions' => $this->buildStateOptions($resource),
32✔
98
            'links' => $this->buildLinks($resource),
32✔
99
            'headers' => $this->buildHeaders($resource),
32✔
100
        ]);
32✔
101
    }
102

103
    private function buildBase(\SimpleXMLElement $resource): array
104
    {
105
        return [
32✔
106
            'shortName' => $this->phpize($resource, 'shortName', 'string'),
32✔
107
            'description' => $this->phpize($resource, 'description', 'string'),
32✔
108
            'urlGenerationStrategy' => $this->phpize($resource, 'urlGenerationStrategy', 'integer'),
32✔
109
            'deprecationReason' => $this->phpize($resource, 'deprecationReason', 'string'),
32✔
110
            'elasticsearch' => $this->phpize($resource, 'elasticsearch', 'bool'),
32✔
111
            'messenger' => $this->phpize($resource, 'messenger', 'bool|string'),
32✔
112
            'mercure' => $this->buildMercure($resource),
32✔
113
            'input' => $this->phpize($resource, 'input', 'bool|string'),
32✔
114
            'output' => $this->phpize($resource, 'output', 'bool|string'),
32✔
115
            'fetchPartial' => $this->phpize($resource, 'fetchPartial', 'bool'),
32✔
116
            'forceEager' => $this->phpize($resource, 'forceEager', 'bool'),
32✔
117
            'paginationClientEnabled' => $this->phpize($resource, 'paginationClientEnabled', 'bool'),
32✔
118
            'paginationClientItemsPerPage' => $this->phpize($resource, 'paginationClientItemsPerPage', 'bool'),
32✔
119
            'paginationClientPartial' => $this->phpize($resource, 'paginationClientPartial', 'bool'),
32✔
120
            'paginationEnabled' => $this->phpize($resource, 'paginationEnabled', 'bool'),
32✔
121
            'paginationFetchJoinCollection' => $this->phpize($resource, 'paginationFetchJoinCollection', 'bool'),
32✔
122
            'paginationUseOutputWalkers' => $this->phpize($resource, 'paginationUseOutputWalkers', 'bool'),
32✔
123
            'paginationItemsPerPage' => $this->phpize($resource, 'paginationItemsPerPage', 'integer'),
32✔
124
            'paginationMaximumItemsPerPage' => $this->phpize($resource, 'paginationMaximumItemsPerPage', 'integer'),
32✔
125
            'paginationPartial' => $this->phpize($resource, 'paginationPartial', 'bool'),
32✔
126
            'paginationType' => $this->phpize($resource, 'paginationType', 'string'),
32✔
127
            'processor' => $this->phpize($resource, 'processor', 'string'),
32✔
128
            'provider' => $this->phpize($resource, 'provider', 'string'),
32✔
129
            'security' => $this->phpize($resource, 'security', 'string'),
32✔
130
            'securityMessage' => $this->phpize($resource, 'securityMessage', 'string'),
32✔
131
            'securityPostDenormalize' => $this->phpize($resource, 'securityPostDenormalize', 'string'),
32✔
132
            'securityPostDenormalizeMessage' => $this->phpize($resource, 'securityPostDenormalizeMessage', 'string'),
32✔
133
            'securityPostValidation' => $this->phpize($resource, 'securityPostValidation', 'string'),
32✔
134
            'securityPostValidationMessage' => $this->phpize($resource, 'securityPostValidationMessage', 'string'),
32✔
135
            'normalizationContext' => isset($resource->normalizationContext->values) ? $this->buildValues($resource->normalizationContext->values) : null,
32✔
136
            'denormalizationContext' => isset($resource->denormalizationContext->values) ? $this->buildValues($resource->denormalizationContext->values) : null,
32✔
137
            'collectDenormalizationErrors' => $this->phpize($resource, 'collectDenormalizationErrors', 'bool'),
32✔
138
            'validationContext' => isset($resource->validationContext->values) ? $this->buildValues($resource->validationContext->values) : null,
32✔
139
            'filters' => $this->buildArrayValue($resource, 'filter'),
32✔
140
            'order' => isset($resource->order->values) ? $this->buildValues($resource->order->values) : null,
32✔
141
            'extraProperties' => $this->buildExtraProperties($resource, 'extraProperties'),
32✔
142
            'read' => $this->phpize($resource, 'read', 'bool'),
32✔
143
            'write' => $this->phpize($resource, 'write', 'bool'),
32✔
144
        ];
32✔
145
    }
146

147
    private function buildFormats(\SimpleXMLElement $resource, string $key): ?array
148
    {
149
        if (!isset($resource->{$key}->format)) {
32✔
150
            return null;
32✔
151
        }
152

153
        $data = [];
×
154
        foreach ($resource->{$key}->format as $format) {
×
155
            if (isset($format['name'])) {
×
156
                $data[(string) $format['name']] = (string) $format;
×
157
                continue;
×
158
            }
159

160
            $data[] = (string) $format;
×
161
        }
162

163
        return $data;
×
164
    }
165

166
    private function buildOpenapi(\SimpleXMLElement $resource): bool|OpenApiOperation|null
167
    {
168
        if (!isset($resource->openapi) && !isset($resource['openapi'])) {
32✔
169
            return null;
32✔
170
        }
171

172
        if (isset($resource['openapi']) && \in_array((string) $resource['openapi'], ['1', '0', 'true', 'false'], true)) {
×
173
            return $this->phpize($resource, 'openapi', 'bool');
×
174
        }
175

176
        $openapi = $resource->openapi;
×
177
        $data = [];
×
178
        $attributes = $openapi->attributes();
×
179
        foreach ($attributes as $attribute) {
×
180
            $data[$attribute->getName()] = $this->phpize($attributes, 'deprecated', 'deprecated' === $attribute->getName() ? 'bool' : 'string');
×
181
        }
182

183
        $data['tags'] = $this->buildArrayValue($resource, 'tag');
×
184

185
        if (isset($openapi->responses->response)) {
×
186
            foreach ($openapi->responses->response as $response) {
×
187
                $data['responses'][(string) $response->attributes()->status] = [
×
188
                    'description' => $this->phpize($response, 'description', 'string'),
×
189
                    'content' => isset($response->content->values) ? $this->buildValues($response->content->values) : null,
×
190
                    'headers' => isset($response->headers->values) ? $this->buildValues($response->headers->values) : null,
×
191
                    'links' => isset($response->links->values) ? $this->buildValues($response->links->values) : null,
×
192
                ];
×
193
            }
194
        }
195

196
        $data['externalDocs'] = isset($openapi->externalDocs) ? new ExternalDocumentation(
×
197
            description: $this->phpize($resource, 'description', 'string'),
×
198
            url: $this->phpize($resource, 'url', 'string'),
×
199
        ) : null;
×
200

201
        if (isset($openapi->parameters->parameter)) {
×
202
            foreach ($openapi->parameters->parameter as $parameter) {
×
203
                $data['parameters'][(string) $parameter->attributes()->name] = new Parameter(
×
204
                    name: $this->phpize($parameter, 'name', 'string'),
×
205
                    in: $this->phpize($parameter, 'in', 'string'),
×
206
                    description: $this->phpize($parameter, 'description', 'string'),
×
207
                    required: $this->phpize($parameter, 'required', 'bool'),
×
208
                    deprecated: $this->phpize($parameter, 'deprecated', 'bool'),
×
209
                    allowEmptyValue: $this->phpize($parameter, 'allowEmptyValue', 'bool'),
×
210
                    schema: isset($parameter->schema->values) ? $this->buildValues($parameter->schema->values) : null,
×
211
                    style: $this->phpize($parameter, 'style', 'string'),
×
212
                    explode: $this->phpize($parameter, 'explode', 'bool'),
×
213
                    allowReserved: $this->phpize($parameter, 'allowReserved', 'bool'),
×
214
                    example: $this->phpize($parameter, 'example', 'string'),
×
215
                    examples: isset($parameter->examples->values) ? new \ArrayObject($this->buildValues($parameter->examples->values)) : null,
×
216
                    content: isset($parameter->content->values) ? new \ArrayObject($this->buildValues($parameter->content->values)) : null,
×
217
                );
×
218
            }
219
        }
220
        $data['requestBody'] = isset($openapi->requestBody) ? new RequestBody(
×
221
            description: $this->phpize($openapi->requestBody, 'description', 'string'),
×
222
            content: isset($openapi->requestBody->content->values) ? new \ArrayObject($this->buildValues($openapi->requestBody->content->values)) : null,
×
223
            required: $this->phpize($openapi->requestBody, 'required', 'bool'),
×
224
        ) : null;
×
225

226
        $data['callbacks'] = isset($openapi->callbacks->values) ? new \ArrayObject($this->buildValues($openapi->callbacks->values)) : null;
×
227

228
        $data['security'] = isset($openapi->security->values) ? $this->buildValues($openapi->security->values) : null;
×
229

230
        if (isset($openapi->servers->server)) {
×
231
            foreach ($openapi->servers->server as $server) {
×
232
                $data['servers'][] = [
×
233
                    'description' => $this->phpize($server, 'description', 'string'),
×
234
                    'url' => $this->phpize($server, 'url', 'string'),
×
235
                    'variables' => isset($server->variables->values) ? $this->buildValues($server->variables->values) : null,
×
236
                ];
×
237
            }
238
        }
239

240
        $data['extensionProperties'] = isset($openapi->extensionProperties->values) ? $this->buildValues($openapi->extensionProperties->values) : null;
×
241

242
        foreach ($data as $key => $value) {
×
243
            if (null === $value) {
×
244
                unset($data[$key]);
×
245
            }
246
        }
247

248
        return new OpenApiOperation(...$data);
×
249
    }
250

251
    private function buildUriVariables(\SimpleXMLElement $resource): ?array
252
    {
253
        if (!isset($resource->uriVariables->uriVariable)) {
32✔
254
            return null;
32✔
255
        }
256

257
        $uriVariables = [];
32✔
258
        foreach ($resource->uriVariables->uriVariable as $data) {
32✔
259
            $parameterName = (string) $data['parameterName'];
32✔
260
            if (1 === (null === $data->attributes() ? 0 : \count($data->attributes()))) {
32✔
261
                $uriVariables[$parameterName] = $parameterName;
32✔
262
                continue;
32✔
263
            }
264

265
            if ($fromProperty = $this->phpize($data, 'fromProperty', 'string')) {
32✔
266
                $uriVariables[$parameterName]['from_property'] = $fromProperty;
×
267
            }
268
            if ($toProperty = $this->phpize($data, 'toProperty', 'string')) {
32✔
269
                $uriVariables[$parameterName]['to_property'] = $toProperty;
32✔
270
            }
271
            if ($fromClass = $this->phpize($data, 'fromClass', 'string')) {
32✔
272
                $uriVariables[$parameterName]['from_class'] = $fromClass;
32✔
273
            }
274
            if ($toClass = $this->phpize($data, 'toClass', 'string')) {
32✔
275
                $uriVariables[$parameterName]['to_class'] = $toClass;
×
276
            }
277
            if (isset($data->identifiers->values)) {
32✔
278
                $uriVariables[$parameterName]['identifiers'] = $this->buildValues($data->identifiers->values);
×
279
            }
280
            if (null !== ($compositeIdentifier = $this->phpize($data, 'compositeIdentifier', 'bool'))) {
32✔
281
                $uriVariables[$parameterName]['composite_identifier'] = $compositeIdentifier;
×
282
            }
283
        }
284

285
        return $uriVariables;
32✔
286
    }
287

288
    private function buildCacheHeaders(\SimpleXMLElement $resource): ?array
289
    {
290
        if (!isset($resource->cacheHeaders->cacheHeader)) {
32✔
291
            return null;
32✔
292
        }
293

294
        $data = [];
×
295
        foreach ($resource->cacheHeaders->cacheHeader as $cacheHeader) {
×
296
            if (isset($cacheHeader->values->value)) {
×
297
                $data[(string) $cacheHeader['name']] = $this->buildValues($cacheHeader->values);
×
298
                continue;
×
299
            }
300

301
            $data[(string) $cacheHeader['name']] = (string) $cacheHeader;
×
302
        }
303

304
        return $data;
×
305
    }
306

307
    private function buildRequirements(\SimpleXMLElement $resource): ?array
308
    {
309
        if (!isset($resource->requirements->requirement)) {
32✔
310
            return null;
32✔
311
        }
312

313
        $data = [];
×
314
        foreach ($resource->requirements->requirement as $requirement) {
×
315
            $data[(string) $requirement->attributes()->property] = (string) $requirement;
×
316
        }
317

318
        return $data;
×
319
    }
320

321
    private function buildMercure(\SimpleXMLElement $resource): array|bool|null
322
    {
323
        if (!isset($resource->mercure)) {
32✔
324
            return null;
32✔
325
        }
326

327
        if (null !== $resource->mercure->attributes()->private) {
×
328
            return ['private' => $this->phpize($resource->mercure->attributes(), 'private', 'bool')];
×
329
        }
330

331
        return true;
×
332
    }
333

334
    private function buildPaginationViaCursor(\SimpleXMLElement $resource): ?array
335
    {
336
        if (!isset($resource->paginationViaCursor->paginationField)) {
32✔
337
            return null;
32✔
338
        }
339

340
        $data = [];
×
341
        foreach ($resource->paginationViaCursor->paginationField as $paginationField) {
×
342
            $data[(string) $paginationField['field']] = (string) $paginationField['direction'];
×
343
        }
344

345
        return $data;
×
346
    }
347

348
    private function buildExceptionToStatus(\SimpleXMLElement $resource): ?array
349
    {
350
        if (!isset($resource->exceptionToStatus->exception)) {
32✔
351
            return null;
32✔
352
        }
353

354
        $data = [];
×
355
        foreach ($resource->exceptionToStatus->exception as $exception) {
×
356
            $data[(string) $exception['class']] = (int) $exception['statusCode'];
×
357
        }
358

359
        return $data;
×
360
    }
361

362
    private function buildExtraProperties(\SimpleXMLElement $resource, string $key = null): ?array
363
    {
364
        if (null !== $key) {
32✔
365
            if (!isset($resource->{$key})) {
32✔
366
                return null;
32✔
367
            }
368

369
            $resource = $resource->{$key};
×
370
        }
371

372
        return $this->buildValues($resource->values);
×
373
    }
374

375
    private function buildOperations(\SimpleXMLElement $resource, array $root): ?array
376
    {
377
        if (!isset($resource->operations->operation)) {
32✔
378
            return null;
32✔
379
        }
380

381
        $data = [];
32✔
382
        foreach ($resource->operations->operation as $operation) {
32✔
383
            $datum = $this->buildExtendedBase($operation);
32✔
384
            foreach ($datum as $key => $value) {
32✔
385
                if (null === $value) {
32✔
386
                    $datum[$key] = $root[$key];
32✔
387
                }
388
            }
389

390
            if (\in_array((string) $operation['class'], [GetCollection::class, Post::class], true)) {
32✔
391
                $datum['itemUriTemplate'] = $this->phpize($operation, 'itemUriTemplate', 'string');
32✔
392
            } elseif (isset($operation['itemUriTemplate'])) {
32✔
393
                throw new InvalidArgumentException(sprintf('"itemUriTemplate" option is not allowed on a %s operation.', $operation['class']));
×
394
            }
395

396
            $data[] = array_merge($datum, [
32✔
397
                'collection' => $this->phpize($operation, 'collection', 'bool'),
32✔
398
                'class' => (string) $operation['class'],
32✔
399
                'method' => $this->phpize($operation, 'method', 'string'),
32✔
400
                'read' => $this->phpize($operation, 'read', 'bool'),
32✔
401
                'deserialize' => $this->phpize($operation, 'deserialize', 'bool'),
32✔
402
                'validate' => $this->phpize($operation, 'validate', 'bool'),
32✔
403
                'write' => $this->phpize($operation, 'write', 'bool'),
32✔
404
                'serialize' => $this->phpize($operation, 'serialize', 'bool'),
32✔
405
                'queryParameterValidate' => $this->phpize($operation, 'queryParameterValidate', 'bool'),
32✔
406
                'priority' => $this->phpize($operation, 'priority', 'integer'),
32✔
407
                'name' => $this->phpize($operation, 'name', 'string'),
32✔
408
            ]);
32✔
409
        }
410

411
        return $data;
32✔
412
    }
413

414
    private function buildGraphQlOperations(\SimpleXMLElement $resource, array $root): ?array
415
    {
416
        if (!isset($resource->graphQlOperations->graphQlOperation)) {
32✔
417
            return null;
32✔
418
        }
419

420
        $data = [];
×
421
        foreach ($resource->graphQlOperations->graphQlOperation as $operation) {
×
422
            $datum = $this->buildBase($operation);
×
423
            foreach ($datum as $key => $value) {
×
424
                if (null === $value) {
×
425
                    $datum[$key] = $root[$key];
×
426
                }
427
            }
428

429
            $data[] = array_merge($datum, [
×
430
                'resolver' => $this->phpize($operation, 'resolver', 'string'),
×
431
                'args' => $this->buildArgs($operation),
×
432
                'extraArgs' => $this->buildExtraArgs($operation),
×
433
                'class' => (string) $operation['class'],
×
434
                'read' => $this->phpize($operation, 'read', 'bool'),
×
435
                'deserialize' => $this->phpize($operation, 'deserialize', 'bool'),
×
436
                'validate' => $this->phpize($operation, 'validate', 'bool'),
×
437
                'write' => $this->phpize($operation, 'write', 'bool'),
×
438
                'serialize' => $this->phpize($operation, 'serialize', 'bool'),
×
439
                'priority' => $this->phpize($operation, 'priority', 'integer'),
×
440
                'name' => $this->phpize($operation, 'name', 'string'),
×
441
            ]);
×
442
        }
443

444
        return $data;
×
445
    }
446

447
    private function buildStateOptions(\SimpleXMLElement $resource): ?OptionsInterface
448
    {
449
        $stateOptions = $resource->stateOptions ?? null;
32✔
450
        if (!$stateOptions) {
32✔
451
            return null;
32✔
452
        }
453
        $elasticsearchOptions = $stateOptions->elasticsearchOptions ?? null;
×
454
        if ($elasticsearchOptions) {
×
455
            return new StateOptions(
×
456
                isset($elasticsearchOptions['index']) ? (string) $elasticsearchOptions['index'] : null,
×
457
                isset($elasticsearchOptions['type']) ? (string) $elasticsearchOptions['type'] : null,
×
458
            );
×
459
        }
460

461
        return null;
×
462
    }
463

464
    /**
465
     * @return Link[]
466
     */
467
    private function buildLinks(\SimpleXMLElement $resource): ?array
468
    {
469
        if (!$resource->links) {
32✔
470
            return null;
32✔
471
        }
472

473
        $links = [];
×
474
        foreach ($resource->links as $link) {
×
475
            $links[] = new Link(rel: (string) $link->link->attributes()->rel, href: (string) $link->link->attributes()->href);
×
476
        }
477

478
        return $links;
×
479
    }
480

481
    /**
482
     * @return array<string, string>
483
     */
484
    private function buildHeaders(\SimpleXMLElement $resource): ?array
485
    {
486
        if (!$resource->headers) {
32✔
487
            return null;
32✔
488
        }
489

NEW
490
        $headers = [];
×
NEW
491
        foreach ($resource->headers as $header) {
×
NEW
492
            $headers[(string) $header->header->attributes()->key] = (string) $header->header->attributes()->value;
×
493
        }
494

NEW
495
        return $headers;
×
496
    }
497
}
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