Completed
Pull Request — master (#338)
by Sam
04:18
created

ValueConverter::convertInputObjectType()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 4
nop 2
dl 0
loc 21
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace Digia\GraphQL\Util;
4
5
use Digia\GraphQL\Util\ConversionException;
6
use Digia\GraphQL\Error\InvariantException;
7
use Digia\GraphQL\Language\SyntaxErrorException;
8
use Digia\GraphQL\Language\Node\BooleanValueNode;
9
use Digia\GraphQL\Language\Node\EnumValueNode;
10
use Digia\GraphQL\Language\Node\FloatValueNode;
11
use Digia\GraphQL\Language\Node\IntValueNode;
12
use Digia\GraphQL\Language\Node\ListValueNode;
13
use Digia\GraphQL\Language\Node\NameNode;
14
use Digia\GraphQL\Language\Node\NodeKindEnum;
15
use Digia\GraphQL\Language\Node\NullValueNode;
16
use Digia\GraphQL\Language\Node\ObjectFieldNode;
17
use Digia\GraphQL\Language\Node\ObjectValueNode;
18
use Digia\GraphQL\Language\Node\StringValueNode;
19
use Digia\GraphQL\Language\Node\ValueNodeInterface;
20
use Digia\GraphQL\Type\Definition\EnumType;
21
use Digia\GraphQL\Type\Definition\InputField;
22
use Digia\GraphQL\Type\Definition\InputObjectType;
23
use Digia\GraphQL\Type\Definition\ListType;
24
use Digia\GraphQL\Type\Definition\NonNullType;
25
use Digia\GraphQL\Type\Definition\ScalarType;
26
use Digia\GraphQL\Type\Definition\SerializableTypeInterface;
27
use Digia\GraphQL\Type\Definition\TypeInterface;
28
use function Digia\GraphQL\Type\idType;
29
30
class ValueConverter
31
{
32
    /**
33
     * Produces a GraphQL Value AST given a PHP value.
34
     *
35
     * A GraphQL type must be provided, which will be used to interpret different
36
     * PHP values.
37
     *
38
     * | JSON Value    | GraphQL Value        |
39
     * | ------------- | -------------------- |
40
     * | Object        | Input Object         |
41
     * | Array         | List                 |
42
     * | Boolean       | Boolean              |
43
     * | String        | String / Enum Value  |
44
     * | Number        | Int / Float          |
45
     * | Mixed         | Enum Value           |
46
     * | null          | NullValue            |
47
     *
48
     * @param mixed         $value
49
     * @param TypeInterface $type
50
     * @return ValueNodeInterface|null
51
     * @throws InvariantException
52
     * @throws SyntaxErrorException
53
     * @throws ConversionException
54
     */
55
    public static function convert($value, TypeInterface $type): ?ValueNodeInterface
56
    {
57
        if ($type instanceof NonNullType) {
58
            $node = self::convert($value, $type->getOfType());
59
60
            return null !== $node && $node->getKind() === NodeKindEnum::NULL ? null : $node;
61
        }
62
63
        if (null === $value) {
64
            return new NullValueNode(null);
65
        }
66
67
        // Convert PHP array to GraphQL list. If the GraphQLType is a list, but
68
        // the value is not an array, convert the value using the list's item type.
69
        if ($type instanceof ListType) {
70
            return self::convertListType($value, $type);
71
        }
72
73
        if ($type instanceof ScalarType || $type instanceof EnumType) {
74
            return self::convertScalarOrEnum($value, $type);
75
        }
76
77
        throw new ConversionException(\sprintf('Unknown type: %s.', (string)$type));
78
    }
79
80
    /**
81
     * @param mixed    $value
82
     * @param ListType $type
83
     * @return ValueNodeInterface|null
84
     * @throws InvariantException
85
     * @throws SyntaxErrorException
86
     * @throws ConversionException
87
     */
88
    protected static function convertListType($value, ListType $type): ?ValueNodeInterface
89
    {
90
        $itemType = $type->getOfType();
91
92
        if (!\is_iterable($value)) {
93
            return self::convert($value, $itemType);
94
        }
95
96
        $nodes = [];
97
98
        foreach ($value as $item) {
99
            $itemNode = self::convert($item, $itemType);
100
            if (null !== $itemNode) {
101
                $nodes[] = $itemNode;
102
            }
103
        }
104
105
        return new ListValueNode($nodes, null);
106
    }
107
108
    /**
109
     * @param mixed                     $value
110
     * @param SerializableTypeInterface $type
111
     * @return ValueNodeInterface|null
112
     * @throws ConversionException
113
     */
114
    protected static function convertScalarOrEnum($value, SerializableTypeInterface $type): ?ValueNodeInterface
115
    {
116
        // Since value is an internally represented value, it must be serialized
117
        // to an externally represented value before converting into an AST.
118
        $serialized = $type->serialize($value);
119
120
        if (null === $serialized) {
121
            return null;
122
        }
123
124
        // Others serialize based on their corresponding JavaScript scalar types.
125
        if (\is_bool($serialized)) {
126
            return new BooleanValueNode($serialized, null);
127
        }
128
129
        if (\is_float($serialized)) {
130
            return new FloatValueNode($serialized, null);
131
        }
132
133
        if (\is_int($serialized)) {
134
            return new IntValueNode($serialized, null);
135
        }
136
137
        if (\is_string($serialized)) {
138
            // Enum types use Enum literals.
139
            if ($type instanceof EnumType) {
140
                return new EnumValueNode($serialized, null);
141
            }
142
143
            // ID types can use Int literals.
144
            if (idType() === $type && \is_numeric($serialized)) {
0 ignored issues
show
introduced by
The condition idType() === $type is always false.
Loading history...
145
                return new IntValueNode($serialized, null);
146
            }
147
148
            return new StringValueNode($serialized, false, null);
149
        }
150
151
        throw new ConversionException(\sprintf('Cannot convert value to AST: %s', $serialized));
152
    }
153
}
154