Passed
Pull Request — 4 (#8209)
by Ingo
09:07
created

Map_Iterator   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 183
rs 9.52
c 0
b 0
f 0
wmc 36

7 Methods

Rating   Name   Duplication   Size   Complexity  
A current() 0 10 4
B next() 0 32 9
A extractValue() 0 10 5
A key() 0 9 4
A __construct() 0 18 5
A rewind() 0 17 5
A valid() 0 6 4
1
<?php
2
3
namespace SilverStripe\ORM;
4
5
use Iterator;
6
7
/**
8
 * Builds a map iterator around an Iterator.  Called by Map
9
 */
10
class Map_Iterator implements Iterator
11
{
12
13
    /**
14
     * @var Iterator
15
     **/
16
    protected $items;
17
18
    protected $keyField, $titleField;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
19
20
    protected $firstItemIdx = 0;
21
22
    protected $endItemIdx;
23
24
    protected $firstItems = array();
25
    protected $lastItems = array();
26
27
    protected $excludedItems = array();
28
29
    /**
30
     * @param Iterator $items The iterator to build this map from
31
     * @param string $keyField The field to use for the keys
32
     * @param string $titleField The field to use for the values
33
     * @param array $firstItems An optional map of items to show first
34
     * @param array $lastItems An optional map of items to show last
35
     */
36
    public function __construct(Iterator $items, $keyField, $titleField, $firstItems = null, $lastItems = null)
37
    {
38
        $this->items = $items;
39
        $this->keyField = $keyField;
40
        $this->titleField = $titleField;
41
        $this->endItemIdx = null;
42
43
        if ($firstItems) {
44
            foreach ($firstItems as $k => $v) {
45
                $this->firstItems[] = array($k, $v);
46
                $this->excludedItems[] = $k;
47
            }
48
        }
49
50
        if ($lastItems) {
51
            foreach ($lastItems as $k => $v) {
52
                $this->lastItems[] = array($k, $v);
53
                $this->excludedItems[] = $k;
54
            }
55
        }
56
    }
57
58
    /**
59
     * Rewind the Iterator to the first element.
60
     *
61
     * @return mixed
62
     */
63
    public function rewind()
64
    {
65
        $this->firstItemIdx = 0;
66
        $this->endItemIdx = null;
67
68
        $rewoundItem = $this->items->rewind();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rewoundItem is correct as $this->items->rewind() targeting Iterator::rewind() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
69
70
        if (isset($this->firstItems[$this->firstItemIdx])) {
71
            return $this->firstItems[$this->firstItemIdx][1];
72
        } else {
73
            if ($rewoundItem) {
0 ignored issues
show
introduced by
$rewoundItem is of type void, thus it always evaluated to false.
Loading history...
74
                return $this->extractValue($rewoundItem, $this->titleField);
75
            } else {
76
                if (!$this->items->valid() && $this->lastItems) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->lastItems of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
77
                    $this->endItemIdx = 0;
78
79
                    return $this->lastItems[0][1];
80
                }
81
            }
82
        }
83
    }
84
85
    /**
86
     * Return the current element.
87
     *
88
     * @return mixed
89
     */
90
    public function current()
91
    {
92
        if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
93
            return $this->lastItems[$this->endItemIdx][1];
94
        } else {
95
            if (isset($this->firstItems[$this->firstItemIdx])) {
96
                return $this->firstItems[$this->firstItemIdx][1];
97
            }
98
        }
99
        return $this->extractValue($this->items->current(), $this->titleField);
100
    }
101
102
    /**
103
     * Extracts a value from an item in the list, where the item is either an
104
     * object or array.
105
     *
106
     * @param  array|object $item
107
     * @param  string $key
108
     * @return mixed
109
     */
110
    protected function extractValue($item, $key)
111
    {
112
        if (is_object($item)) {
113
            if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) {
114
                return $item->{$key}();
115
            }
116
            return $item->{$key};
117
        } else {
118
            if (array_key_exists($key, $item)) {
119
                return $item[$key];
120
            }
121
        }
122
    }
123
124
    /**
125
     * Return the key of the current element.
126
     *
127
     * @return string
128
     */
129
    public function key()
130
    {
131
        if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
132
            return $this->lastItems[$this->endItemIdx][0];
133
        } else {
134
            if (isset($this->firstItems[$this->firstItemIdx])) {
135
                return $this->firstItems[$this->firstItemIdx][0];
136
            } else {
137
                return $this->extractValue($this->items->current(), $this->keyField);
138
            }
139
        }
140
    }
141
142
    /**
143
     * Move forward to next element.
144
     *
145
     * @return mixed
146
     */
147
    public function next()
148
    {
149
        $this->firstItemIdx++;
150
151
        if (isset($this->firstItems[$this->firstItemIdx])) {
152
            return $this->firstItems[$this->firstItemIdx][1];
153
        } else {
154
            if (!isset($this->firstItems[$this->firstItemIdx - 1])) {
155
                $this->items->next();
156
            }
157
158
            if ($this->excludedItems) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->excludedItems of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
159
                while (($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems, true)) {
160
                    $this->items->next();
161
                }
162
            }
163
        }
164
165
        if (!$this->items->valid()) {
166
            // iterator has passed the preface items, off the end of the items
167
            // list. Track through the end items to go through to the next
168
            if ($this->endItemIdx === null) {
169
                $this->endItemIdx = -1;
170
            }
171
172
            $this->endItemIdx++;
173
174
            if (isset($this->lastItems[$this->endItemIdx])) {
175
                return $this->lastItems[$this->endItemIdx];
176
            }
177
178
            return false;
179
        }
180
    }
181
182
    /**
183
     * Checks if current position is valid.
184
     *
185
     * @return boolean
186
     */
187
    public function valid()
188
    {
189
        return (
190
            (isset($this->firstItems[$this->firstItemIdx])) ||
191
            (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) ||
192
            $this->items->valid()
193
        );
194
    }
195
}
196