Completed
Push — master ( b02a4e...93d881 )
by Siro Díaz
01:20
created

DoublyLinkedList   C

Complexity

Total Complexity 63

Size/Duplication

Total Lines 443
Duplicated Lines 30.25 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 63
lcom 1
cbo 3
dl 134
loc 443
rs 5.8893
c 1
b 0
f 1

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 7 7 1
A clear() 0 5 2
A get() 8 8 2
C search() 0 30 8
A getLast() 0 6 2
A searchLast() 0 6 2
A contains() 0 17 4
A indexOf() 19 19 4
A lastIndexOf() 19 19 4
B remove() 10 29 5
A getAll() 17 17 4
A insertBeginning() 0 14 2
A insertEnd() 0 8 1
A insertAt() 0 16 2
C delete() 27 27 8
A deleteBeginning() 8 8 1
A deleteAt() 0 19 2
A deleteEnd() 19 19 2
A toArray() 0 8 2
A rewind() 0 4 1
A current() 0 3 1
A key() 0 3 1
A next() 0 4 1
A valid() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DoublyLinkedList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DoublyLinkedList, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * DataStructures for PHP
4
 *
5
 * @link      https://github.com/SiroDiaz/DataStructures
6
 * @copyright Copyright (c) 2017 Siro Díaz Palazón
7
 * @license   https://github.com/SiroDiaz/DataStructures/blob/master/README.md (MIT License)
8
 */
9
namespace DataStructures\Lists;
10
11
use DataStructures\Lists\Traits\{CountTrait, ArrayAccessTrait};
12
use DataStructures\Lists\Nodes\DoublyLinkedListNode;
13
use DataStructures\Lists\ListAbstract;
14
use OutOfBoundsException;
15
16
/**
17
 * DoublyLinkedList
18
 *
19
 * DoublyLinkedList is a double linked list that has
20
 * a pointer to the next and the previous node.
21
 *
22
 * @author Siro Diaz Palazon <[email protected]>
23
 */
24
class DoublyLinkedList extends ListAbstract {
25
    use ArrayAccessTrait;
26
27
    private $head;
28
    private $tail;
29
    private $position;
30
    private $current;
31
32 View Code Duplication
    public function __construct() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
33
        $this->head = null;
34
        $this->tail = &$this->head;
35
        $this->size = 0;
36
        $this->position = 0;
37
        $this->current = &$this->head;
38
    }
39
40
    /**
41
     * Removes all nodes of the list. It removes from the beginning.
42
     */
43
    public function clear() {
44
        while($this->head !== null) {
45
            $this->shift();
46
        }
47
    }
48
    
49
    /**
50
     * Gets the data stored in the position especified.
51
     *
52
     * @param integer $index Index that must be greater than 0
53
     *  and lower than the list size.
54
     * @return mixed The data stored in the given index
55
     * @throws OutOfBoundsException if index is out bounds.
56
     */
57 View Code Duplication
    public function get($index) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
58
        $node = $this->search($index);
59
        if($node === null) {
60
            return null;
61
        }
62
63
        return $node->data;
64
    }
65
66
    /**
67
     * Gets the node stored in the position especified.
68
     * If index is 0 or (size - 1) the method is O(1) else O(n).
69
     *
70
     * @param integer $index the position.
71
     * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1)
72
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null the node stored in $index.
73
     */
74
    protected function search($index) {
75
        if($index < 0 || $index > $this->size - 1) {
76
            throw new OutOfBoundsException();
77
        }
78
79
        if($index === 0) {
80
            return $this->head;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->head; of type null|DataStructures\List...es\DoublyLinkedListNode adds the type DataStructures\Lists\Nodes\DoublyLinkedListNode to the return on line 80 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
81
        }
82
83
        if($index === $this->size - 1) {
84
            return $this->tail;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->tail; of type null|DataStructures\List...es\DoublyLinkedListNode adds the type DataStructures\Lists\Nodes\DoublyLinkedListNode to the return on line 84 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
85
        }
86
87
        $current = &$this->head;
88
        if($index < (int) ceil($this->size / 2)) {
89
            $i = 0;
90
            while($i < $index) {
91
                $current = &$current->next;
92
                $i++;
93
            }    
94
        } else {
95
            $i = $this->size;
96
            while($i > $index) {
97
                $current = &$current->prev;
98
                $i--;
99
            }
100
        }
101
102
        return $current;
103
    }
104
105
    /**
106
     * Returns the data in the last node with O(1).
107
     *
108
     * @return mixed null if the list is empty.
109
     */
