Test Failed
Pull Request — master (#42)
by
unknown
12:11 queued 04:14
created

DataLoader.php$0 ➔ makeLoader()   B

Complexity

Conditions 3

Size

Total Lines 79

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 79
ccs 16
cts 16
cp 1
rs 8.4581
cc 3
crap 3

5 Methods

Rating   Name   Duplication   Size   Complexity  
A DataLoader.php$0 ➔ load() 0 10 1
B DataLoader.php$0 ➔ resolve() 0 30 6
A DataLoader.php$0 ➔ __construct() 0 7 1
A DataLoader.php$0 ➔ serializeKey() 0 8 3
A DataLoader.php$0 ➔ scheduleResolveIfNeeded() 0 5 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Butler\Graphql;
4
5
use Amp\Deferred;
6
use Amp\Loop;
7
use Closure;
8
use ReflectionFunction;
9
10
class DataLoader
11
{
12
    private $loaders = [];
13
14
    /**
15
     * @param  callable|Closure  $batchLoadFunction
16
     */
17 31
    public function __invoke($batchLoadFunction, $defaultResolveValue = null)
18
    {
19 31
        if (! $batchLoadFunction instanceof Closure) {
20 31
            $batchLoadFunction = Closure::fromCallable($batchLoadFunction);
21
        }
22
23
        $identifier = $this->identifierForClosure($batchLoadFunction);
24
25 9
        return $this->loaders[$identifier] = $this->loaders[$identifier]
26
            ?? $this->makeLoader($batchLoadFunction, $defaultResolveValue);
27 9
    }
28 1
29
    private function identifierForClosure(Closure $closure)
30
    {
31 9
        $reflection = new ReflectionFunction($closure);
32
33 9
        return $reflection->getFileName() . '@' .
34 9
            $reflection->getStartLine() . '-' . $reflection->getEndLine();
35
    }
36
37 9
    private function makeLoader(Closure $batchLoadFunction, $defaultResolveValue)
38
    {
39 9
        return new class ($batchLoadFunction, $defaultResolveValue)
40
        {
41 9
            private $batchLoadFunction;
42 9
            private $defaultResolveValue;
43
44
            private $deferredPromises;
45 9
            private $needsResolving;
46
47 9
            public function __construct(Closure $batchLoadFunction, $defaultResolveValue = null)
48 9
            {
49 9
                $this->batchLoadFunction = $batchLoadFunction;
50 8
                $this->defaultResolveValue = $defaultResolveValue;
51 8
52
                $this->deferredPromises = [];
53 8
                $this->needsResolving = false;
54 9
            }
55 9
56 9
            public function load($key)
57
            {
58
                $serializedKey = self::serializeKey($key);
59
60 24
                [$deferred] = $this->deferredPromises[$serializedKey]
61
                    ?? $this->deferredPromises[$serializedKey] = [new Deferred(), $key];
62 24
63
                $this->scheduleResolveIfNeeded();
64
65
                return $deferred->promise();
66
            }
67
68
            private function scheduleResolveIfNeeded()
69
            {
70
                if (! $this->needsResolving) {
71
                    Loop::defer(Closure::fromCallable([$this, 'resolve']));
72
                    $this->needsResolving = true;
73
                }
74
            }
75
76
            private function resolve()
77
            {
78
                $indexedOriginalKeys = [];
79
                foreach ($this->deferredPromises as [$_, $originalKey]) {
80
                    $indexedOriginalKeys[] = $originalKey;
81
                }
82
83
                $result = ($this->batchLoadFunction)($indexedOriginalKeys);
84
85
                $currentIndex = 0;
86
                foreach ($result as $key => $value) {
87
                    if ($key === $currentIndex) {
88
                        $key = $indexedOriginalKeys[$key] ?? $key;
89
                    }
90
                    $key = self::serializeKey($key);
91
92
                    if ([$deferred] = $this->deferredPromises[$key] ?? null) {
93
                        $deferred->resolve($value);
94
                        unset($this->deferredPromises[$key]);
95
                    }
96
97
                    ++$currentIndex;
98
                }
99
100
                foreach ($this->deferredPromises as $key => [$deferredPromise]) {
101
                    $deferredPromise->resolve($this->defaultResolveValue);
102
                }
103
104
                $this->deferredPromises = [];
105
                $this->needsResolving = false;
106
            }
107
108
            private static function serializeKey($key)
109
            {
110
                if (is_object($key)) {
111
                    return spl_object_hash($key);
112
                } elseif (is_array($key)) {
113
                    return md5(json_encode($key));
114
                }
115
                return (string) $key;
116
            }
117
        };
118
    }
119
120
    public function run(): void
121
    {
122
        Loop::run();
123
    }
124
}
125