|
4 | 4 |
|
5 | 5 | class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator
|
6 | 6 | {
|
7 |
| - private $container = array(); |
8 | 7 |
|
| 8 | + /** |
| 9 | + * @var mixed[] Data storage with lower-case keys |
| 10 | + * @see offsetSet() |
| 11 | + * @see offsetExists() |
| 12 | + * @see offsetUnset() |
| 13 | + * @see offsetGet() |
| 14 | + * @see count() |
| 15 | + * @see current() |
| 16 | + * @see next() |
| 17 | + * @see key() |
| 18 | + */ |
| 19 | + private $data = array(); |
| 20 | + |
| 21 | + /** |
| 22 | + * @var string[] Case-Sensitive keys. |
| 23 | + * @see offsetSet() |
| 24 | + * @see offsetUnset() |
| 25 | + * @see key() |
| 26 | + */ |
| 27 | + private $keys = array(); |
| 28 | + |
| 29 | + /** |
| 30 | + * Construct |
| 31 | + * |
| 32 | + * Allow creating either an empty Array, or convert an existing Array to a |
| 33 | + * Case-Insensitive Array. (Caution: Data may be lost when converting Case- |
| 34 | + * Sensitive Arrays to Case-Insensitive Arrays) |
| 35 | + * |
| 36 | + * @param mixed[] $initial (optional) Existing Array to convert. |
| 37 | + * |
| 38 | + * @return void |
| 39 | + * |
| 40 | + * @access public |
| 41 | + */ |
| 42 | + public function __construct(Array $initial = NULL) |
| 43 | + { |
| 44 | + if ($initial !== NULL) { |
| 45 | + foreach ($initial as $key => $value) { |
| 46 | + $this->offsetSet($key, $value); |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * Offset Set |
| 53 | + * |
| 54 | + * Set data at a specified Offset. Converts the offset to lower-case, and |
| 55 | + * stores the Case-Sensitive Offset and the Data at the lower-case indexes |
| 56 | + * in $this->keys and @this->data. |
| 57 | + * |
| 58 | + * @see https://secure.php.net/manual/en/arrayaccess.offseteset.php |
| 59 | + * |
| 60 | + * @param string $offset The offset to store the data at (case-insensitive). |
| 61 | + * @param mixed $value The data to store at the specified offset. |
| 62 | + * |
| 63 | + * @return void |
| 64 | + * |
| 65 | + * @access public |
| 66 | + */ |
9 | 67 | public function offsetSet($offset, $value)
|
10 | 68 | {
|
11 | 69 | if ($offset === null) {
|
12 |
| - $this->container[] = $value; |
| 70 | + $this->data[] = $value; |
13 | 71 | } else {
|
14 |
| - $index = array_search(strtolower($offset), array_keys(array_change_key_case($this->container, CASE_LOWER))); |
15 |
| - if (!($index === false)) { |
16 |
| - $keys = array_keys($this->container); |
17 |
| - unset($this->container[$keys[$index]]); |
18 |
| - } |
19 |
| - $this->container[$offset] = $value; |
| 72 | + $offsetlower = strtolower($offset); |
| 73 | + $this->data[$offsetlower] = $value; |
| 74 | + $this->keys[$offsetlower] = $offset; |
20 | 75 | }
|
21 | 76 | }
|
22 | 77 |
|
| 78 | + /** |
| 79 | + * Offset Exists |
| 80 | + * |
| 81 | + * Checks if the Offset exists in data storage. The index is looked up with |
| 82 | + * the lower-case version of the provided offset. |
| 83 | + * |
| 84 | + * @see https://secure.php.net/manual/en/arrayaccess.offsetexists.php |
| 85 | + * |
| 86 | + * @param string $offset Offset to check |
| 87 | + * |
| 88 | + * @return bool If the offset exists. |
| 89 | + * |
| 90 | + * @access public |
| 91 | + */ |
23 | 92 | public function offsetExists($offset)
|
24 | 93 | {
|
25 |
| - return array_key_exists(strtolower($offset), array_change_key_case($this->container, CASE_LOWER)); |
| 94 | + return (bool) array_key_exists(strtolower($offset), $this->data); |
26 | 95 | }
|
27 | 96 |
|
| 97 | + /** |
| 98 | + * Offset Unset |
| 99 | + * |
| 100 | + * Unsets the specified offset. Converts the provided offset to lowercase, |
| 101 | + * and unsets the Case-Sensitive Key, as well as the stored data. |
| 102 | + * |
| 103 | + * @see https://secure.php.net/manual/en/arrayaccess.offsetunset.php |
| 104 | + * |
| 105 | + * @param string $offset The offset to unset. |
| 106 | + * |
| 107 | + * @return void |
| 108 | + * |
| 109 | + * @access public |
| 110 | + */ |
28 | 111 | public function offsetUnset($offset)
|
29 | 112 | {
|
30 |
| - unset($this->container[$offset]); |
| 113 | + $offsetlower = strtolower($offset); |
| 114 | + unset($this->data[$offsetlower]); |
| 115 | + unset($this->keys[$offsetlower]); |
31 | 116 | }
|
32 | 117 |
|
| 118 | + /** |
| 119 | + * Offset Get |
| 120 | + * |
| 121 | + * Return the stored data at the provided offset. The offset is converted to |
| 122 | + * lowercase and the lookup is done on the Data store directly. |
| 123 | + * |
| 124 | + * @see https://secure.php.net/manual/en/arrayaccess.offsetget.php |
| 125 | + * |
| 126 | + * @param string $offset Offset to lookup. |
| 127 | + * |
| 128 | + * @return mixed The data stored at the offset. |
| 129 | + * |
| 130 | + * @access public |
| 131 | + */ |
33 | 132 | public function offsetGet($offset)
|
34 | 133 | {
|
35 |
| - $index = array_search(strtolower($offset), array_keys(array_change_key_case($this->container, CASE_LOWER))); |
36 |
| - if ($index === false) { |
37 |
| - return null; |
38 |
| - } |
39 |
| - |
40 |
| - $values = array_values($this->container); |
41 |
| - return $values[$index]; |
| 134 | + $offsetlower = strtolower($offset); |
| 135 | + return isset($this->data[$offsetlower]) ? $this->data[$offsetlower] : NULL; |
42 | 136 | }
|
43 | 137 |
|
| 138 | + /** |
| 139 | + * Count |
| 140 | + * |
| 141 | + * @see https://secure.php.net/manual/en/countable.count.php |
| 142 | + * |
| 143 | + * @param void |
| 144 | + * |
| 145 | + * @return int The number of elements stored in the Array. |
| 146 | + * |
| 147 | + * @access public |
| 148 | + */ |
44 | 149 | public function count()
|
45 | 150 | {
|
46 |
| - return count($this->container); |
| 151 | + return (int) count($this->data); |
47 | 152 | }
|
48 | 153 |
|
| 154 | + /** |
| 155 | + * Current |
| 156 | + * |
| 157 | + * @see https://secure.php.net/manual/en/iterator.current.php |
| 158 | + * |
| 159 | + * @param void |
| 160 | + * |
| 161 | + * @return mixed Data at the current position. |
| 162 | + * |
| 163 | + * @access public |
| 164 | + */ |
49 | 165 | public function current()
|
50 | 166 | {
|
51 |
| - return current($this->container); |
| 167 | + return current($this->data); |
52 | 168 | }
|
53 | 169 |
|
| 170 | + /** |
| 171 | + * Next |
| 172 | + * |
| 173 | + * @see https://secure.php.net/manual/en/iterator.next.php |
| 174 | + * |
| 175 | + * @param void |
| 176 | + * |
| 177 | + * @return void |
| 178 | + * |
| 179 | + * @access public |
| 180 | + */ |
54 | 181 | public function next()
|
55 | 182 | {
|
56 |
| - return next($this->container); |
| 183 | + next($this->data); |
57 | 184 | }
|
58 | 185 |
|
| 186 | + /** |
| 187 | + * Key |
| 188 | + * |
| 189 | + * @see https://secure.php.net/manual/en/iterator.key.php |
| 190 | + * |
| 191 | + * @param void |
| 192 | + * |
| 193 | + * @return mixed Case-Sensitive key at current position. |
| 194 | + * |
| 195 | + * @access public |
| 196 | + */ |
59 | 197 | public function key()
|
60 | 198 | {
|
61 |
| - return key($this->container); |
| 199 | + $key = key($this->data); |
| 200 | + return isset($this->keys[$key]) ? $this->keys[$key] : $key; |
62 | 201 | }
|
63 | 202 |
|
| 203 | + /** |
| 204 | + * Valid |
| 205 | + * |
| 206 | + * @see https://secure.php.net/manual/en/iterator.valid.php |
| 207 | + * |
| 208 | + * @param void |
| 209 | + * |
| 210 | + * @return bool If the current position is valid. |
| 211 | + * |
| 212 | + * @access public |
| 213 | + */ |
64 | 214 | public function valid()
|
65 | 215 | {
|
66 |
| - return !($this->current() === false); |
| 216 | + return (bool) !(key($this->data) === NULL); |
67 | 217 | }
|
68 | 218 |
|
| 219 | + /** |
| 220 | + * Rewind |
| 221 | + * |
| 222 | + * @see https://secure.php.net/manual/en/iterator.rewind.php |
| 223 | + * |
| 224 | + * @param void |
| 225 | + * |
| 226 | + * @return void |
| 227 | + * |
| 228 | + * @access public |
| 229 | + */ |
69 | 230 | public function rewind()
|
70 | 231 | {
|
71 |
| - reset($this->container); |
| 232 | + reset($this->data); |
72 | 233 | }
|
73 | 234 | }
|
0 commit comments