1: <?php
2:
3: /**
4: * Jyxo PHP Library
5: *
6: * LICENSE
7: *
8: * This source file is subject to the new BSD license that is bundled
9: * with this package in the file license.txt.
10: * It is also available through the world-wide-web at this URL:
11: * https://github.com/jyxo/php/blob/master/license.txt
12: */
13:
14: /**
15: * Iterator which applies a callback over results (lazy-loaded calls).
16: * Supports iteration over both Traversable and array.
17: *
18: * @category Jyxo
19: * @package Jyxo_Spl
20: * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
21: * @license https://github.com/jyxo/php/blob/master/license.txt
22: * @author Jakub Tománek
23: */
24: class Jyxo_Spl_MapIterator implements Countable, Jyxo_Spl_ArrayCopy, OuterIterator, SeekableIterator
25: {
26: /**
27: * Source data we iterate over.
28: *
29: * @var Iterator
30: */
31: private $iterator;
32:
33: /**
34: * Mapping callback applied to each item.
35: *
36: * @var callback|Closure
37: */
38: private $map;
39:
40: /**
41: * Constructor.
42: *
43: * @param array|Iterator|IteratorAggregate $data Source data
44: * @param callback|Closure $map Applied callback or closure
45: * @throws InvalidArgumentException Invalid source data or callback is not callable
46: */
47: public function __construct($data, $map)
48: {
49: if (is_array($data)) {
50: $this->iterator = new ArrayIterator($data);
51: } elseif ($data instanceof IteratorAggregate) {
52: $this->iterator = $data->getIterator();
53: } elseif ($data instanceof Iterator) {
54: $this->iterator = $data;
55: } else {
56: throw new InvalidArgumentException('Supplied data argument is not traversable.');
57: }
58: if (!is_callable($map)) {
59: throw new InvalidArgumentException('Supplied callback is not callable.');
60: }
61:
62: $this->map = $map;
63: }
64:
65: /**
66: * Returns count of source data.
67: *
68: * @return integer
69: */
70: public function count()
71: {
72: $count = 0;
73: if ($this->iterator instanceof Countable) {
74: $count = count($this->iterator);
75: } else {
76: $count = iterator_count($this->iterator);
77: }
78: return $count;
79: }
80:
81: /**
82: * Rewinds the iterator to the beginning.
83: */
84: public function rewind()
85: {
86: $this->iterator->rewind();
87: }
88:
89: /**
90: * Advances the internal pointer.
91: */
92: public function next()
93: {
94: $this->iterator->next();
95: }
96:
97: /**
98: * Returns if current pointer position is valid.
99: *
100: * @return boolean
101: */
102: public function valid()
103: {
104: return $this->iterator->valid();
105: }
106:
107: /**
108: * Returns current data.
109: *
110: * @return mixed
111: */
112: public function current()
113: {
114: return $this->map($this->iterator->current());
115: }
116:
117: /**
118: * Returns current key.
119: *
120: * @return integer
121: */
122: public function key()
123: {
124: return $this->iterator->key();
125: }
126:
127: /**
128: * Converts source data to result using a callback function.
129: *
130: * @param mixed $item Source data
131: * @return mixed
132: */
133: private function map($item)
134: {
135: return call_user_func($this->map, $item);
136: }
137:
138: /**
139: * Returns all data as an array.
140: *
141: * @return array
142: */
143: public function toArray()
144: {
145: return array_map($this->map, iterator_to_array($this->iterator));
146: }
147:
148: /**
149: * Returns inner iterator (works even when constructed with array data)
150: *
151: * @return Iterator
152: */
153: public function getInnerIterator()
154: {
155: return $this->iterator;
156: }
157:
158: /**
159: * Seeks to defined position. Does NOT throw {@link OutOfBoundsException}.
160: *
161: * @param integer $position New position
162: */
163: public function seek($position)
164: {
165: if ($this->iterator instanceof SeekableIterator) {
166: try {
167: $this->iterator->seek($position);
168: } catch (OutOfBoundsException $e) {
169: // Skipped on purpose, I don't think it's necessary
170: // If you'd like to have this exception throw, remove this try-catch and add to 'else' block
171: // If (!$this->valid()) { throw new OutOfBoundsException('Invalid seek position'); };
172: }
173: } else {
174: $this->rewind();
175: for ($i = 0; $i < $position; $i++) {
176: $this->next();
177: }
178: }
179: }
180: }
181: