ValidatedFieldDefinition::tryInferName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 9
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Type\Definition;
6
7
use ReflectionClass;
8
use ReflectionException;
9
use function array_filter;
10
use function array_keys;
11
use function count;
12
use function is_array;
13
use function is_callable;
14
use function lcfirst;
15
use function preg_replace;
16
use function range;
17
use function ucfirst;
18
19
class ValidatedFieldDefinition extends FieldDefinition
20
{
21
    /** @var callable */
22
    protected $typeSetter;
23
    protected $validFieldName;
24
    protected $resultFieldName;
25
26
    /**
27
     * @param mixed[] $config
28
     */
29 18
    public function __construct(array $config)
30
    {
31 18
        $args = $config['args'];
32 18
        $name = $config['name'] ?? lcfirst($this->tryInferName());
33
34 18
        $this->validFieldName = $config['validName'] ?? 'valid';
35 18
        $this->resultFieldName = $config['resultName'] ?? 'result';
36
37 18
        parent::__construct([
38
            'type' => function() use($name, $config, $args) {
39 16
				return static::_create($name, $args, $config);
0 ignored issues
show
Bug Best Practice introduced by
The method GraphQL\Type\Definition\...ldDefinition::_create() is not static, but was called statically. ( Ignorable by Annotation )

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

39
				return static::/** @scrutinizer ignore-call */ _create($name, $args, $config);
Loading history...
40 18
			},
41 18
            'args' => $args,
42 18
            'name' => $name,
43
            'resolve' => function ($value, $args1, $context, $info) use ($config, $args) {
44
                // validate inputs
45 12
                $config['type'] = new InputObjectType([
46 12
                    'name'=>'',
47 12
                    'fields' => $args,
48
                ]);
49 12
                $config['isRoot'] = true;
50
51 12
                $errors          = $this->_validate($config, $args1);
52 12
                $result          = $errors;
53 12
                $result[$this->validFieldName] = !$errors;
54
55 12
                if (!empty($result['valid'])) {
56 1
                    $result[$this->resultFieldName] = $config['resolve']($value, $args1, $context, $info);
57
                }
58
59 12
                return $result;
60 18
            },
61
        ]);
62 18
    }
63
64 16
    protected function _create($name, $args, $config) {
65 16
		return UserErrorsType::create([
66 16
			'errorCodes' => $config['errorCodes'] ?? null,
67
			'isRoot' => true,
68
			'fields' => [
69 16
				$this->resultFieldName => [
70 16
					'type' => $config['type'],
71 16
					'description' => 'The payload, if any',
72
					'resolve' => static function ($value) {
73 12
						return $value['result'] ?? null;
74 16
					},
75
				],
76 16
				$this->validFieldName => [
77 16
					'type' => Type::nonNull(Type::boolean()),
78 16
					'description' => 'Whether all validation passed. True for yes, false for no.',
79
					'resolve' => static function ($value) {
80 12
						return $value['valid'];
81 16
					},
82
				],
83
			],
84 16
			'validate' => $config['validate'] ?? null,
85 16
			'type' => new InputObjectType([
86 16
				'fields' => $args,
87 16
				'name' => '',
88
			]),
89 16
			'typeSetter' => $config['typeSetter'] ?? null,
90 16
		], [$name], false, ucfirst($name) . 'Result');
91
	}
92
93 1
    private function _noop($value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

93
    private function _noop(/** @scrutinizer ignore-unused */ $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
94
        // this is just a no-op validation function to fallback to when no validation function is provided
95 1
        return 0;
96
    }
97
98
    /**
99
     * @param   mixed[] $arr
100
     */
101 4
    protected function _isAssoc(array $arr) : bool
102
    {
103 4
        if ($arr === []) {
104 1
            return false;
105
        }
106 4
        return array_keys($arr) !== range(0, count($arr) - 1);
107
    }
108
109
    /**
110
     * @param   mixed[]  $value
111
     * @param   string[] $path
112
     *
113
     * @throws  ValidateItemsError
114
     */
115 4
    protected function _validateItems($config, array $value, array $path, callable $validate) : void
116
    {
117 4
        foreach ($value as $idx => $subValue) {
118 4
            if (is_array($subValue) && !$this->_isAssoc($subValue)) {
119 2
                $path[count($path)-1] = $idx;
120 2
                $newPath              = $path;
121 2
                $newPath[]            = 0;
122 2
                $this->_validateItems($config, $subValue, $newPath, $validate );
123
            } else {
124 4
                $path[count($path) - 1] = $idx;
125 4
                $err                    = $validate($subValue);
126
127 4
                if(empty($err)) {
128 4
                    $wrappedType = $config['type']->getInnermostType();
129 4
                    $err = $this->_validate([
130 4
                        'type' => $wrappedType
131 4
                    ], $subValue, $config['type'] instanceof ListOfType);
132
                }
133
134 4
                if ($err) {
135 4
                    throw new ValidateItemsError($path, $err);
136
                }
137
            }
138
        }
139 1
    }
140
141
    /**
142
     * @param mixed[] $arg
143
     * @param mixed $value
144
     * @return mixed[]
145
     */
146 12
    protected function _validate(array $arg, $value, bool $isParentList = false) : array
147
    {
148 12
        $res = [];
149
	    
150 12
        if (is_callable($arg['type'])) {
151 1
            $arg['type'] =  $arg['type']();
152
        }
153
154 12
        $type = $arg['type'];
155
        switch ($type) {
156 12
            case $type instanceof ListOfType:
157 4
                $this->_validateListOfType($arg, $value, $res);
158 4
                break;
159
160 12
            case $type instanceof NonNull:
161 2
                $arg['type'] = $type->getWrappedType();
162 2
                $res         = $this->_validate($arg, $value);
163 2
                break;
164
165 12
            case $type instanceof InputObjectType:
166 12
                $this->_validateInputObject($arg, $value, $res, $isParentList);
167 12
                break;
168
169
            default:
170 11
                if (is_callable($arg['validate'] ?? null)) {
171 9
                    $res['error'] = $arg['validate']($value) ?? [];
172
                }
173
        }
174
175 12
        return array_filter($res);
176
    }
177
178 4
    protected function _validateListOfType(array $config, $value, array &$res) {
179
        try {
180 4
            $this->_validateItems($config, $value, [0], $config['validate'] ?? [$this, "_noop"]);
181 4
        } catch (ValidateItemsError $e) {
182 4
            if(isset($e->error['suberrors'])) {
183 2
                $err = $e->error;
184
            }
185
            else {
186
                $err = [
187 2
                    'error' => $e->error,
188
                ];
189
            }
190 4
            $err['path'] = $e->path;
191 4
            $res[] = $err;
192
        }
193 4
    }
194
195 12
    protected function _validateInputObject($arg, $value, &$res, bool $isParentList) {
196 12
        $type = $arg['type'];
197 12
        if (isset($arg['validate'])) {
198 4
            $err = $arg['validate']($value) ?? [];
199 4
            if ($err) {
200 1
                $res['error'] = $err;
201 1
                return;
202
            }
203
        }
204
205 11
        $this->_validateInputObjectFields($type, $arg, $value, $res, $isParentList);
206 11
    }
207
208 11
    protected function _validateInputObjectFields($type, array $config, $value, &$res, $isParentList = false) {
209 11
        $createSubErrors = UserErrorsType::needSuberrors($config, $isParentList);
210
211 11
        $fields = $type->getFields();
212 11
        if (is_array($value)) {
213 11
            foreach ($value as $key => $subValue) {
214 11
                $config                 = $fields[$key]->config;
215 11
                $error = $this->_validate($config, $subValue);
216
217 11
                if($error) {
218 11
                    $createSubErrors ? $res[UserErrorsType::SUBERRORS_NAME][$key] = $error : $res[$key] = $error;
219
                }
220
            }
221
        }
222 11
    }
223
224
    /**
225
     * @return mixed|string|string[]|null
226
     *
227
     * @throws ReflectionException
228
     */
229 1
    protected function tryInferName()
230
    {
231
        // If class is extended - infer name from className
232
        // QueryType -> Type
233
        // SomeOtherType -> SomeOther
234 1
        $tmp  = new ReflectionClass($this);
235 1
        $name = $tmp->getShortName();
236
237 1
        return preg_replace('~Type$~', '', $name);
238
    }
239
}
240