Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
9 | class SS_Map implements ArrayAccess, Countable, IteratorAggregate { |
||
10 | |||
11 | protected $list, $keyField, $valueField; |
||
12 | |||
13 | /** |
||
14 | * @see SS_Map::unshift() |
||
15 | * |
||
16 | * @var array $firstItems |
||
17 | */ |
||
18 | protected $firstItems = array(); |
||
19 | |||
20 | /** |
||
21 | * @see SS_Map::push() |
||
22 | * |
||
23 | * @var array $lastItems |
||
24 | */ |
||
25 | protected $lastItems = array(); |
||
26 | |||
27 | /** |
||
28 | * Construct a new map around an SS_list. |
||
29 | * |
||
30 | * @param $list The list to build a map from |
||
31 | * @param $keyField The field to use as the key of each map entry |
||
32 | * @param $valueField The field to use as the value of each map entry |
||
33 | */ |
||
34 | public function __construct(SS_List $list, $keyField = "ID", $valueField = "Title") { |
||
35 | $this->list = $list; |
||
36 | $this->keyField = $keyField; |
||
37 | $this->valueField = $valueField; |
||
38 | } |
||
39 | |||
40 | /** |
||
41 | * Set the key field for this map. |
||
42 | * |
||
43 | * @var string $keyField |
||
44 | */ |
||
45 | public function setKeyField($keyField) { |
||
46 | $this->keyField = $keyField; |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * Set the value field for this map. |
||
51 | * |
||
52 | * @var string $valueField |
||
53 | */ |
||
54 | public function setValueField($valueField) { |
||
55 | $this->valueField = $valueField; |
||
56 | } |
||
57 | |||
58 | /** |
||
59 | * Return an array equivalent to this map. |
||
60 | * |
||
61 | * @return array |
||
62 | */ |
||
63 | public function toArray() { |
||
64 | $array = array(); |
||
65 | |||
66 | foreach($this as $k => $v) { |
||
67 | $array[$k] = $v; |
||
68 | } |
||
69 | |||
70 | return $array; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Return all the keys of this map. |
||
75 | * |
||
76 | * @return array |
||
77 | */ |
||
78 | public function keys() { |
||
79 | return array_keys($this->toArray()); |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Return all the values of this map. |
||
84 | * |
||
85 | * @return array |
||
86 | */ |
||
87 | public function values() { |
||
88 | return array_values($this->toArray()); |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Unshift an item onto the start of the map. |
||
93 | * |
||
94 | * Stores the value in addition to the {@link DataQuery} for the map. |
||
95 | * |
||
96 | * @var string $key |
||
97 | * @var mixed $value |
||
98 | */ |
||
99 | public function unshift($key, $value) { |
||
100 | $oldItems = $this->firstItems; |
||
101 | $this->firstItems = array( |
||
102 | $key => $value |
||
103 | ); |
||
104 | |||
105 | if($oldItems) { |
||
106 | $this->firstItems = $this->firstItems + $oldItems; |
||
107 | } |
||
108 | |||
109 | return $this; |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Pushes an item onto the end of the map. |
||
114 | * |
||
115 | * @var string $key |
||
116 | * @var mixed $value |
||
117 | */ |
||
118 | public function push($key, $value) { |
||
119 | $oldItems = $this->lastItems; |
||
120 | |||
121 | $this->lastItems = array( |
||
122 | $key => $value |
||
123 | ); |
||
124 | |||
125 | if($oldItems) { |
||
126 | $this->lastItems = $this->lastItems + $oldItems; |
||
127 | } |
||
128 | |||
129 | return $this; |
||
130 | } |
||
131 | |||
132 | // ArrayAccess |
||
133 | |||
134 | /** |
||
135 | * @var string $key |
||
136 | * |
||
137 | * @return boolean |
||
138 | */ |
||
139 | public function offsetExists($key) { |
||
140 | if(isset($this->firstItems[$key])) { |
||
141 | return true; |
||
142 | } |
||
143 | |||
144 | if(isset($this->lastItems[$key])) { |
||
145 | return true; |
||
146 | } |
||
147 | |||
148 | $record = $this->list->find($this->keyField, $key); |
||
149 | |||
150 | return $record != null; |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * @var string $key |
||
155 | * |
||
156 | * @return mixed |
||
157 | */ |
||
158 | public function offsetGet($key) { |
||
159 | if(isset($this->firstItems[$key])) { |
||
160 | return $this->firstItems[$key]; |
||
161 | } |
||
162 | |||
163 | if(isset($this->lastItems[$key])) { |
||
164 | return $this->lastItems[$key]; |
||
165 | } |
||
166 | |||
167 | $record = $this->list->find($this->keyField, $key); |
||
168 | |||
169 | if($record) { |
||
170 | $col = $this->valueField; |
||
171 | |||
172 | return $record->$col; |
||
173 | } |
||
174 | |||
175 | return null; |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Sets a value in the map by a given key that has been set via |
||
180 | * {@link SS_Map::push()} or {@link SS_Map::unshift()} |
||
181 | * |
||
182 | * Keys in the map cannot be set since these values are derived from a |
||
183 | * {@link DataQuery} instance. In this case, use {@link SS_Map::toArray()} |
||
184 | * and manipulate the resulting array. |
||
185 | * |
||
186 | * @var string $key |
||
187 | * @var mixed $value |
||
188 | */ |
||
189 | public function offsetSet($key, $value) { |
||
190 | if(isset($this->firstItems[$key])) { |
||
191 | return $this->firstItems[$key] = $value; |
||
192 | } |
||
193 | |||
194 | if(isset($this->lastItems[$key])) { |
||
195 | return $this->lastItems[$key] = $value; |
||
196 | } |
||
197 | |||
198 | user_error( |
||
199 | "SS_Map is read-only. Please use $map->push($key, $value) to append values", |
||
200 | E_USER_ERROR |
||
201 | ); |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * Removes a value in the map by a given key which has been added to the map |
||
206 | * via {@link SS_Map::push()} or {@link SS_Map::unshift()} |
||
207 | * |
||
208 | * Keys in the map cannot be unset since these values are derived from a |
||
209 | * {@link DataQuery} instance. In this case, use {@link SS_Map::toArray()} |
||
210 | * and manipulate the resulting array. |
||
211 | * |
||
212 | * @var string $key |
||
213 | * @var mixed $value |
||
214 | */ |
||
215 | public function offsetUnset($key) { |
||
216 | if(isset($this->firstItems[$key])) { |
||
217 | unset($this->firstItems[$key]); |
||
218 | |||
219 | return; |
||
220 | } |
||
221 | |||
222 | if(isset($this->lastItems[$key])) { |
||
223 | unset($this->lastItems[$key]); |
||
224 | |||
225 | return; |
||
226 | } |
||
227 | |||
228 | user_error( |
||
229 | "SS_Map is read-only. Unset cannot be called on keys derived from the DataQuery", |
||
230 | E_USER_ERROR |
||
231 | ); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * Returns an SS_Map_Iterator instance for iterating over the complete set |
||
236 | * of items in the map. |
||
237 | * |
||
238 | * Satisfies the IteratorAggreagte interface. |
||
239 | * |
||
240 | * @return SS_Map_Iterator |
||
241 | */ |
||
242 | public function getIterator() { |
||
243 | return new SS_Map_Iterator( |
||
244 | $this->list->getIterator(), |
||
245 | $this->keyField, |
||
246 | $this->valueField, |
||
247 | $this->firstItems, |
||
248 | $this->lastItems |
||
249 | ); |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Returns the count of items in the list including the additional items set |
||
254 | * through {@link SS_Map::push()} and {@link SS_Map::unshift}. |
||
255 | * |
||
256 | * @return int |
||
257 | */ |
||
258 | public function count() { |
||
259 | return $this->list->count() |
||
260 | count($this->firstItems) |
||
|
|||
261 | count($this->lastItems); |
||
262 | } |
||
263 | } |
||
264 | |||
428 |