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 |
||
19 | abstract class AbstractResultsIterator implements \Countable, \Iterator |
||
20 | { |
||
21 | /** |
||
22 | * @var array Documents. |
||
23 | */ |
||
24 | protected $documents = []; |
||
25 | |||
26 | /** |
||
27 | * @var int |
||
28 | */ |
||
29 | private $count = 0; |
||
30 | |||
31 | /** |
||
32 | * @var array |
||
33 | */ |
||
34 | private $aggregations = []; |
||
35 | |||
36 | /** |
||
37 | * @var Converter |
||
38 | */ |
||
39 | private $converter; |
||
40 | |||
41 | /** |
||
42 | * @var Manager |
||
43 | */ |
||
44 | private $manager; |
||
45 | |||
46 | /** |
||
47 | * Elasticsearch manager configuration. |
||
48 | * |
||
49 | * @var array |
||
50 | */ |
||
51 | private $managerConfig = []; |
||
52 | |||
53 | /** |
||
54 | * @var string If value is not null then results are scrollable. |
||
55 | */ |
||
56 | private $scrollId; |
||
57 | |||
58 | /** |
||
59 | * @var string Scroll duration. |
||
60 | */ |
||
61 | private $scrollDuration; |
||
62 | |||
63 | /** |
||
64 | * Used to count iteration. |
||
65 | * |
||
66 | * @var int |
||
67 | */ |
||
68 | private $key = 0; |
||
69 | |||
70 | /** |
||
71 | * @param array $rawData |
||
72 | * @param Manager $manager |
||
73 | * @param array $scroll |
||
74 | */ |
||
75 | public function __construct( |
||
76 | array $rawData, |
||
77 | Manager $manager, |
||
78 | array $scroll = [] |
||
79 | ) { |
||
80 | $this->manager = $manager; |
||
81 | $this->converter = $manager->getConverter(); |
||
82 | $this->managerConfig = $manager->getConfig(); |
||
83 | |||
84 | if (isset($scroll['_scroll_id']) && isset($scroll['duration'])) { |
||
85 | $this->scrollId = $scroll['_scroll_id']; |
||
86 | $this->scrollDuration = $scroll['duration']; |
||
87 | } |
||
88 | |||
89 | if (isset($rawData['aggregations'])) { |
||
90 | $this->aggregations = &$rawData['aggregations']; |
||
91 | } |
||
92 | |||
93 | if (isset($rawData['hits']['hits'])) { |
||
94 | $this->documents = $rawData['hits']['hits']; |
||
95 | } |
||
96 | if (isset($rawData['hits']['total'])) { |
||
97 | $this->count = $rawData['hits']['total']; |
||
98 | } |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Destructor. |
||
103 | */ |
||
104 | public function __destruct() |
||
105 | { |
||
106 | // Clear scroll if initialized |
||
107 | if ($this->isScrollable()) { |
||
108 | $this->manager->clearScroll($this->scrollId); |
||
109 | } |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * @return array |
||
114 | */ |
||
115 | protected function getAggregations() |
||
116 | { |
||
117 | return $this->aggregations; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @return Manager |
||
122 | */ |
||
123 | protected function getManager() |
||
127 | |||
128 | /** |
||
129 | * Returns total count of documents. |
||
130 | * |
||
131 | * @return int |
||
132 | */ |
||
133 | public function count() |
||
134 | { |
||
135 | return $this->count; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Return the current element. |
||
140 | * |
||
141 | * @return mixed |
||
142 | */ |
||
143 | public function current() |
||
144 | { |
||
145 | return $this->getDocument($this->key()); |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Move forward to next element. |
||
150 | */ |
||
151 | public function next() |
||
152 | { |
||
153 | $this->advanceKey(); |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Return the key of the current element. |
||
158 | * |
||
159 | * @return mixed |
||
160 | */ |
||
161 | public function key() |
||
162 | { |
||
163 | return $this->key; |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * Checks if current position is valid. |
||
168 | * |
||
169 | * @return bool |
||
170 | */ |
||
171 | public function valid() |
||
172 | { |
||
173 | if (!isset($this->documents)) { |
||
174 | return false; |
||
175 | } |
||
176 | |||
177 | $valid = $this->documentExists($this->key()); |
||
178 | if ($valid) { |
||
179 | return true; |
||
180 | } |
||
181 | |||
182 | $this->page(); |
||
183 | |||
184 | return $this->documentExists($this->key()); |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Rewind the Iterator to the first element. |
||
189 | */ |
||
190 | public function rewind() |
||
191 | { |
||
192 | $this->key = 0; |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * @return bool |
||
197 | */ |
||
198 | public function isScrollable() |
||
199 | { |
||
200 | return !empty($this->scrollId); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @return array |
||
205 | */ |
||
206 | protected function getManagerConfig() |
||
207 | { |
||
208 | return $this->managerConfig; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @return Converter |
||
213 | */ |
||
214 | protected function getConverter() |
||
215 | { |
||
216 | return $this->converter; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Gets document array from the container. |
||
221 | * |
||
222 | * @param mixed $key |
||
223 | * |
||
224 | * @return mixed |
||
225 | */ |
||
226 | protected function getDocument($key) |
||
227 | { |
||
228 | if (!$this->documentExists($key)) { |
||
229 | return null; |
||
230 | } |
||
231 | |||
232 | return $this->convertDocument($this->documents[$key]); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Checks whether document exists in the container. |
||
237 | * |
||
238 | * @param mixed $key |
||
239 | * |
||
240 | * @return bool |
||
241 | */ |
||
242 | protected function documentExists($key) |
||
243 | { |
||
244 | return array_key_exists($key, $this->documents); |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * Advances key. |
||
249 | * |
||
250 | * @return $this |
||
251 | */ |
||
252 | protected function advanceKey() |
||
253 | { |
||
254 | if ($this->isScrollable() && ($this->documents[$this->key()] == end($this->documents))) { |
||
255 | $this->page(); |
||
256 | } else { |
||
257 | $this->key++; |
||
258 | } |
||
259 | |||
260 | return $this; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Rewind's the iteration and returns first result. |
||
265 | * |
||
266 | * @return mixed|null |
||
267 | */ |
||
268 | public function first() |
||
269 | { |
||
270 | $this->rewind(); |
||
271 | |||
272 | return $this->getDocument($this->key()); |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Advances scan page. |
||
277 | * |
||
278 | * @return $this |
||
279 | */ |
||
280 | protected function page() |
||
281 | { |
||
282 | if ($this->key() == $this->count() || !$this->isScrollable()) { |
||
283 | return $this; |
||
284 | } |
||
285 | |||
286 | $raw = $this->manager->scroll($this->scrollId, $this->scrollDuration, Result::RESULTS_RAW); |
||
287 | $this->rewind(); |
||
288 | $this->scrollId = $raw['_scroll_id']; |
||
289 | $this->documents = $raw['hits']['hits']; |
||
290 | |||
291 | return $this; |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Returns score of current hit. |
||
296 | * |
||
297 | * @return int |
||
298 | */ |
||
299 | View Code Duplication | public function getDocumentScore() |
|
311 | |||
312 | /** |
||
313 | * Returns sort of current hit. |
||
314 | * |
||
315 | * @return mixed |
||
316 | */ |
||
317 | View Code Duplication | public function getDocumentSort() |
|
329 | |||
330 | /** |
||
331 | * Converts raw array to document object or array, depends on iterator type. |
||
332 | * |
||
333 | * @param array $document |
||
334 | * |
||
335 | * @return object|array |
||
336 | */ |
||
337 | abstract protected function convertDocument(array $document); |
||
338 | } |
||
339 |
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.