Completed
Push — master ( c6925e...1b130f )
by Siro Díaz
01:40
created

DoublyLinkedList   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 363
Duplicated Lines 28.1 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 50
lcom 1
cbo 3
dl 102
loc 363
rs 8.6206
c 1
b 0
f 1

20 Methods

Rating   Name   Duplication   Size   Complexity  
A insertEnd() 0 8 1
A insertAt() 0 16 2
A __construct() 7 7 1
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 deleteBeginning() 13 13 2
A deleteAt() 0 18 2
A deleteEnd() 17 17 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
    protected $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
     * Gets the node stored in the position especified.
42
     * If index is 0 or (size - 1) the method is O(1) else O(n).
43
     *
44
     * @param integer $index the position.
45
     * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1)
46
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null the node stored in $index.
47
     */
48
    protected function search($index) {
49
        if($index < 0 || $index > $this->size - 1) {
50
            throw new OutOfBoundsException();
51
        }
52
53
        if($index === 0) {
54
            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 54 which is incompatible with the return type declared by the abstract method DataStructures\Lists\ListAbstract::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
55
        }
56
57
        if($index === $this->size - 1) {
58
            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 58 which is incompatible with the return type declared by the abstract method DataStructures\Lists\ListAbstract::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
59
        }
60
61
        $current = &$this->head;
62
        if($index < (int) ceil($this->size / 2)) {
63
            $i = 0;
64
            while($i < $index) {
65
                $current = &$current->next;
66
                $i++;
67
            }    
68
        } else {
69
            $i = $this->size;
70
            while($i > $index) {
71
                $current = &$current->prev;
72
                $i--;
73
            }
74
        }
75
76
        return $current;
77
    }
78
79
    /**
80
     * Returns the data in the last node with O(1).
81
     *
82
     * @return mixed null if the list is empty.
83
     */
84
    public function getLast() {
85
        if($this->head === null) {
86
            return null;
87
        }
88
        return $this->tail->data;
89
    }
90
91
    /**
92
     * Returns the last node with O(1).
93
     *
94
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null if the list is empty.
95
     */
96
    public function searchLast() {
97
        if($this->head === null) {
98
            return null;
99
        }
100
        return $this->tail;
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106
    public function contains($data) : bool {
107
        if($this->empty()) {
108
            return false;
109
        }
110
111
        $current = $this->head->next;
112
        $prev = $this->head;
113
        while($current !== $this->head) {
114
            if($prev->data === $data) {
115
                return true;
116
            }
117
            $prev = $current;
118
            $current = $current->next;
119
        }
120
121
        return $prev->data === $data;
122
    }
123
124
    /**
125
     * {@inheritDoc}
126
     */
127 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...
128
        if($this->head === null) {
129
            return false;
130
        }
131
        
132
        $current = $this->head;
133
        $i = 0;
134
        
135
        while($i < $this->size) {
136
            if($current->data === $data) {
137
                return $i;
138
            }
139
140
            $current = $current->next;
141
            $i++;
142
        }
143
144
        return false;
145
    }
146
147
    /**
148
     * {@inheritDoc}
149
     */
150 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...
151
        if($this->head === null) {
152
            return false;
153
        }
154
        
155
        $current = $this->head;
156
        $i = 0;
157
        $pos = false;
158
        while($i < $this->size) {
159
            if($current->data == $data) {
160
                $pos = $i;
161
            }
162
163
            $current = $current->next;
164
            $i++;
165
        }
166
167
        return $pos;
168
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173
    public function remove($data) {
174
        $current = &$this->head;
175
        $i = 0;
176
        
177
        if($this->head === null) {
178
            return null;
179
        }
180
181
        if($this->head->data === $data) {
182
            $this->head = &$this->head->next;
183
            $this->head->prev = &$this->tail;
184
            $this->size--;
185
            
186
            return $data;
187
        }
188
189 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...
190
            if($current->data === $data) {
191
                $current->prev = &$current->next;
192
                $current = null;
193
                $this->size--;
194
                return $data;
195
            }
196
197
            $current = $current->next;
198
        }
199
200
        return null;
201
    }
202
    
203
    /**
204
     * Generator for retrieve all nodes stored.
205
     * 
206
     * @return null if the head is null (or list is empty)
207
     */
208 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...
209
        if($this->head === null) {
210
            return;
211
        }
