Endpoint   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 492
Duplicated Lines 0 %

Test Coverage

Coverage 84.06%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 61
eloc 106
c 1
b 0
f 0
dl 0
loc 492
ccs 116
cts 138
cp 0.8406
rs 3.52

35 Methods

Rating   Name   Duplication   Size   Complexity  
A add() 0 11 3
A getMutation() 0 3 1
A getTypeForClass() 0 3 1
A addQuery() 0 8 2
A getTypesForClass() 0 15 2
A addMutation() 0 8 2
A getSubscriptionNameForResolver() 0 5 1
A getClassForType() 0 7 2
A addSubscription() 0 11 3
A setSubscriptions() 0 5 2
A addType() 0 21 5
A allTypes() 0 3 1
A removeSubscription() 0 5 1
A getType() 0 13 5
A hasSubscription() 0 3 1
A allQueries() 0 3 1
A getQuery() 0 3 1
A getName() 0 3 1
A allSubscriptions() 0 3 1
A getSubscriptionResolver() 0 3 1
A removeType() 0 15 4
A hasQuery() 0 3 1
A allInterfaces() 0 3 1
A hasMutation() 0 3 1
A removeMutation() 0 5 1
A allMutations() 0 3 1
A getSubscriptionsResolvers() 0 3 1
A __construct() 0 3 1
A hasTypeForClass() 0 3 1
A hasType() 0 10 4
A removeQuery() 0 5 1
A getSubscription() 0 3 1
A setMutations() 0 5 2
A setQueries() 0 5 2
A setTypes() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like Endpoint often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Endpoint, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Definition\Registry;
12
13
use Ynlo\GraphQLBundle\Definition\ClassAwareDefinitionInterface;
14
use Ynlo\GraphQLBundle\Definition\DefinitionInterface;
15
use Ynlo\GraphQLBundle\Definition\InterfaceDefinition;
16
use Ynlo\GraphQLBundle\Definition\MutationDefinition;
17
use Ynlo\GraphQLBundle\Definition\QueryDefinition;
18
use Ynlo\GraphQLBundle\Definition\SubscriptionDefinition;
19
use Ynlo\GraphQLBundle\Resolver\EmptyObjectResolver;
20
21
/**
22
 * Class Endpoint
23
 */