110
    public function getLast() {
111
        if($this->head === null) {
112
            return null;
113
        }
114
        return $this->tail->data;
115
    }
116
117
    /**
118
     * Returns the last node with O(1).
119
     *
120
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null if the list is empty.
121
     */
122
    public function searchLast() {
123
        if($this->head === null) {
124
            return null;
125
        }
126
        return $this->tail;
127
    }
128
129
    /**
130
     * {@inheritDoc}
131
     */
132
    public function contains($data) : bool {
133
        if($this->empty()) {
134
            return false;
135
        }
136
137
        $current = $this->head->next;
138
        $prev = $this->head;
139
        while($current !== $this->head) {
140
            if($prev->data === $data) {
141
                return true;
142
            }
143
            $prev = $current;
144
            $current = $current->next;
145
        }
146
147
        return false;
148
    }
149
150
    /**
151
     * {@inheritDoc}
152
     */
153 View Code Duplication
    public function indexOf($data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
154
        if($this->head === null) {
155
            return false;
156
        }
157
        
158
        $current = $this->head;
159
        $i = 0;
160
        
161
        while($i < $this->size) {
162
            if($current->data === $data) {
163
                return $i;
164
            }
165
166
            $current = $current->next;
167
            $i++;
168
        }
169
170
        return false;
171
    }
172
173
    /**
174
     * {@inheritDoc}
175
     */
176 View Code Duplication
    public function lastIndexOf($data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
177
        if($this->head === null) {
178
            return false;
179
        }
180
        
181
        $current = $this->head;
182
        $i = 0;
183
        $pos = false;
184
        while($i < $this->size) {
185
            if($current->data == $data) {
186
                $pos = $i;
187
            }
188
189
            $current = $current->next;
190
            $i++;
191
        }
192
193
        return $pos;
194
    }
195
196
    /**
197
     * {@inheritDoc}
198
     */
199
    public function remove($data) {
200
        $current = &$this->head;
201
        $i = 0;
202
        
203
        if($this->head === null) {
204
            return null;
205
        }
206
207
        if($this->head->data === $data) {
208
            $this->head = &$this->head->next;
209
            $this->head->prev = &$this->tail;
210
            $this->size--;
211
            
212
            return $data;
213
        }
214
215 View Code Duplication
        while($i < $this->size) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
216
            if($current->data === $data) {
217
                $current->prev = &$current->next;
218
                $current = null;
219
                $this->size--;
220
                return $data;
221
            }
222
223
            $current = $current->next;
224
        }
225
226
        return null;
227
    }
228
    
229
    /**
230
     * Generator for retrieve all nodes stored.
231
     * 
232
     * @return null if the head is null (or list is empty)
233
     */
234 View Code Duplication
    public function getAll() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
235
        if($this->head === null) {
236
            return;
237
        }
238
239
        if($this->head === $this->tail) {
240
            yield $this->head->data;
241
        } else {
242
            $current = $this->head;
243
            $i = 0;
244
            while($i < $this->size) {
245
                yield $current->data;
246
                $current = $current->next;
247
                $i++;
248
            }
249
        }
250
    }
251
252
    /**
253
     * Inserts at the beginning of the list.
254
     *
255
     * @param mixed $data
256
     */
257
    protected function insertBeginning($data) {
258
        $newNode = new DoublyLinkedListNode($data);
259
        if($this->head === null) {
260
            $newNode->next = &$this->head;
261
            $newNode->prev = &$this->head;
262
            $this->head = &$newNode;
263
            $this->tail = &$newNode;
264
        } else {
265
            $this->tail->next = &$newNode;
266
            $newNode->next = &$this->head;
267
            $newNode->prev = &$this->tail;
268
            $this->head = &$newNode;
269
        }
270
    }
271
272
    /**
273
     * Add a new node in the specified index.
274
     *
275
     * @param mixed $data the data to be stored.
276
     */
277
    protected function insertEnd($data) {
278
        $newNode = new DoublyLinkedListNode($data);
279
        $this->tail->next = &$newNode;
280
        $newNode->next = &$this->head;
281
        $newNode->prev = &$this->tail;
282
        $this->tail = &$newNode;
283
        $this->head->prev = &$newNode;
284
    }
285
286
    /**
287
     * Add a new node in the specified index.
288
     *
289
     * @param integer $index the position.
290
     * @param mixed $data the data to be stored.
291
     */
