Skip to content

Commit 3b050f4

Browse files
author
Marc Bennewitz
committed
Merge pull request #16 from marc-mabe/EnumMap
EnumMap implementation
2 parents 7be34c3 + 66442ed commit 3b050f4

File tree

2 files changed

+641
-0
lines changed

2 files changed

+641
-0
lines changed

src/MabeEnum/EnumMap.php

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
<?php
2+
3+
namespace MabeEnum;
4+
5+
use SplObjectStorage;
6+
use InvalidArgumentException;
7+
use RuntimeException;
8+
9+
/**
10+
* EnumMap implementation in base of SplObjectStorage
11+
*
12+
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
13+
* @copyright Copyright (c) 2012 Marc Bennewitz
14+
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
15+
*/
16+
class EnumMap extends SplObjectStorage
17+
{
18+
19+
/**
20+
* key()-behaviour: return the current iterator position
21+
*/
22+
const KEY_AS_INDEX = 1;
23+
24+
/**
25+
* key()-behaviour: return the current enum name
26+
*/
27+
const KEY_AS_NAME = 2;
28+
29+
/**
30+
* key()-behaviour: return the current enum value
31+
*/
32+
const KEY_AS_VALUE = 3;
33+
34+
/**
35+
* key()-behaviour: return the current enum ordinal
36+
*/
37+
const KEY_AS_ORDINAL = 4;
38+
39+
/**
40+
* current()-behaviour: return the current enum object
41+
*/
42+
const CURRENT_AS_ENUM = 8;
43+
44+
/**
45+
* current()-behaviour: return data mapped the current enum
46+
*/
47+
const CURRENT_AS_DATA = 16;
48+
49+
/**
50+
* current()-behaviour: return the current enum name
51+
*/
52+
const CURRENT_AS_NAME = 24;
53+
54+
/**
55+
* current()-behaviour: return the current enum value
56+
*/
57+
const CURRENT_AS_VALUE = 32;
58+
59+
/**
60+
* current()-behaviour: return the current enum ordinal
61+
*/
62+
const CURRENT_AS_ORDINAL = 40;
63+
64+
/**
65+
* The classname of an enumeration this map is for
66+
* @var string
67+
*/
68+
private $enumClass;
69+
70+
/**
71+
* Flags to define behaviors
72+
* (Default = KEY_AS_INDEX | CURRENT_AS_ENUM)
73+
* @var int
74+
*/
75+
private $flags = 9;
76+
77+
/**
78+
* Constructor
79+
* @param string $enumClass The classname of an enumeration the map is for
80+
* @param int|null $flags Behaviour flags, see KEY_AS_* and CURRENT_AS_* constants
81+
*/
82+
public function __construct($enumClass, $flags = null)
83+
{
84+
if (!is_subclass_of($enumClass, __NAMESPACE__ . '\Enum')) {
85+
throw new InvalidArgumentException(sprintf(
86+
"This EnumMap can handle subclasses of '%s' only",
87+
__NAMESPACE__ . '\Enum'
88+
));
89+
}
90+
$this->enumClass = $enumClass;
91+
92+
if ($flags !== null) {
93+
$this->setFlags($flags);
94+
}
95+
}
96+
97+
/**
98+
* Get the classname of enumeration this map is for
99+
* @return string
100+
*/
101+
public function getEnumClass()
102+
{
103+
return $this->enumClass;
104+
}
105+
106+
/**
107+
* Set behaviour flags
108+
* see KEY_AS_* and CURRENT_AS_* constants
109+
* @param int $flags
110+
* @return void
111+
* @throws InvalidArgumentException On invalid or unsupported flags
112+
*/
113+
public function setFlags($flags)
114+
{
115+
$flags = (int)$flags;
116+
117+
$keyFlag = $flags & 7;
118+
if ($keyFlag > 4) {
119+
throw new InvalidArgumentException(
120+
"Unsupported flag given for key() behavior"
121+
);
122+
} elseif (!$keyFlag) {
123+
$keyFlag = $this->flags & 7;
124+
}
125+
126+
127+
$currentFlag = $flags & 56;
128+
if ($currentFlag > 40) {
129+
throw new InvalidArgumentException(
130+
"Unsupported flag given for current() behavior"
131+
);
132+
} elseif (!$currentFlag) {
133+
$currentFlag = $this->flags & 56;
134+
}
135+
136+
$this->flags = $keyFlag | $currentFlag;
137+
}
138+
139+
/**
140+
* Get the behaviour flags
141+
* @return int
142+
*/
143+
public function getFlags()
144+
{
145+
return $this->flags;
146+
}
147+
148+
/**
149+
* Attach a new enumeration or overwrite an existing one
150+
* @param Enum|scalar $enum
151+
* @param mixed $data
152+
* @return void
153+
* @throws InvalidArgumentException On an invalid given enum
154+
*/
155+
public function attach($enum, $data = null)
156+
{
157+
$this->initEnum($enum);
158+
parent::attach($enum, $data);
159+
}
160+
161+
/**
162+
* Test if the given enumeration exists
163+
* @param Enum|scalar $enum
164+
* @return boolean
165+
*/
166+
public function contains($enum)
167+
{
168+
try {
169+
$this->initEnum($enum);
170+
return parent::contains($enum);
171+
} catch (InvalidArgumentException $e) {
172+
// On an InvalidArgumentException the given argument can't be contained in this map
173+
return false;
174+
}
175+
}
176+
177+
/**
178+
* Detach an enumeration
179+
* @param Enum|scalar $enum
180+
* @return void
181+
* @throws InvalidArgumentException On an invalid given enum
182+
*/
183+
public function detach($enum)
184+
{
185+
$this->initEnum($enum);
186+
parent::detach($enum);
187+
}
188+
189+
/**
190+
* Get a unique identifier for the given enumeration
191+
* @param Enum|scalar $enum
192+
* @return string
193+
* @throws InvalidArgumentException On an invalid given enum
194+
*/
195+
public function getHash($enum)
196+
{
197+
$this->initEnum($enum);
198+
199+
// getHash is available since PHP 5.4
200+
return spl_object_hash($enum);
201+
}
202+
203+
/**
204+
* Test if the given enumeration exists
205+
* @param Enum|scalar $enum
206+
* @return boolean
207+
* @see contains()
208+
*/
209+
public function offsetExists($enum)
210+
{
211+
return $this->contains($enum);
212+
}
213+
214+
/**
215+
* Get mapped data for this given enum
216+
* @param Enum|scalar $enum
217+
* @return mixed
218+
* @throws InvalidArgumentException On an invalid given enum
219+
*/
220+
public function offsetGet($enum)
221+
{
222+
$this->initEnum($enum);
223+
return parent::offsetGet($enum);
224+
}
225+
226+
/**
227+
* Attach a new enumeration or overwrite an existing one
228+
* @param Enum|scalar $enum
229+
* @param mixed $data
230+
* @return void
231+
* @throws InvalidArgumentException On an invalid given enum
232+
* @see attach()
233+
*/
234+
public function offsetSet($enum, $data = null)
235+
{
236+
$this->initEnum($enum);
237+
parent::offsetSet($enum, $data);
238+
}
239+
240+
/**
241+
* Detach an existing enumeration
242+
* @param Enum|scalar $enum
243+
* @return void
244+
* @throws InvalidArgumentException On an invalid given enum
245+
* @see detach()
246+
*/
247+
public function offsetUnset($enum)
248+
{
249+
$this->initEnum($enum);
250+
parent::offsetUnset($enum);
251+
}
252+
253+
/**
254+
* Get the current item
255+
* The return value varied by the behaviour of the current flag
256+
* @return mixed
257+
*/
258+
public function current()
259+
{
260+
switch ($this->flags & 120) {
261+
case self::CURRENT_AS_ENUM:
262+
return parent::current();
263+
case self::CURRENT_AS_DATA:
264+
return parent::getInfo();
265+
case self::CURRENT_AS_VALUE:
266+
return parent::current()->getValue();
267+
case self::CURRENT_AS_NAME:
268+
return parent::current()->getName();
269+
case self::CURRENT_AS_ORDINAL:
270+
return parent::current()->getOrdinal();
271+
default:
272+
throw new RuntimeException('Invalid current flag');
273+
}
274+
}
275+
276+
/**
277+
* Get the current item-key
278+
* The return value varied by the behaviour of the key flag
279+
* @return scalar
280+
*/
281+
public function key()
282+
{
283+
switch ($this->flags & 7) {
284+
case self::KEY_AS_INDEX:
285+
return parent::key();
286+
case self::KEY_AS_NAME:
287+
return parent::current()->getName();
288+
case self::KEY_AS_ORDINAL:
289+
return parent::current()->getOrdinal();
290+
case self::KEY_AS_VALUE:
291+
return parent::current()->getValue();
292+
default:
293+
throw new RuntimeException('Invalid key flag');
294+
}
295+
}
296+
297+
/**
298+
* Initialize an enumeration
299+
* @param Enum|scalar $enum
300+
* @return Enum
301+
* @throws InvalidArgumentException On an invalid given enum
302+
*/
303+
private function initEnum(&$enum)
304+
{
305+
// auto instantiate
306+
if (is_scalar($enum)) {
307+
$enumClass = $this->enumClass;
308+
$enum = $enumClass::get($enum);
309+
return;
310+
}
311+
312+
// allow only enums of the same type
313+
// (don't allow instance of)
314+
$enumClass = get_class($enum);
315+
if ($enumClass && strcasecmp($enumClass, $this->enumClass) === 0) {
316+
return;
317+
}
318+
319+
throw new InvalidArgumentException(sprintf(
320+
"The given enum of type '%s' isn't same as the required type '%s'",
321+
get_class($enum) ?: gettype($enum),
322+
$this->enumClass
323+
));
324+
}
325+
}

0 commit comments

Comments
 (0)