24
class Endpoint
25
{
26
    /**
27
     * @var string
28
     */
29
    protected $name;
30
31
    /**
32
     * @var DefinitionInterface[]
33
     */
34
    protected $types = [];
35
36
    /**
37
     * @var string[]
38
     */
39
    protected $typeMap = [];
40
41
    /**
42
     * @var InterfaceDefinition[]
43
     */
44
    protected $interfaces = [];
45
46
    /**
47
     * @var MutationDefinition[]
48
     */
49
    protected $mutations = [];
50
51
    /**
52
     * @var QueryDefinition[]
53
     */
54
    protected $queries = [];
55
56
    /**
57
     * @var SubscriptionDefinition[]
58
     */
59
    protected $subscriptions = [];
60
61
    /**
62
     * Map subscriptions names to a resolver
63
     *
64
     * @var string[]
65
     */
66
    protected $subscriptionsMap = [];
67
68
    /**
69
     * Endpoint constructor.
70
     *
71
     * @param string $name
72
     */
73 102
    public function __construct(string $name)
74
    {
75 102
        $this->name = $name;
76 102
    }
77
78
    /**
79
     * @return string
80
     */
81 11
    public function getName(): string
82
    {
83 11
        return $this->name;
84
    }
85
86
    /**
87
     * @return DefinitionInterface[]
88
     */
89 5
    public function allTypes(): array
90
    {
91 5
        return $this->types;
92
    }
93
94
    /**
95
     * @param string $type
96
     *
97
     * @return null|string
98
     */
99 9
    public function getClassForType(string $type):?string
100
    {
101 9
        if (isset($this->typeMap[$type])) {
102 8
            return $this->typeMap[$type];
103
        }
104
105 2
        return null;
106
    }
107
108
    /**
109
     * @param string $class
110
     *
111
     * @return bool
112
     */
113 34
    public function hasTypeForClass(string $class): bool
114
    {
115 34
        return \in_array($class, $this->typeMap);
116
    }
117
118
    /**
119
     * @param string $class
120
     *
121
     * @return array|string[]
122
     */
123 27
    public function getTypesForClass(string $class): array
124
    {
125 27
        $types = array_filter(
126 27
            $this->typeMap,
127 27
            function ($val) use ($class) {
128 26
                return $val === $class;
129 27
            }
130
        );
131
132 27
        if (empty($types)) {
133 1
            $error = sprintf('Does not exist any valid type for class "%s"', $class);
134 1
            throw new \UnexpectedValueException($error);
135
        }
136
137 26
        return array_keys($types);
138
    }
139
140
    /**
141
     * Return the first type representing this class
142
     *
143
     * NOTE! a class can be represented by many types use `getTypesForClass`
144
     * to get all possible types.
145
     *
146
     * @param string $class
147
     *
148
     * @return string
149
     *
150
     * @throws \UnexpectedValueException if the class does not have any valid type
151
     */
152 22
    public function getTypeForClass(string $class): string
153
    {
154 22
        return $this->getTypesForClass($class)[0];
155
    }
156
157
    /**
158
     * Helper method to avoid in runtime
159
     * recurring all types to get only interfaces
160
     *
161
     * @return InterfaceDefinition[]
162
     */
163 4
    public function allInterfaces(): array
164
    {
165 4
        return $this->interfaces;
166
    }
167
168
    /**
169
     * @return array|MutationDefinition[]
170
     */
171 6
    public function allMutations(): array
172
    {
173 6
        return $this->mutations;
174
    }
175
176
    /**
177
     * @return array|QueryDefinition[]
178
     */
179 6
    public function allQueries(): array
180
    {
181 6
        return $this->queries;
182
    }
183
184
    /**
185
     * @return array|SubscriptionDefinition[]
186
     */
187 4
    public function allSubscriptions(): array
188
    {
189 4
        return $this->subscriptions;
190
    }
191
192
    /**
193
     * @return string[]
194
     */
195
    public function getSubscriptionsResolvers(): array
196
    {
197
        return $this->subscriptionsMap;
198
    }
199
200
    /**
201
     * @param string $resolver
202
     *
203
     * @return string
204
     */
205
    public function getSubscriptionNameForResolver($resolver): string
206
    {
207
        $byResolver = array_flip($this->subscriptionsMap);
208
209
        return $byResolver[$resolver] ?? null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $byResolver[$resolver] ?? null could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
210
    }
211
212
    /**
213
     * @param string $name
214
     *
215
     * @return string
216
     */
217
    public function getSubscriptionResolver($name): string
218
    {
219
        return $this->subscriptionsMap[$name] ?? null;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->subscriptionsMap[$name] ?? null could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
220
    }
221
222
    /**
223
     * @param DefinitionInterface[] $types
224
     */
225 2
    public function setTypes(array $types)
226
    {
227 2
        $this->types = [];
228 2
        $this->interfaces = [];
229 2
        foreach ($types as $type) {
230 2
            $this->addType($type);
231
        }
232 2
    }
233
234
    /**
235
     * @param MutationDefinition[] $mutations
236
     */
237 3
    public function setMutations(array $mutations)
238
    {
239 3
        $this->mutations = [];
240 3
        foreach ($mutations as $mutation) {
241 3
            $this->addMutation($mutation);
242
        }
243 3
    }
244
245
    /**
246
     * @param QueryDefinition[] $queries
247
     */
248 3
    public function setQueries(array $queries)
249
    {
250 3
        $this->queries = [];
251 3
        foreach ($queries as $query) {
252 3
            $this->addQuery($query);
253
        }
254 3
    }
255
256
    /**
257
     * @param SubscriptionDefinition[] $subscriptions
258
     */
259 2
    public function setSubscriptions(array $subscriptions)
260
    {
261 2
        $this->subscriptions = [];
262 2
        foreach ($subscriptions as $subscription) {
263
            $this->addSubscription($subscription);
264
        }
265 2
    }
266
267
    /**
268
     * @param string $name
269
     *
270
     * @return bool
271
     */
272 44
    public function hasType($name): bool
273
    {
274
        //in case pass FQN class name resolve the first matching type
275 44
        if (class_exists($name) || interface_exists($name)) {
276 3
            if ($this->hasTypeForClass($name)) {
277 3
                $name = $this->getTypesForClass($name)[0];
278
            }
279
        }
280
281 44
        return array_key_exists($name, $this->types);
282
    }
283
284
    /**
285
     * @param string $name
286
     *
287
     * @return bool
288
     */
289 12
    public function hasMutation($name): bool
290
    {
291 12
        return array_key_exists($name, $this->mutations);
292
    }
293
294
    /**
295
     * @param string $name
296
     *
297
     * @return bool
298
     */
299 14
    public function hasQuery($name): bool
300
    {
301 14
        return array_key_exists($name, $this->queries);
302
    }
303
304
    /**
305
     * @param string $name
306
     *
307
     * @return bool
308
     */
309
    public function hasSubscription($name): bool
310
    {
311
        return array_key_exists($name, $this->subscriptions);
312
    }
313
314
    /**
315
     * @param string $name
316
     *
317
     * @return Endpoint
318
     */
319 3
    public function removeType($name): Endpoint
320
    {
321 3
        if ($this->hasType($name)) {
322 3
            $type = $this->getType($name);
323 3
            if ($type instanceof InterfaceDefinition) {
324 2
                unset($this->interfaces[$type->getName()]);
325
            }
326
        }
327
328 3
        unset($this->types[$name]);
329 3
        if (isset($this->typeMap[$name])) {
330 3
            unset($this->typeMap[$name]);
331
        }
332
333 3
        return $this;
334
    }
335
336
    /**
337
     * @param string $name
338
     *
339
     * @return Endpoint
340
     */
341 2
    public function removeQuery($name): Endpoint
342
    {
343 2
        unset($this->queries[$name]);
344
345 2
        return $this;
346
    }
347
348
    /**
349
     * @param string $name
350
     *
351
     * @return Endpoint
352
     */
353 2
    public function removeMutation($name): Endpoint
354
    {
355 2
        unset($this->mutations[$name]);
356
357 2
        return $this;
358
    }
359
360
    /**
361
     * @param string $name
362
     *
363
     * @return Endpoint
364
     */
365
    public function removeSubscription($name): Endpoint
366
    {
367
        unset($this->subscriptions[$name]);
368
369
        return $this;
370
    }
371
372
    /**
373
     * @param string $name
374
     *
375
     * @return DefinitionInterface
376
     *
377
     * @throws \UnexpectedValueException
378
     */
379 38
    public function getType($name)
380
    {
381 38
        if (class_exists($name) || interface_exists($name)) {
382 18
            if ($this->hasTypeForClass($name)) {
383 18
                $name = $this->getTypeForClass($name);
384
            }
385
        }
386
387 38
        if (!$this->hasType($name)) {
388 1
            throw new \UnexpectedValueException(sprintf('Does not exist a valid definition for "%s" type', $name));
389
        }
390
391 37
        return $this->types[$name];
392
    }
393
394
    /**
395
     * @param string $name
396
     *
397
     * @return MutationDefinition
398
     */
399 7
    public function getMutation($name): MutationDefinition
400
    {
401 7
        return $this->mutations[$name];
402
    }
403
404
    /**
405
     * @param string $name
406
     *
407
     * @return SubscriptionDefinition
408
     */
409
    public function getSubscription($name): SubscriptionDefinition
410
    {
411
        return $this->subscriptions[$name];
412
    }
413
414
    /**
415
     * @param string $name
416
     *
417
     * @return QueryDefinition
418
     */
419 9
    public function getQuery($name): QueryDefinition
420
    {
421 9
        return $this->queries[$name];
422
    }
423
424
    /**
425
     * @param DefinitionInterface $definition
426
     *
427
     * @return Endpoint
428
     */
429 17
    public function add(DefinitionInterface $definition): Endpoint
430
    {
431 17
        if ($definition instanceof MutationDefinition) {
432 1
            return $this->addMutation($definition);
433
        }
434
435 16
        if ($definition instanceof QueryDefinition) {
436 2
            return $this->addQuery($definition);
437
        }
438
439 15
        return $this->addType($definition);
440
    }
441
442
    /**
443
     * @param DefinitionInterface $type
444
     *
445
     * @return Endpoint
446
     */
447 39
    public function addType(DefinitionInterface $type): Endpoint
448
    {
449 39
        if ($this->hasType($type->getName())) {
450 1
            throw new \RuntimeException(sprintf('Duplicate definition for type with name "%s"', $type->getName()));
451
        }
452 39
        $this->types[$type->getName()] = $type;
453
454 39
        if ($type instanceof InterfaceDefinition) {
455 34
            $this->interfaces[$type->getName()] = $type;
456
        }
457
458 39
        if ($type instanceof ClassAwareDefinitionInterface) {
459 39
            if ($type->getClass()) {
460 38
                $class = $type->getClass();
461
                //all classes are saved without \ at the beginning
462 38
                $class = preg_replace('/^\\\\/', null, $class);
463 38
                $this->typeMap[$type->getName()] = $class;
464
            }
465
        }
466
467 39
        return $this;
468
    }
469
470
    /**
471
     * @param MutationDefinition $mutation
472
     *
473
     * @return Endpoint
474
     */
475 12
    public function addMutation(MutationDefinition $mutation): Endpoint
476
    {
477 12
        if ($this->hasMutation($mutation->getName())) {
478 1
            throw new \RuntimeException(sprintf('Duplicate definition for query with name "%s"', $mutation->getName()));
479
        }
480 12
        $this->mutations[$mutation->getName()] = $mutation;
481
482 12
        return $this;
483
    }
484
485
    /**
486
     * @param QueryDefinition $query
487
     *
488
     * @return Endpoint
489
     */
490 14
    public function addQuery(QueryDefinition $query): Endpoint
491
    {
492 14
        if ($this->hasQuery($query->getName())) {
493 1
            throw new \RuntimeException(sprintf('Duplicate definition for query with name "%s"', $query->getName()));
494
        }
495 14
        $this->queries[$query->getName()] = $query;
496
497 14
        return $this;
498
    }
499
500
    /**
501
     * @param SubscriptionDefinition $subscription
502
     *
503
     * @return Endpoint
504
     */
505
    public function addSubscription(SubscriptionDefinition $subscription): Endpoint
506
    {
507
        if ($this->hasSubscription($subscription->getName())) {
508
            throw new \RuntimeException(sprintf('Duplicate definition for subscription with name "%s"', $subscription->getName()));
509
        }
510
        if (!is_a($subscription->getResolver(), EmptyObjectResolver::class, true)) {
511
            $this->subscriptionsMap[$subscription->getName()] = $subscription->getResolver();
512
        }
513
        $this->subscriptions[$subscription->getName()] = $subscription;
514
515
        return $this;
516
    }
517
}
518