Pool::get()   A
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
c 1
b 0
f 0
nc 4
nop 3
dl 0
loc 16
rs 9.2222
1
<?php
2
3
namespace Helix\Asana\Api;
4
5
use Closure;
6
use Helix\Asana\Api;
7
use Helix\Asana\Base\AbstractEntity;
8
use Helix\Asana\Base\Data;
9
10
/**
11
 * Pools entities in runtime memory.
12
 */
13
class Pool {
14
15
    /**
16
     * `[ gid => entity ]`
17
     *
18
     * @var AbstractEntity[]
19
     */
20
    private $entities = [];
21
22
    /**
23
     * `[ key => gid ]`
24
     *
25
     * @var string[];
26
     */
27
    private $gids = [];
28
29
    /**
30
     * @param AbstractEntity $entity
31
     */
32
    protected function _add (AbstractEntity $entity): void {
33
        assert($entity->hasGid());
34
        $gid = $entity->getGid();
35
        $this->entities[$gid] = $entity;
36
        $this->gids[$gid] = $gid;
37
    }
38
39
    /**
40
     * @param AbstractEntity $entity
41
     * @param string[] $keys
42
     */
43
    protected function _addKeys (AbstractEntity $entity, ...$keys): void {
44
        assert($entity->hasGid());
45
        $this->gids += array_fill_keys($keys, $entity->getGid());
46
    }
47
48
    /**
49
     * @param string $key
50
     * @param Api|Data $caller For hydration if needed.
51
     * @return null|AbstractEntity
52
     */
53
    protected function _get (string $key, $caller) {
54
        if (isset($this->gids[$key])) {
55
            return $this->entities[$this->gids[$key]];
56
        }
57
        unset($caller);
58
        return null;
59
    }
60
61
    /**
62
     * Polls. The entity may be gone by the time this returns (cache race).
63
     *
64
     * @param string $key
65
     * @return bool
66
     */
67
    protected function _has (string $key): bool {
68
        return isset($this->gids[$key]);
69
    }
70
71
    /**
72
     * This is final to ensure pH balance.
73
     *
74
     * Subclasses must override the internal methods instead of this.
75
     *
76
     * @param AbstractEntity $entity
77
     */
78
    final public function add (AbstractEntity $entity): void {
79
        assert($entity->hasGid());
80
        if (!$entity->isDiff()) {
81
            $this->_add($entity);
82
            $this->_addKeys($entity, ...$entity->getPoolKeys());
83
        }
84
    }
85
86
    /**
87
     * This is final to ensure pH balance.
88
     *
89
     * Subclasses must override the internal methods instead of this.
90
     *
91
     * @param string $key
92
     * @param Api|Data $caller
93
     * @param Closure $factory `fn( Api|Data $caller ): null|AbstractEntity`
94
     * @return null|mixed|AbstractEntity
95
     */
96
    final public function get (string $key, $caller, Closure $factory) {
97
        /** @var AbstractEntity $entity */
98
        if (!$entity = $this->_get($key, $caller) and $entity = $factory($caller)) {
99
            $gid = $entity->getGid();
100
            // duplicate with dynamic key? (e.g. "/users/me")
101
            if ($this->_has($gid) and $pooled = $this->_get($gid, $caller)) { // poll & fetch
102
                if ($pooled->__merge($entity)) { // new data?
103
                    $this->add($pooled); // renew everything
104
                }
105
                $this->_addKeys($pooled, $key);
106
                return $pooled;
107
            }
108
            $this->add($entity);
109
            $this->_addKeys($entity, $key);
110
        }
111
        return $entity;
112
    }
113
114
    /**
115
     * @param string[] $keys
116
     */
117
    public function remove (array $keys): void {
118
        foreach ($keys as $key) {
119
            unset($this->entities[$key]);
120
            unset($this->gids[$key]);
121
        }
122
    }
123
124
}