Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — annotations (#407)
by Vincent
17:47
created

ArgumentsTransformer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Transformer;
6
7
use GraphQL\Type\Definition\EnumType;
8
use GraphQL\Type\Definition\InputObjectType;
9
use GraphQL\Type\Definition\ListOfType;
10
use GraphQL\Type\Definition\ResolveInfo;
11
use GraphQL\Type\Definition\Type;
12
use Overblog\GraphQLBundle\Error\InvalidArgumentError;
13
use Overblog\GraphQLBundle\Error\InvalidArgumentsError;
14
use Symfony\Component\PropertyAccess\PropertyAccess;
15
use Symfony\Component\PropertyAccess\PropertyAccessor;
16
use Symfony\Component\Validator\Validator\ValidatorInterface;
17
18
class ArgumentsTransformer
19
{
20
    /**
21
     * @var ValidatorInterface
22
     */
23
    protected $validator;
24
25
    /**
26
     * @var array
27
     */
28
    protected $classesMap;
29
30
    /**
31
     * @var PropertyAccessor
32
     */
33
    protected $accessor;
34
35 1
    public function __construct(ValidatorInterface $validator, $classesMap = [])
36
    {
37 1
        $this->validator = $validator;
38 1
        $this->accessor = PropertyAccess::createPropertyAccessor();
39 1
        $this->classesMap = $classesMap;
40 1
    }
41
42
    /**
43
     * Get the PHP class for a given type.
44
     *
45
     * @param string $type
46
     *
47
     * @return object|false
48
     */
49 1
    private function getTypeClassInstance(string $type)
50
    {
51 1
        $classname = isset($this->classesMap[$type]) ? $this->classesMap[$type]['class'] : false;
52
53 1
        return $classname ? new $classname() : false;
54
    }
55
56
    /**
57
     * Extract given type from Resolve Info.
58
     *
59
     * @param string      $type
60
     * @param ResolveInfo $info
61
     *
62
     * @return Type
63
     */
64 1
    private function getType(string $type, ResolveInfo $info): Type
65
    {
66 1
        return $info->schema->getType($type);
67
    }
68
69
    /**
70
     * Populate an object based on type with given data.
71
     *
72
     * @param Type        $type
73
     * @param mixed       $data
74
     * @param bool        $multiple
75
     * @param ResolveInfo $info
76
     *
77
     * @return mixed
78
     */
79 1
    private function populateObject(Type $type, $data, $multiple = false, ResolveInfo $info)
80
    {
81 1
        if (!$data) {
82 1
            return $data;
83
        }
84
85 1
        if ($multiple) {
86
            return \array_map(function ($data) use ($type, $info) {
87 1
                return $this->populateObject($type, $data, false, $info);
88 1
            }, $data);
89
        }
90
91 1
        if ($type instanceof EnumType) {
92 1
            $instance = $this->getTypeClassInstance($type->name);
93 1
            if ($instance) {
94 1
                $this->accessor->setValue($instance, 'value', $data);
95
96 1
                return $instance;
97
            } else {
98 1
                return $data;
99
            }
100 1
        } elseif ($type instanceof InputObjectType) {
101 1
            $instance = $this->getTypeClassInstance($type->name);
102 1
            if (!$instance) {
103
                return $data;
104
            }
105
106 1
            $fields = $type->getFields();
107
108 1
            foreach ($fields as $name => $field) {
109 1
                $fieldData = $this->accessor->getValue($data, \sprintf('[%s]', $name));
110
111 1
                if ($field->getType() instanceof ListOfType) {
112 1
                    $fieldValue = $this->populateObject($field->getType()->getWrappedType(), $fieldData, true, $info);
113
                } else {
114 1
                    $fieldValue = $this->populateObject($field->getType(), $fieldData, false, $info);
115
                }
116
117 1
                $this->accessor->setValue($instance, $name, $fieldValue);
118
            }
119
120 1
            return $instance;
121
        } else {
122 1
            return $data;
123
        }
124
    }
125
126
    /**
127
     * Given a GraphQL type and an array of data, populate corresponding object recursively
128
     * using annoted classes.
129
     *
130
     * @param string      $argType
131
     * @param mixed       $data
132
     * @param ResolveInfo $info
133
     *
134
     * @return mixed
135
     */
136 1
    public function getInstanceAndValidate(string $argType, $data, ResolveInfo $info, string $argName)
137
    {
138 1
        $isRequired = '!' === $argType[\strlen($argType) - 1];
139 1
        $isMultiple = '[' === $argType[0];
140 1
        $endIndex = ($isRequired ? 1 : 0) + ($isMultiple ? 1 : 0);
141 1
        $type = \substr($argType, $isMultiple ? 1 : 0, $endIndex > 0 ? -$endIndex : \strlen($argType));
142
143 1
        $result = $this->populateObject($this->getType($type, $info), $data, $isMultiple, $info);
144 1
        $errors = [];
145 1
        if (\is_object($result)) {
146 1
            $errors = $this->validator->validate($result);
147
        }
148
149 1
        if (\count($errors) > 0) {
150
            throw new InvalidArgumentError($argName, $errors);
0 ignored issues
show
Bug introduced by
It seems like $errors defined by array() on line 144 can also be of type array; however, Overblog\GraphQLBundle\E...entError::__construct() does only seem to accept object<Symfony\Component...ViolationListInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
151
        } else {
152 1
            return $result;
153
        }
154
    }
155
156
    /**
157
     * Transform a list of arguments into their corresponding php class and validate them.
158
     *
159
     * @param array       $mapping
160
     * @param mixed       $data
161
     * @param ResolveInfo $info
162
     *
163
     * @return array
164
     */
165 1
    public function getArguments(array $mapping, $data, ResolveInfo $info)
166
    {
167 1
        $args = [];
168 1
        $exceptions = [];
169
170 1
        foreach ($mapping as $name => $type) {
171
            try {
172 1
                $value = $this->getInstanceAndValidate($type, $data[$name], $info, $name);
173 1
                $args[] = $value;
174
            } catch (InvalidArgumentError $exception) {
175 1
                $exceptions[] = $exception;
176
            }
177
        }
178
179 1
        if (!empty($exceptions)) {
180
            throw new InvalidArgumentsError($exceptions);
181
        }
182
183 1
        return $args;
184
    }
185
}
186