Passed
Push — master ( 8bd912...d93388 )
by Alan
06:58 queued 02:20
created

Factory/OperationResourceMetadataFactory.php (2 issues)

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
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\Core\Metadata\Resource\Factory;
15
16
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
17
18
/**
19
 * Creates or completes operations.
20
 *
21
 * @author Kévin Dunglas <[email protected]>
22
 */
23
final class OperationResourceMetadataFactory implements ResourceMetadataFactoryInterface
24
{
25
    /**
26
     * @internal
27
     */
28
    public const SUPPORTED_COLLECTION_OPERATION_METHODS = [
29
        'GET' => true,
30
        'POST' => true,
31
    ];
32
33
    /**
34
     * @internal
35
     */
36
    public const SUPPORTED_ITEM_OPERATION_METHODS = [
37
        'GET' => true,
38
        'PUT' => true,
39
        // PATCH is automatically supported if at least one patch format has been configured
40
        'DELETE' => true,
41
    ];
42
43
    private $decorated;
44
    private $patchFormats;
45
46
    public function __construct(ResourceMetadataFactoryInterface $decorated, array $patchFormats = [])
47
    {
48
        $this->decorated = $decorated;
49
        $this->patchFormats = $patchFormats;
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function create(string $resourceClass): ResourceMetadata
56
    {
57
        $resourceMetadata = $this->decorated->create($resourceClass);
58
        $isAbstract = (new \ReflectionClass($resourceClass))->isAbstract();
59
60
        $collectionOperations = $resourceMetadata->getCollectionOperations();
61
        if (null === $collectionOperations) {
62
            $resourceMetadata = $resourceMetadata->withCollectionOperations($this->createOperations($isAbstract ? ['GET'] : ['GET', 'POST']));
63
        } else {
64
            $resourceMetadata = $this->normalize(true, $resourceClass, $resourceMetadata, $collectionOperations);
65
        }
66
67
        $itemOperations = $resourceMetadata->getItemOperations();
68
        if (null === $itemOperations) {
69
            $methods = ['GET', 'DELETE'];
70
71
            if (!$isAbstract) {
72
                $methods[] = 'PUT';
73
74
                if ($this->patchFormats) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->patchFormats of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
75
                    $methods[] = 'PATCH';
76
                }
77
            }
78
79
            $resourceMetadata = $resourceMetadata->withItemOperations($this->createOperations($methods));
80
        } else {
81
            $resourceMetadata = $this->normalize(false, $resourceClass, $resourceMetadata, $itemOperations);
82
        }
83
84
        $graphql = $resourceMetadata->getGraphql();
85
        if (null === $graphql) {
86
            $resourceMetadata = $resourceMetadata->withGraphql(['item_query' => [], 'collection_query' => [], 'delete' => [], 'update' => [], 'create' => []]);
87
        } else {
88
            $resourceMetadata = $this->normalizeGraphQl($resourceMetadata, $graphql);
89
        }
90
91
        return $resourceMetadata;
92
    }
93
94
    private function createOperations(array $methods): array
95
    {
96
        $operations = [];
97
        foreach ($methods as $method) {
98
            $operations[strtolower($method)] = ['method' => $method];
99
        }
100
101
        return $operations;
102
    }
103
104
    private function normalize(bool $collection, string $resourceClass, ResourceMetadata $resourceMetadata, array $operations): ResourceMetadata
105
    {
106
        $newOperations = [];
107
        foreach ($operations as $operationName => $operation) {
108
            // e.g.: @ApiResource(itemOperations={"get"})
109
            if (\is_int($operationName) && \is_string($operation)) {
110
                $operationName = $operation;
111
                $operation = [];
112
            }
113
114
            $upperOperationName = strtoupper((string) $operationName);
115
            if ($collection) {
116
                $supported = isset(self::SUPPORTED_COLLECTION_OPERATION_METHODS[$upperOperationName]);
117
            } else {
118
                $supported = isset(self::SUPPORTED_ITEM_OPERATION_METHODS[$upperOperationName]) || ($this->patchFormats && 'PATCH' === $upperOperationName);
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->patchFormats of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
119
            }
120
121
            if (!isset($operation['method']) && !isset($operation['route_name'])) {
122
                if ($supported) {
123
                    $operation['method'] = $upperOperationName;
124
                } else {
125
                    @trigger_error(sprintf('The "route_name" attribute will not be set automatically again in API Platform 3.0, set it for the %s operation "%s" of the class "%s".', $collection ? 'collection' : 'item', $operationName, $resourceClass), E_USER_DEPRECATED);
126
                    $operation['route_name'] = $operationName;
127
                }
128
            }
129
130
            if (isset($operation['method'])) {
131
                $operation['method'] = strtoupper($operation['method']);
132
            }
133
134
            $newOperations[$operationName] = $operation;
135
        }
136
137
        return $collection ? $resourceMetadata->withCollectionOperations($newOperations) : $resourceMetadata->withItemOperations($newOperations);
138
    }
139
140
    private function normalizeGraphQl(ResourceMetadata $resourceMetadata, array $operations): ResourceMetadata
141
    {
142
        foreach ($operations as $operationName => $operation) {
143
            if (\is_int($operationName) && \is_string($operation)) {
144
                unset($operations[$operationName]);
145
                $operations[$operation] = [];
146
            }
147
        }
148
149
        return $resourceMetadata->withGraphql($operations);
150
    }
151
}
152