292
    protected function insertAt($index, $data) {
293
        $newNode = new DoublyLinkedListNode($data);
294
        $current = $this->head;
295
        $prev = null;
296
        $i = 0;
297
        while($i < $index) {
298
            $prev = $current;
299
            $current = $current->next;
300
            $i++;
301
        }
302
        
303
        $prev->next = &$newNode;
304
        $newNode->prev = &$prev;
305
        $newNode->next = &$current;
306
        $current->prev = &$newNode;
307
    }
308
    
309
    /**
310
     * Delete a node in the given position and returns it back.
311
     *
312
     * @param integer $index the position.
313
     * @throws OutOfBoundsException if index is negative
314
     *  or is greater than the size of the list.
315
     */
316 View Code Duplication
    public function delete($index) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
317
        if($index < 0 || ($index > 0 && $index > $this->size - 1)) {
318
            throw new OutOfBoundsException();
319
        }
320
321
        // if the list is empty
322
        if($this->head === null) {
323
            return null;
324
        }
325
        
326
        // if only there is an element
327
        if($this->head->next === $this->head) {
328
            $temp = $this->head;
329
            $this->head = null;
330
            $this->size--;
331
332
            return $temp->data;
333
        }
334
335
        if($index === 0) {
336
            return $this->deleteBeginning();
337
        } else if($index === $this->size - 1) {
338
            return $this->deleteEnd();
339
        } else {
340
            return $this->deleteAt($index);
341
        }
342
    }
343
344
    /**
345
     * Deletes at the beginnig of the list and returns the data stored.
346
     *
347
     * @return mixed the data stored in the node.
348
     */
349 View Code Duplication
    private function deleteBeginning() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
350
        $temp = $this->head;
351
        $this->head = &$this->head->next;
352
        $this->tail->next = &$this->head;
353
        $this->size--;
354
355
        return $temp->data;
356
    }
357
358
    /**
359
     * Deletes at the specified position and returns the data stored.
360
     *
361
     * @param integer $index the position.
362
     * @return mixed the data stored in the node.
363
     */
364
    private function deleteAt($index) {
365
        $i = 0;
366
        $prev = $this->head;
367
        $current = $this->head;
368
        
369
        while($i < $index) {
370
            $prev = $current;
371
            $current = $current->next;
372
            $i++;
373
        }
374
375
        $temp = $current;
376
        $prev->next = &$current->next;
377
        $current->next->prev = &$pre;
0 ignored issues
show
Bug introduced by
The variable $pre does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
378
        $current = null;
0 ignored issues
show
Unused Code introduced by
$current is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
379
        $this->size--;
380
381
        return $temp->data;
382
    }
383
384
    /**
385
     * Deletes at the end of the list and returns the data stored.
386
     *
387
     * @return mixed the data stored in the node.
388
     */
389 View Code Duplication
    private function deleteEnd() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
390
        $prev = $this->head;
391
        $current = $this->head;
392
        
393
        while($current !== $this->tail) {
394
            $prev = $current;
395
            $current = $current->next;
396
        }
397
        
398
        $temp = $current;
399
        $prev->next = &$this->head;
400
        $this->head->prev = &$prev;
401
        $this->tail = &$prev;
402
        $current = null;
0 ignored issues
show
Unused Code introduced by
$current is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
403
404
        $this->size--;
405
406
        return $temp->data;
407
    }
408
    
409
    /**
410
     * Converts/exports the list content into array type.
411
     *
412
     * @return array data stored in all nodes.
413
     */
414
    public function toArray() : array {
415
        $arr = [];
416
        foreach($this->getAll() as $node) {
417
            $arr[] = $node;
418
        }
419
420
        return $arr;
421
    }
422
423
    /**
424
     * Reset the cursor position.
425
     */
426
    public function rewind() {
427
        $this->position = 0;
428
        $this->current = &$this->head;
429
    }
430
431
    /**
432
     * Returns the current node data.
433
     *
434
     * @return mixed
435
     */
436
    public function current() {
437
        return $this->current->data;
438
    }
439
440
    /**
441
     * Key or index that indicates the cursor position.
442
     *
443
     * @return integer The current position.
444
     */
445
    public function key() {
446
        return $this->position;
447
    }
448
449
    /**
450
     * Move the cursor to the next node and increments the
451
     * position counter.
452
     */
453
    public function next() {
454
        ++$this->position;
455
        $this->current = $this->current->next;
456
    }
457
458
    /**
459
     * Returns if the current pointer position is valid.
460
     *
461
     * @return boolean true if pointer is not last, else false.
462
     */
463
    public function valid() {
464
        return $this->position < $this->size;
465
    }
466
}