Issues (167)

src/Util/ValueConverter.php (2 issues)

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
        // Populate the fields of the input object by creating ASTs from each value
74
        // in the PHP object according to the fields in the input type.
75
        if ($type instanceof InputObjectType) {
76
            return self::convertInputObjectType($value, $type);
77
        }
78
79
        if ($type instanceof ScalarType || $type instanceof EnumType) {
80
            return self::convertScalarOrEnum($value, $type);
81
        }
82
83
        throw new ConversionException(\sprintf('Unknown type: %s.', (string)$type));
84
    }
85
86
    /**
87
     * @param mixed    $value
88
     * @param ListType $type
89
     * @return ValueNodeInterface|null
90
     * @throws InvariantException
91
     * @throws SyntaxErrorException
92
     * @throws ConversionException
93
     */
94
    protected static function convertListType($value, ListType $type): ?ValueNodeInterface
95
    {
96
        $itemType = $type->getOfType();
97
98
        if (!\is_iterable($value)) {
99
            return self::convert($value, $itemType);
100
        }
101
102
        $nodes = [];
103
104
        foreach ($value as $item) {
105
            $itemNode = self::convert($item, $itemType);
106
            if (null !== $itemNode) {
107
                $nodes[] = $itemNode;
108
            }
109
        }
110
111
        return new ListValueNode($nodes, null);
112
    }
113
114
    /**
115
     * @param mixed           $value
116
     * @param InputObjectType $type
117
     * @return ObjectValueNode|null
118
     * @throws InvariantException
119
     * @throws SyntaxErrorException
120
     * @throws ConversionException
121
     */
122
    protected static function convertInputObjectType($value, InputObjectType $type): ?ObjectValueNode
123
    {
124
        if (null === $value || !\is_object($value)) {
125
            return null;
126
        }
127
128
        /** @var InputField[] $fields */
129
        $fields = \array_values($type->getFields());
130
131
        $nodes = [];
132
133
        foreach ($fields as $field) {
134
            $fieldName  = $field->getName();
135
            $fieldValue = self::convert($value[$fieldName], $field->getType());
0 ignored issues
show
It seems like $field->getType() can also be of type null; however, parameter $type of Digia\GraphQL\Util\ValueConverter::convert() does only seem to accept Digia\GraphQL\Type\Definition\TypeInterface, 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

135
            $fieldValue = self::convert($value[$fieldName], /** @scrutinizer ignore-type */ $field->getType());
Loading history...
136
137
            if (null !== $fieldValue) {
138
                $nodes[] = new ObjectFieldNode(new NameNode($fieldName, null), $fieldValue, null);
139
            }
140
        }
141
142
        return new ObjectValueNode($nodes, null);
143
    }
144
145
    /**
146
     * @param mixed                     $value
147
     * @param SerializableTypeInterface $type
148
     * @return ValueNodeInterface|null
149
     * @throws ConversionException
150
     */
151
    protected static function convertScalarOrEnum($value, SerializableTypeInterface $type): ?ValueNodeInterface
152
    {
153
        // Since value is an internally represented value, it must be serialized
154
        // to an externally represented value before converting into an AST.
155
        $serialized = $type->serialize($value);
156
157
        if (null === $serialized) {
158
            return null;
159
        }
160
161
        // Others serialize based on their corresponding JavaScript scalar types.
162
        if (\is_bool($serialized)) {
163
            return new BooleanValueNode($serialized, null);
164
        }
165
166
        if (\is_float($serialized)) {
167
            return new FloatValueNode($serialized, null);
168
        }
169
170
        if (\is_int($serialized)) {
171
            return new IntValueNode($serialized, null);
172
        }
173
174
        if (\is_string($serialized)) {
175
            // Enum types use Enum literals.
176
            if ($type instanceof EnumType) {
177
                return new EnumValueNode($serialized, null);
178
            }
179
180
            // ID types can use Int literals.
181
            if (idType() === $type && \is_numeric($serialized)) {
0 ignored issues
show
The condition idType() === $type is always false.
Loading history...
182
                return new IntValueNode($serialized, null);
183
            }
184
185
            return new StringValueNode($serialized, false, null);
186
        }
187
188
        throw new ConversionException(\sprintf('Cannot convert value to AST: %s', $serialized));
189
    }
190
}
191