1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the pinepain/php-object-maps PHP library. |
5
|
|
|
* |
6
|
|
|
* Copyright (c) 2016-2017 Bogdan Padalko <[email protected]> |
7
|
|
|
* |
8
|
|
|
* Licensed under the MIT license: http://opensource.org/licenses/MIT |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please view the LICENSE |
11
|
|
|
* file that was distributed with this source code or visit http://opensource.org/licenses/MIT |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
|
15
|
|
|
namespace Pinepain\ObjectMaps; |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
use OutOfBoundsException; |
19
|
|
|
use OverflowException; |
20
|
|
|
use Ref\WeakReference; |
21
|
|
|
|
22
|
|
|
use function spl_object_hash; |
23
|
|
|
|
24
|
|
|
|
25
|
|
|
class ObjectMap implements ObjectMapInterface |
26
|
|
|
{ |
27
|
|
|
const WEAK_KEY = 1 << 0; |
28
|
|
|
const WEAK_VALUE = 1 << 1; |
29
|
|
|
const WEAK_KEY_VALUE = self::WEAK_KEY | self::WEAK_VALUE; |
30
|
|
|
|
31
|
|
|
protected $behavior = 0; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var Bucket[] |
35
|
|
|
*/ |
36
|
|
|
protected $keys = []; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @param int $behavior |
40
|
|
|
*/ |
41
|
1 |
|
public function __construct(int $behavior = 0) |
42
|
|
|
{ |
43
|
1 |
|
$this->behavior = $behavior; |
44
|
1 |
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* {@inheritdoc} |
48
|
|
|
*/ |
49
|
1 |
|
public function put($key, $value) |
50
|
|
|
{ |
51
|
1 |
|
$hash = $this->getHash($key); |
52
|
|
|
|
53
|
1 |
|
if (isset($this->keys[$hash])) { |
54
|
|
|
throw new OverflowException('Value with such key already exists'); |
55
|
|
|
} |
56
|
|
|
|
57
|
1 |
|
$bucket = $this->createBucket($key, $value, $hash); |
58
|
|
|
|
59
|
1 |
|
$this->keys[$hash] = $bucket; |
60
|
1 |
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* {@inheritdoc} |
64
|
|
|
*/ |
65
|
1 |
|
public function get($key) |
66
|
|
|
{ |
67
|
1 |
|
$hash = $this->getHash($key); |
68
|
|
|
|
69
|
1 |
|
if (!isset($this->keys[$hash])) { |
70
|
|
|
throw new OutOfBoundsException('Value with such key not found'); |
71
|
|
|
} |
72
|
|
|
|
73
|
1 |
|
return $this->keys[$hash]->value; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* {@inheritdoc} |
78
|
|
|
*/ |
79
|
|
|
public function has($key): bool |
80
|
|
|
{ |
81
|
|
|
$hash = $this->getHash($key); |
82
|
|
|
|
83
|
|
|
return isset($this->keys[$hash]); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* {@inheritdoc} |
88
|
|
|
*/ |
89
|
|
|
public function remove($key) |
90
|
|
|
{ |
91
|
|
|
$hash = $this->getHash($key); |
92
|
|
|
|
93
|
|
|
if (!isset($this->keys[$hash])) { |
94
|
|
|
throw new OutOfBoundsException('Value with such key not found'); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
$bucket = $this->keys[$hash]; |
98
|
|
|
|
99
|
|
|
$this->doRemove($hash); |
100
|
|
|
|
101
|
|
|
return $bucket->value; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* {@inheritdoc} |
106
|
|
|
*/ |
107
|
|
|
public function count() |
108
|
|
|
{ |
109
|
|
|
return count($this->keys); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* {@inheritdoc} |
114
|
|
|
*/ |
115
|
|
|
public function clear() |
116
|
|
|
{ |
117
|
|
|
$this->keys = []; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @param $value |
122
|
|
|
* |
123
|
|
|
* @return string |
124
|
|
|
*/ |
125
|
1 |
|
protected function getHash($value) |
126
|
|
|
{ |
127
|
1 |
|
return spl_object_hash($value); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param string $hash |
132
|
|
|
* |
133
|
|
|
* @return void |
134
|
|
|
*/ |
135
|
|
|
protected function doRemove(string $hash) |
136
|
|
|
{ |
137
|
|
|
unset($this->keys[$hash]); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @param object $key |
142
|
|
|
* @param object $value |
143
|
|
|
* @param string $hash |
144
|
|
|
* |
145
|
|
|
* @return Bucket |
146
|
|
|
*/ |
147
|
1 |
|
protected function createBucket($key, $value, string $hash) |
148
|
|
|
{ |
149
|
1 |
View Code Duplication |
if ($this->behavior & self::WEAK_KEY) { |
|
|
|
|
150
|
|
|
$key = new WeakReference($key, function () use ($hash) { |
151
|
|
|
$this->doRemove($hash); |
152
|
|
|
}); |
153
|
|
|
} |
154
|
|
|
|
155
|
1 |
View Code Duplication |
if ($this->behavior & self::WEAK_VALUE) { |
|
|
|
|
156
|
|
|
$value = new WeakReference($value, function () use ($hash) { |
157
|
|
|
$this->doRemove($hash); |
158
|
|
|
}); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
return new Bucket($key, $value); |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
|
166
|
|
|
|
167
|
|
|
|
168
|
|
|
|
169
|
|
|
|
170
|
|
|
|
171
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.