Passed
Pull Request — master (#351)
by Kirill
02:29
created

EnumType::buildValues()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 36
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 22
nc 6
nop 1
dl 0
loc 36
rs 8.9457
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Type\Definition;
4
5
use Digia\GraphQL\Error\InvariantException;
6
use Digia\GraphQL\Language\Node\ASTNodeAwareInterface;
7
use Digia\GraphQL\Language\Node\ASTNodeTrait;
8
use Digia\GraphQL\Language\Node\EnumTypeDefinitionNode;
9
use Digia\GraphQL\Language\Node\EnumValueNode;
10
use Digia\GraphQL\Language\Node\NodeInterface;
11
use Digia\GraphQL\Schema\Definition;
12
use GraphQL\Contracts\TypeSystem\EnumValueInterface;
13
use GraphQL\Contracts\TypeSystem\Type\EnumTypeInterface;
14
use function Digia\GraphQL\Type\isAssocArray;
15
use function Digia\GraphQL\Type\newEnumValue;
16
use function Digia\GraphQL\Util\toString;
17
18
/**
19
 * Enum Type Definition
20
 *
21
 * Some leaf values of requests and input values are Enums. GraphQL serializes
22
 * Enum values as strings, however internally Enums can be represented by any
23
 * kind of type, often integers.
24
 *
25
 * Example:
26
 *
27
 *     $RGBType = newEnumType([
28
 *       'name'   => 'RGB',
29
 *       'values' => [
30
 *         'RED'   => ['value' => 0],
31
 *         'GREEN' => ['value' => 1],
32
 *         'BLUE'  => ['value' => 2]
33
 *       ]
34
 *     ]);
35
 *
36
 * Note: If a value is not provided in a definition, the name of the enum value
37
 * will be used as its internal value.
38
 */
39
class EnumType extends Definition implements EnumTypeInterface, SerializableTypeInterface, ASTNodeAwareInterface
40
{
41
    use NameTrait;
42
    use DescriptionTrait;
43
    use ASTNodeTrait;
44
45
    /**
46
     * @var array
47
     */
48
    protected $rawValues;
49
50
    /**
51
     * A list of enum value instances.
52
     *
53
     * @var EnumValue[]
54
     */
55
    protected $values;
56
57
    /**
58
     * EnumType constructor.
59
     *
60
     * @param string                      $name
61
     * @param null|string                 $description
62
     * @param EnumValue[]                 $rawValues
63
     * @param EnumTypeDefinitionNode|null $astNode
64
     */
65
    public function __construct(string $name, ?string $description, array $rawValues, ?EnumTypeDefinitionNode $astNode)
66
    {
67
        $this->name        = $name;
68
        $this->description = $description;
69
        $this->astNode     = $astNode;
70
        $this->rawValues   = $rawValues;
71
    }
72
73
    /**
74
     * @param mixed $value
75
     * @return null|string
76
     * @throws InvariantException
77
     */
78
    public function serialize($value)
79
    {
80
        $enumValue = $this->getValueByValue($value);
81
82
        if ($enumValue) {
83
            return $enumValue->getName();
84
        }
85
86
        return null;
87
    }
88
89
    /**
90
     * @param mixed $value
91
     * @return mixed|null
92
     * @throws InvariantException
93
     */
94
    public function parseValue($value)
95
    {
96
        if (\is_string($value)) {
97
            $enumValue = $this->getValueByName($value);
98
99
            if ($enumValue !== null) {
100
                return $enumValue->getValue();
101
            }
102
        }
103
104
        return null;
105
    }
106
107
    /**
108
     * @param NodeInterface $node
109
     * @return mixed|null
110
     * @throws InvariantException
111
     */
112
    public function parseLiteral(NodeInterface $node)
113
    {
114
        if ($node instanceof EnumValueNode) {
115
            $enumValue = $this->getValueByName($node->getValue());
0 ignored issues
show
Bug introduced by
It seems like $node->getValue() can also be of type null; however, parameter $name of Digia\GraphQL\Type\Defin...mType::getValueByName() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

115
            $enumValue = $this->getValueByName(/** @scrutinizer ignore-type */ $node->getValue());
Loading history...
116
117
            if ($enumValue !== null) {
118
                return $enumValue->getValue();
119
            }
120
        }
121
122
        return null;
123
    }
124
125
    /**
126
     * @param string $name
127
     * @return EnumValue|EnumValueInterface|null
128
     * @throws InvariantException
129
     */
130
    public function getValue(string $name): ?EnumValueInterface
131
    {
132
        return $this->getValueByName($name);
133
    }
134
135
    /**
136
     * @param string $name
137
     * @return bool
138
     * @throws InvariantException
139
     */
140
    public function hasValue(string $name): bool
141
    {
142
        return $this->getValue($name) !== null;
143
    }
144
145
    /**
146
     * @return EnumValue[]
147
     * @throws InvariantException
148
     */
149
    public function getValues(): array
150
    {
151
        if (!isset($this->values)) {
152
            $this->values = $this->buildValues($this->rawValues);
153
        }
154
        return $this->values;
155
    }
156
157
    /**
158
     * @param string $name
159
     * @return EnumValue|null
160
     * @throws InvariantException
161
     */
162
    protected function getValueByName(string $name): ?EnumValue
163
    {
164
        foreach ($this->getValues() as $enumValue) {
165
            if ($enumValue->getName() === $name) {
166
                return $enumValue;
167
            }
168
        }
169
170
        return null;
171
    }
172
173
    /**
174
     * @param mixed $value
175
     * @return EnumValue|null
176
     * @throws InvariantException
177
     */
178
    protected function getValueByValue($value): ?EnumValue
179
    {
180
        foreach ($this->getValues() as $enumValue) {
181
            if ($enumValue->getValue() === $value) {
182
                return $enumValue;
183
            }
184
        }
185
186
        return null;
187
    }
188
189
    /**
190
     * @param array $rawValues
191
     * @return array
192
     * @throws InvariantException
193
     */
194
    protected function buildValues(array $rawValues): array
195
    {
196
        if (!isAssocArray($rawValues)) {
197
            throw new InvariantException(\sprintf('%s values must be an associative array with value names as keys.',
198
                $this->name));
199
        }
200
201
        $values = [];
202
203
        foreach ($rawValues as $valueName => $valueConfig) {
204
            if (!isAssocArray($valueConfig)) {
205
                throw new InvariantException(\sprintf(
206
                    '%s.%s must refer to an object with a "value" key representing an internal value but got: %s.',
207
                    $this->name,
208
                    $valueName,
209
                    toString($valueConfig)
210
                ));
211
            }
212
213
            if (isset($valueConfig['isDeprecated'])) {
214
                throw new InvariantException(\sprintf(
215
                    '%s.%s should provide "deprecationReason" instead of "isDeprecated".',
216
                    $this->name,
217
                    $valueName
218
                ));
219
            }
220
221
            $valueConfig['name']  = $valueName;
222
            $valueConfig['value'] = \array_key_exists('value', $valueConfig)
223
                ? $valueConfig['value']
224
                : $valueName;
225
226
            $values[] = newEnumValue($valueConfig);
227
        }
228
229
        return $values;
230
    }
231
}
232