Dht::deleteEntityNode()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Yiranzai\Dht;
4
5
/**
6
 * Class Hash
7
 * @package Yiranzai\Dht
8
 */
9
class Dht implements \JsonSerializable
10
{
11
12
    public const DEFAULT_ALGO = 'time33';
13
    /**
14
     * @var string
15
     */
16
    protected $algo = self::DEFAULT_ALGO;
17
    /**
18
     * all node cache
19
     *
20
     * @var array
21
     */
22
    protected $locations = [];
23
    /**
24
     * virtual node num
25
     *
26
     * @var int
27
     */
28
    protected $virtualNodeNum = 24;
29
    /**
30
     * entity node cache
31
     *
32
     * @var
33
     */
34
    protected $nodes = [];
35
36
    /**
37
     * Hash constructor.
38
     * @param array $config
39
     */
40 24
    public function __construct($config = [])
41
    {
42 24
        if (!empty($config)) {
43 3
            foreach ($config as $key => $item) {
44 3
                if ($key === 'algo') {
45 3
                    $this->algo($item);
46 3
                    continue;
47
                }
48 3
                $this->$key = $item;
49
            }
50
        }
51 24
    }
52
53
    /**
54
     * @param string $str
55
     * @return $this
56
     */
57 6
    public function algo(string $str): self
58
    {
59 6
        if ($this->isSupportHashAlgos($str)) {
60 6
            $this->algo = $str;
61
        }
62 6
        return $this;
63
    }
64
65
    /**
66
     * @param string $str
67
     * @return bool
68
     */
69 6
    public function isSupportHashAlgos(string $str): bool
70
    {
71 6
        return $str === self::DEFAULT_ALGO || in_array($str, $this->supportHashAlgos(), true);
72
    }
73
74
    /**
75
     * @return array
76
     */
77 6
    public function supportHashAlgos(): array
78
    {
79 6
        return hash_algos();
80
    }
81
82
    /**
83
     * @return string
84
     */
85 6
    public function toJson(): string
86
    {
87 6
        $json = json_encode($this->jsonSerialize());
88 6
        if (json_last_error() !== JSON_ERROR_NONE) {
89
            throw new \RuntimeException(json_last_error_msg());
90
        }
91
92 6
        return $json;
93
    }
94
95
    /**
96
     * Convert the object into something JSON serializable.
97
     *
98
     * @return array
99
     */
100 6
    public function jsonSerialize(): array
101
    {
102 6
        return $this->toArray();
103
    }
104
105
    /**
106
     * @return array
107
     */
108 9
    public function toArray(): array
109
    {
110 9
        $array = array();
111 9
        foreach ($this as $key => $value) {
112 9
            $array[$key] = $value;
113
        }
114 9
        return $array;
115
    }
116
117
    /**
118
     * @param string $str
119
     * @return int
120
     */
121 21
    public function hashGenerate(string $str): int
122
    {
123 21
        if ($this->algo === self::DEFAULT_ALGO) {
124 15
            return $this->time33($str);
125
        }
126 6
        return (int)sprintf('%u', hash($this->algo, $str));
127
    }
128
129
    /**
130
     * @param string $str
131
     * @return int
132
     */
133 15
    protected function time33(string $str): int
134
    {
135 15
        $hash = 0;
136 15
        $str  = md5($str);
137 15
        $len  = 32;
138 15
        for ($i = 0; $i < $len; $i++) {
139 15
            $hash = ($hash << 5) + $hash + ord($str{$i});
140
        }
141 15
        return $hash & 0x7FFFFFFF;
142
    }
143
144
    /**
145
     * 寻找字符串所在的机器位置
146
     * @param string $key
147
     * @return string
148
     */
149 6
    public function getLocation(string $key): string
150
    {
151 6
        if (empty($this->locations)) {
152 3
            throw new \RuntimeException('This nodes is empty, please add a node');
153
        }
154
155 3
        $position = $this->hashGenerate($key);
156
        //默认取第一个节点
157 3
        $node = current($this->locations);
158 3
        foreach ($this->locations as $k => $v) {
159
            //如果当前的位置,小于或等于节点组中的一个节点,那么当前位置对应该节点
160 3
            if ($position <= $k) {
161 3
                $node = $v;
162 3
                break;
163
            }
164
        }
165 3
        return $node;
166
    }
167
168
    /**
169
     * 添加一个节点
170
     * @param string $node
171
     * @return Dht
172
     */
173 18
    public function addEntityNode(string $node): self
174
    {
175 18
        if ($this->existsNode($node)) {
176 3
            throw new \RuntimeException('This node already exists');
177
        }
178 18
        $this->nodes[$node] = [];
179
        //生成虚拟节点
180 18
        for ($i = 0; $i < $this->virtualNodeNum; $i++) {
181 18
            $tmp                   = $this->hashGenerate($node . $i);
182 18
            $this->locations[$tmp] = $node;
183 18
            $this->nodes[$node][]  = $tmp;
184
        }
185
        //对节点排序
186 18
        ksort($this->locations, SORT_NUMERIC);
187 18
        return $this;
188
    }
189
190
    /**
191
     * @param string $node
192
     * @return bool
193
     */
194 18
    public function existsNode(string $node): bool
195
    {
196 18
        return array_key_exists($node, $this->nodes);
197
    }
198
199
    /**
200
     * delete a entity node
201
     *
202
     * @param string $node
203
     * @return Dht
204
     */
205 3
    public function deleteEntityNode(string $node): self
206
    {
207 3
        foreach ($this->nodes[$node] as $v) {
208 3
            unset($this->locations[$v]);
209
        }
210 3
        unset($this->nodes[$node]);
211 3
        return $this;
212
    }
213
214
    /**
215
     * @param int $num
216
     * @return $this
217
     */
218 6
    public function virtualNodeNum(int $num): self
219
    {
220 6
        $this->virtualNodeNum = $num;
221 6
        return $this;
222
    }
223
}
224