212
213
        if($this->head === $this->tail) {
214
            yield $this->head->data;
215
        } else {
216
            $current = $this->head;
217
            $i = 0;
218
            while($i < $this->size) {
219
                yield $current->data;
220
                $current = $current->next;
221
                $i++;
222
            }
223
        }
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229
    protected function insertBeginning($data) {
230
        $newNode = new DoublyLinkedListNode($data);
231
        if($this->head === null) {
232
            $newNode->next = &$this->head;
233
            $newNode->prev = &$this->head;
234
            $this->head = &$newNode;
235
            $this->tail = &$newNode;
236
        } else {
237
            $this->tail->next = &$newNode;
238
            $newNode->next = &$this->head;
239
            $newNode->prev = &$this->tail;
240
            $this->head = &$newNode;
241
        }
242
    }
243
244
    protected function insertEnd($data) {
245
        $newNode = new DoublyLinkedListNode($data);
246
        $this->tail->next = &$newNode;
247
        $newNode->next = &$this->head;
248
        $newNode->prev = &$this->tail;
249
        $this->tail = &$newNode;
250
        $this->head->prev = &$newNode;
251
    }
252
253
    /**
254
     * Add a new node in the specified index.
255
     *
256
     * @param integer $index the position.
257
     * @param mixed $data the data to be stored.
258
     */
259
    protected function insertAt($index, $data) {
260
        $newNode = new DoublyLinkedListNode($data);
261
        $current = $this->head;
262
        $prev = null;
263
        $i = 0;
264
        while($i < $index) {
265
            $prev = $current;
266
            $current = $current->next;
267
            $i++;
268
        }
269
        
270
        $prev->next = &$newNode;
271
        $newNode->prev = &$prev;
272
        $newNode->next = &$current;
273
        $current->prev = &$newNode;
274
    }
275
276
    /**
277
     * Deletes at the beginnig of the list and returns the data stored.
278
     *
279
     * @return mixed the data stored in the node.
280
     */
281 View Code Duplication
    protected 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...
282
        // if only there is an element
283
        if($this->head->next === $this->head) {
284
            $temp = $this->head;
285
            $this->head = null;
286
        } else {
287
            $temp = $this->head;
288
            $this->head = &$this->head->next;
289
            $this->tail->next = &$this->head;
290
            
291
        }
292
        return $temp->data;
293
    }
294
295
    /**
296
     * Deletes at the specified position and returns the data stored.
297
     *
298
     * @param integer $index the position.
299
     * @return mixed the data stored in the node.
300
     */
301
    protected function deleteAt($index) {
302
        $i = 0;
303
        $prev = $this->head;
304
        $current = $this->head;
305
        
306
        while($i < $index) {
307
            $prev = $current;
308
            $current = $current->next;
309
            $i++;
310
        }
311
312
        $temp = $current;
313
        $prev->next = &$current->next;
314
        $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...
315
        $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...
316
317
        return $temp->data;
318
    }
319
320
    /**
321
     * Deletes at the end of the list and returns the data stored.
322
     *
323
     * @return mixed the data stored in the node.
324
     */
325 View Code Duplication
    protected 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...
326
        $prev = $this->head;
327
        $current = $this->head;
328
        
329
        while($current !== $this->tail) {
330
            $prev = $current;
331
            $current = $current->next;
332
        }
333
        
334
        $temp = $current;
335
        $prev->next = &$this->head;
336
        $this->head->prev = &$prev;
337
        $this->tail = &$prev;
338
        $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...
339
340
        return $temp->data;
341
    }
342
343
    /**
344
     * Reset the cursor position.
345
     */
346
    public function rewind() {
347
        $this->position = 0;
348
        $this->current = &$this->head;
349
    }
350
351
    /**
352
     * Returns the current node data.
353
     *
354
     * @return mixed
355
     */
356
    public function current() {
357
        return $this->current->data;
358
    }
359
360
    /**
361
     * Key or index that indicates the cursor position.
362
     *
363
     * @return integer The current position.
364
     */
365
    public function key() {
366
        return $this->position;
367
    }
368
369
    /**
370
     * Move the cursor to the next node and increments the
371
     * position counter.
372
     */
373
    public function next() {
374
        ++$this->position;
375
        $this->current = $this->current->next;
376
    }
377
378
    /**
379
     * Returns if the current pointer position is valid.
380
     *
381
     * @return boolean true if pointer is not last, else false.
382
     */
383
    public function valid() {
384
        return $this->position < $this->size;
385
    }
386
}