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

DataLoader.php$0 ➔ serializeKey()   A

Complexity

Conditions 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 10
cc 3
crap 12
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