Completed
Push — master ( f5d71d...bfd9cb )
by Sam
12:52
created

Map_Iterator::__construct()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 13
c 1
b 0
f 0
nc 6
nop 5
dl 0
loc 22
rs 8.6737
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
	/**
60
	 * Rewind the Iterator to the first element.
61
	 *
62
	 * @return mixed
63
	 */
64
	public function rewind()
65
	{
66
		$this->firstItemIdx = 0;
67
		$this->endItemIdx = null;
68
69
		$rewoundItem = $this->items->rewind();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rewoundItem is correct as $this->items->rewind() (which targets 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...
70
71
		if (isset($this->firstItems[$this->firstItemIdx])) {
72
			return $this->firstItems[$this->firstItemIdx][1];
73
		} else {
74
			if ($rewoundItem) {
75
				return $this->extractValue($rewoundItem, $this->titleField);
76
			} else {
77
				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...
78
					$this->endItemIdx = 0;
79
80
					return $this->lastItems[0][1];
81
				}
82
			}
83
		}
84
	}
85
86
	/**
87
	 * Return the current element.
88
	 *
89
	 * @return mixed
90
	 */
91
	public function current()
92
	{
93
		if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
94
			return $this->lastItems[$this->endItemIdx][1];
95
		} else {
96
			if (isset($this->firstItems[$this->firstItemIdx])) {
97
				return $this->firstItems[$this->firstItemIdx][1];
98
			}
99
		}
100
		return $this->extractValue($this->items->current(), $this->titleField);
101
	}
102
103
	/**
104
	 * Extracts a value from an item in the list, where the item is either an
105
	 * object or array.
106
	 *
107
	 * @param  array|object $item
108
	 * @param  string $key
109
	 * @return mixed
110
	 */
111
	protected function extractValue($item, $key)
112
	{
113
		if (is_object($item)) {
114
			if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) {
115
				return $item->{$key}();
116
			}
117
			return $item->{$key};
118
		} else {
119
			if (array_key_exists($key, $item)) {
120
				return $item[$key];
121
			}
122
		}
123
	}
124
125
	/**
126
	 * Return the key of the current element.
127
	 *
128
	 * @return string
129
	 */
130
	public function key()
131
	{
132
		if (($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) {
133
			return $this->lastItems[$this->endItemIdx][0];
134
		} else {
135
			if (isset($this->firstItems[$this->firstItemIdx])) {
136
				return $this->firstItems[$this->firstItemIdx][0];
137
			} else {
138
				return $this->extractValue($this->items->current(), $this->keyField);
139
			}
140
		}
141
	}
142
143
	/**
144
	 * Move forward to next element.
145
	 *
146
	 * @return mixed
147
	 */
148
	public function next()
149
	{
150
		$this->firstItemIdx++;
151
152
		if (isset($this->firstItems[$this->firstItemIdx])) {
153
			return $this->firstItems[$this->firstItemIdx][1];
154
		} else {
155
			if (!isset($this->firstItems[$this->firstItemIdx - 1])) {
156
				$this->items->next();
157
			}
158
159
			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...
160
				while (($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems, true)) {
161
					$this->items->next();
162
				}
163
			}
164
		}
165
166
		if (!$this->items->valid()) {
167
			// iterator has passed the preface items, off the end of the items
168
			// list. Track through the end items to go through to the next
169
			if ($this->endItemIdx === null) {
170
				$this->endItemIdx = -1;
171
			}
172
173
			$this->endItemIdx++;
174
175
			if (isset($this->lastItems[$this->endItemIdx])) {
176
				return $this->lastItems[$this->endItemIdx];
177
			}
178
179
			return false;
180
		}
181
	}
182
183
	/**
184
	 * Checks if current position is valid.
185
	 *
186
	 * @return boolean
187
	 */
188
	public function valid()
189
	{
190
		return (
191
			(isset($this->firstItems[$this->firstItemIdx])) ||
192
			(($this->endItemIdx !== null) && isset($this->lastItems[$this->endItemIdx])) ||
193
			$this->items->valid()
194
		);
195
	}
196
}
197