Skip to content

Commit ac334b1

Browse files
authored
Merge pull request #109 from marc-mabe/immutable-set
Immutable EnumSet
2 parents 4a31f69 + a96c681 commit ac334b1

File tree

6 files changed

+838
-212
lines changed

6 files changed

+838
-212
lines changed

README.md

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Total Downloads](https://poser.pugx.org/marc-mabe/php-enum/downloads.png)](https://packagist.org/packages/marc-mabe/php-enum)
66
[![Latest Stable](https://poser.pugx.org/marc-mabe/php-enum/v/stable.png)](https://packagist.org/packages/marc-mabe/php-enum)
77

8-
This is a native PHP implementation to add enumeration support to PHP >= 5.3.
8+
This is a native PHP implementation to add enumeration support to PHP.
99
It's an abstract class that needs to be extended to use it.
1010

1111

@@ -204,40 +204,62 @@ But of course this solution has downsides, too:
204204

205205
## EnumSet
206206

207-
An `EnumSet` groups enumerators of the same enumeration type together.
207+
An `EnumSet` is a specialized Set implementation for use with enumeration types.
208+
All of the enumerators in an `EnumSet` must come from a single enumeration type that is specified, when the set is
209+
created.
208210

209-
It implements `Iterator` and `Countable`
210-
so elements can be iterated and counted like a normal array
211-
using `foreach` and `count()`.
211+
Enum sets are represented internally as bit vectors. The bit vektor is eigther an integer type or a binary string type
212+
depending on how many enumerators are defined is the enumeration type. This representation is extremely compact and
213+
efficient. Bulk operations will run very quickly. Enumerators of an `EnumSet` are unique and ordered based on it's
214+
ordinal number by design.
212215

213-
Internally it's based on a bitset. Integer bitset or binary bitset
214-
depending on how many enumerators are defined for the given enumeration.
216+
It implements `IteratorAggregate` and `Countable` to be directly iterable with `foreach` and countable with `count()`.
215217

216-
Enumerators attached to an `EnumSet` are unique and ordered based on it's ordinal number by design.
218+
The `EnumSet` has a mutable and an immutable interface.
219+
Mutable methods starts with `set`, `attach` and `detach`.
220+
Immutable methods starts with `with` or `without`.
217221

218222
```php
219223
use MabeEnum\EnumSet;
220224

221-
// create a new EnumSet
222-
$enumSet = new EnumSet('UserStatus');
225+
// create a new EnumSet and initialize with the given enumerators
226+
$enumSet = new EnumSet('UserStatus', [UserStatus::ACTIVE()]);
223227

228+
// modify an EnumSet (mutable interface)
224229

225230
// attach enumerators (by value or by instance)
226-
$enumSet->attach(UserStatus::INACTIVE);
227-
$enumSet->attach(UserStatus::ACTIVE());
228-
$enumSet->attach(UserStatus::DELETED());
231+
$enumSet->attachEnumerators([UserStatus::INACTIVE, UserStatus::DELETED()]);
232+
// or
233+
$enumSet->attachEnumerator(UserStatus::INACTIVE);
234+
$enumSet->attachEnumerator(UserStatus::DELETED());
229235

236+
// detach enumerators (by value or by instance)
237+
$enumSet->detachEnumerators([UserStatus::INACTIVE, UserStatus::DELETED()]);
238+
// or
239+
$enumSet->detachEnumerator(UserStatus::INACTIVE);
240+
$enumSet->detachEnumerator(UserStatus::DELETED());
241+
242+
243+
// The immutable interface will create a new EnumSet for each modification
244+
245+
// add enumerators (by value or by instance)
246+
$enumSet = $enumSet->withEnumerators([UserStatus::INACTIVE, UserStatus::DELETED()]);
247+
// or
248+
$enumSet = $enumSet->withEnumerator(UserStatus::INACTIVE);
249+
$enumSet = $enumSet->withEnumerator(UserStatus::DELETED());
230250

231251
// detach enumerators (by value or by instance)
232-
$enumSet->detach(UserStatus::INACTIVE);
233-
$enumSet->detach(UserStatus::DELETED());
252+
$enumSet->withoutEnumerators([UserStatus::INACTIVE, UserStatus::DELETED()]);
253+
// or
254+
$enumSet = $enumSet->withEnumerator(UserStatus::INACTIVE);
255+
$enumSet = $enumSet->withEnumerator(UserStatus::DELETED());
234256

235257

236258
// contains enumerators (by value or by instance)
237259
$enumSet->contains(UserStatus::INACTIVE); // bool
238260

239261

240-
// count number of attached enumerations
262+
// count the number of enumerators
241263
$enumSet->count();
242264
count($enumSet);
243265

@@ -261,12 +283,23 @@ $enumSet->isEqual($other); // Check if the EnumSet is the same as other
261283
$enumSet->isSubset($other); // Check if the EnumSet is a subset of other
262284
$enumSet->isSuperset($other); // Check if the EnumSet is a superset of other
263285

264-
$enumSet->union($other); // Produce a new set with enumerators from both this and other (this | other)
265-
$enumSet->intersect($other); // Produce a new set with enumerators common to both this and other (this & other)
266-
$enumSet->diff($other); // Produce a new set with enumerators in this but not in other (this - other)
267-
$enumSet->symDiff($other); // Produce a new set with enumerators in either this and other but not in both (this ^ other)
286+
287+
// union, intersect, difference and symmetric difference
288+
289+
// ... the mutable interface will modify the set
290+
$enumSet->setUnion($other); // Enumerators from both this and other (this | other)
291+
$enumSet->setIntersect($other); // Enumerators common to both this and other (this & other)
292+
$enumSet->setDiff($other); // Enumerators in this but not in other (this - other)
293+
$enumSet->setSymDiff($other); // Enumerators in either this and other but not in both (this ^ other)
294+
295+
// ... the immutable interface will produce a new set
296+
$enumSet = $enumSet->withUnion($other); // Enumerators from both this and other (this | other)
297+
$enumSet = $enumSet->withIntersect($other); // Enumerators common to both this and other (this & other)
298+
$enumSet = $enumSet->withDiff($other); // Enumerators in this but not in other (this - other)
299+
$enumSet = $enumSet->withSymDiff($other); // Enumerators in either this and other but not in both (this ^ other)
268300
```
269301

302+
270303
## EnumMap
271304

272305
An `EnumMap` maps enumerators of the same type to data assigned to.
@@ -331,6 +364,7 @@ $enumMap->getKeys();
331364
$enumMap->getValues();
332365
```
333366

367+
334368
## Serializing
335369

336370
Because this enumeration implementation is based on a singleton pattern and in PHP
@@ -373,6 +407,7 @@ var_dump($north1->is($north2)); // returns TRUE - this way the two instances are
373407
var_dump($north2->is($north1)); // returns TRUE - equality works in both directions
374408
```
375409

410+
376411
# Why not `SplEnum`
377412

378413
* `SplEnum` is not build-in into PHP and requires pecl extension installed.

bench/EnumSet32Bench.php

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,40 +48,105 @@ public function init()
4848
$this->enumerators = Enum32::getEnumerators();
4949

5050
$this->emptySet = new EnumSet(Enum32::class);
51-
$this->fullSet = new EnumSet(Enum32::class);
51+
$this->fullSet = new EnumSet(Enum32::class, $this->enumerators);
52+
}
53+
54+
public function benchAttachEnumerator()
55+
{
5256
foreach ($this->enumerators as $enumerator) {
53-
$this->fullSet->attach($enumerator);
57+
$this->emptySet->attachEnumerator($enumerator);
5458
}
5559
}
5660

57-
public function benchAttachEnumerator()
61+
public function benchWithEnumerator()
5862
{
5963
foreach ($this->enumerators as $enumerator) {
60-
$this->emptySet->attach($enumerator);
64+
$this->emptySet->withEnumerator($enumerator);
65+
}
66+
}
67+
68+
public function benchAttachEnumerators()
69+
{
70+
$this->emptySet->withEnumerators($this->enumerators);
71+
}
72+
73+
public function benchWithEnumerators()
74+
{
75+
$this->emptySet->attachEnumerators($this->enumerators);
76+
}
77+
78+
public function benchWithValue()
79+
{
80+
foreach ($this->values as $value) {
81+
$this->emptySet->withEnumerator($value);
6182
}
6283
}
6384

6485
public function benchAttachValue()
6586
{
6687
foreach ($this->values as $value) {
67-
$this->emptySet->attach($value);
88+
$this->emptySet->attachEnumerator($value);
6889
}
6990
}
7091

92+
public function benchAttachValues()
93+
{
94+
$this->emptySet->attachEnumerators($this->values);
95+
}
96+
97+
public function benchWithValues()
98+
{
99+
$this->emptySet->withEnumerators($this->values);
100+
}
101+
71102
public function benchDetachEnumerator()
72103
{
73104
foreach ($this->enumerators as $enumerator) {
74-
$this->fullSet->detach($enumerator);
105+
$this->fullSet->detachEnumerator($enumerator);
75106
}
76107
}
77108

109+
public function benchWithoutEnumerator()
110+
{
111+
foreach ($this->enumerators as $enumerator) {
112+
$this->fullSet->withoutEnumerator($enumerator);
113+
}
114+
}
115+
116+
public function benchDetachEnumerators()
117+
{
118+
$this->fullSet->detachEnumerators($this->enumerators);
119+
}
120+
121+
public function benchWithoutEnumerators()
122+
{
123+
$this->fullSet->withoutEnumerators($this->enumerators);
124+
}
125+
78126
public function benchDetachValue()
79127
{
80128
foreach ($this->values as $value) {
81-
$this->fullSet->detach($value);
129+
$this->fullSet->detachEnumerator($value);
130+
}
131+
}
132+
133+
public function benchWithoutValue()
134+
{
135+
foreach ($this->values as $value) {
136+
$this->fullSet->withoutEnumerator($value);
82137
}
83138
}
84139

140+
public function benchDetachValues()
141+
{
142+
$this->fullSet->detachEnumerators($this->values);
143+
}
144+
145+
public function benchWithoutValues()
146+
{
147+
$this->fullSet->withoutEnumerators($this->values);
148+
}
149+
85150
public function benchContainsEnumerator()
86151
{
87152
foreach ($this->enumerators as $enumerator) {
@@ -135,24 +200,44 @@ public function benchIsSuperset()
135200
$this->fullSet->isSuperset($this->fullSet);
136201
}
137202

138-
public function benchUnion()
203+
public function benchSetUnion()
204+
{
205+
$this->fullSet->setUnion($this->emptySet);
206+
}
207+
208+
public function benchWithUnion()
209+
{
210+
$this->fullSet->withUnion($this->emptySet);
211+
}
212+
213+
public function benchSetIntersect()
214+
{
215+
$this->fullSet->setIntersect($this->emptySet);
216+
}
217+
218+
public function benchWithIntersect()
219+
{
220+
$this->fullSet->withIntersect($this->emptySet);
221+
}
222+
223+
public function benchSetDiff()
139224
{
140-
$this->fullSet->union($this->emptySet);
225+
$this->fullSet->setDiff($this->emptySet);
141226
}
142227

143-
public function benchIntersect()
228+
public function benchWithDiff()
144229
{
145-
$this->fullSet->intersect($this->emptySet);
230+
$this->fullSet->withDiff($this->emptySet);
146231
}
147232

148-
public function benchDiff()
233+
public function benchSetSymDiff()
149234
{
150-
$this->fullSet->diff($this->emptySet);
235+
$this->fullSet->setSymDiff($this->emptySet);
151236
}
152237

153-
public function benchSymDiff()
238+
public function benchWithSymDiff()
154239
{
155-
$this->fullSet->symDiff($this->emptySet);
240+
$this->fullSet->withSymDiff($this->emptySet);
156241
}
157242

158243
public function benchGetOrdinalsFull()

0 commit comments

Comments
 (0)