1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @license https://github.com/f500/equatable/blob/master/LICENSE MIT |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
declare(strict_types=1); |
8
|
|
|
|
9
|
|
|
namespace F500\Equatable; |
10
|
|
|
|
11
|
|
|
use ArrayIterator; |
12
|
|
|
use Countable; |
13
|
|
|
use IteratorAggregate; |
14
|
|
|
use Traversable; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @copyright Copyright (c) 2015 Future500 B.V. |
18
|
|
|
* @author Jasper N. Brouwer <[email protected]> |
19
|
|
|
*/ |
20
|
|
|
final class EquatableMap implements Equatable, Countable, IteratorAggregate |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* @var Equatable[] |
24
|
|
|
*/ |
25
|
|
|
private $items = []; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @param Equatable[] $items |
29
|
|
|
*/ |
30
|
|
View Code Duplication |
public function __construct(array $items = []) |
|
|
|
|
31
|
|
|
{ |
32
|
|
|
foreach ($items as $key => $value) { |
33
|
|
|
if (!$value instanceof Equatable) { |
34
|
|
|
throw InvalidArgumentException::invalidTypeInArray('items', Equatable::class, $value); |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
$this->items[$key] = $value; |
38
|
|
|
} |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function __clone() |
|
|
|
|
42
|
|
|
{ |
43
|
|
|
$items = []; |
44
|
|
|
|
45
|
|
|
foreach ($this->items as $key => $item) { |
46
|
|
|
$items[$key] = clone $item; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
$this->items = $items; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
View Code Duplication |
public function add(string $key, Equatable $value): EquatableMap |
|
|
|
|
53
|
|
|
{ |
54
|
|
|
if ($this->containsKey($key)) { |
55
|
|
|
throw InRangeException::keyInRange($key); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$items = $this->items; |
59
|
|
|
|
60
|
|
|
$items[$key] = $value; |
61
|
|
|
|
62
|
|
|
return new static($items); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
View Code Duplication |
public function remove(Equatable $value): EquatableMap |
|
|
|
|
66
|
|
|
{ |
67
|
|
|
$key = $this->search($value); |
68
|
|
|
$items = $this->items; |
69
|
|
|
|
70
|
|
|
unset($items[$key]); |
71
|
|
|
|
72
|
|
|
return new static($items); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
View Code Duplication |
public function replace(string $key, Equatable $value): EquatableMap |
|
|
|
|
76
|
|
|
{ |
77
|
|
|
if (!$this->containsKey($key)) { |
78
|
|
|
throw OutOfRangeException::keyOutOfRange($key); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
$items = $this->items; |
82
|
|
|
|
83
|
|
|
$items[$key] = $value; |
84
|
|
|
|
85
|
|
|
return new static($items); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
public function get(string $key): Equatable |
89
|
|
|
{ |
90
|
|
|
if (!$this->containsKey($key)) { |
91
|
|
|
throw OutOfRangeException::keyOutOfRange($key); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
return $this->items[$key]; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
View Code Duplication |
public function search(Equatable $value): string |
|
|
|
|
98
|
|
|
{ |
99
|
|
|
foreach ($this->items as $key => $item) { |
100
|
|
|
if ($item->equals($value)) { |
101
|
|
|
return $key; |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
throw OutOfRangeException::valueOutOfRange($value); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
View Code Duplication |
public function searchAll(Equatable $value): array |
|
|
|
|
109
|
|
|
{ |
110
|
|
|
$foundKeys = []; |
111
|
|
|
|
112
|
|
|
foreach ($this->items as $index => $item) { |
113
|
|
|
if ($item->equals($value)) { |
114
|
|
|
$foundKeys[] = $index; |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
if (!$foundKeys) { |
|
|
|
|
119
|
|
|
throw OutOfRangeException::valueOutOfRange($value); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return $foundKeys; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
public function contains(Equatable $value): bool |
126
|
|
|
{ |
127
|
|
|
try { |
128
|
|
|
$this->search($value); |
129
|
|
|
return true; |
130
|
|
|
} catch (OutOfRangeException $e) { |
131
|
|
|
return false; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
public function containsKey(string $key): bool |
136
|
|
|
{ |
137
|
|
|
return isset($this->items[$key]); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
View Code Duplication |
public function equals($other): bool |
|
|
|
|
141
|
|
|
{ |
142
|
|
|
if (!$other instanceof static) { |
143
|
|
|
return false; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
if ($this->count() !== $other->count()) { |
147
|
|
|
return false; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
foreach ($this->items as $item) { |
151
|
|
|
if ($this->countItem($item) !== $other->countItem($item)) { |
152
|
|
|
return false; |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return true; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
public function count(): int |
160
|
|
|
{ |
161
|
|
|
return count($this->items); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
View Code Duplication |
public function countItem(Equatable $value): int |
|
|
|
|
165
|
|
|
{ |
166
|
|
|
$count = 0; |
167
|
|
|
|
168
|
|
|
foreach ($this->items as $item) { |
169
|
|
|
if ($item->equals($value)) { |
170
|
|
|
$count++; |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
return $count; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
public function getIterator(): Traversable |
178
|
|
|
{ |
179
|
|
|
return new ArrayIterator($this->items); |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
|
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.