-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathIteratorStream.php
240 lines (210 loc) · 5.12 KB
/
IteratorStream.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
<?php
/**
* @copyright Copyright (c) 2015 Matthew Weier O'Phinney (https://mwop.net)
* @license http://opensource.org/licenses/BSD-2-Clause BSD-2-Clause
*/
namespace Psr7Examples;
use Countable;
use IteratorAggregate;
use Traversable;
use Psr\Http\Message\StreamableInterface;
/**
* Iterator-based stream implementation.
*
* Wraps an iterator to allow seeking, reading, and casting to string.
*
* Keys are ignored, and content is concatenated without separators.
*/
class IteratorStream implements StreamableInterface
{
/**
* @var Traversable
*/
private $iterator;
/**
* Current position in iterator
*
* @var int
*/
private $position = 0;
/**
* Construct a stream instance using an iterator.
*
* If the iterator is an IteratorAggregate, pulls the inner iterator
* and composes that instead, to ensure we have access to the various
* iterator capabilities.
*
* @param Traversable $iterator
*/
public function __construct(Traversable $iterator)
{
if ($iterator instanceof IteratorAggregate) {
$iterator = $iterator->getIterator();
}
$this->iterator = $iterator;
}
/**
* @return string
*/
public function __toString()
{
$this->iterator->rewind();
return $this->getContents();
}
/**
* No-op.
*
* @return void
*/
public function close()
{
}
/**
* @return null|Traversable
*/
public function detach()
{
$iterator = $this->iterator;
$this->iterator = null;
return $iterator;
}
/**
* @return int|null Returns the size of the iterator, or null if unknown.
*/
public function getSize()
{
if ($this->iterator instanceof Countable) {
return count($this->iterator);
}
return null;
}
/**
* @return int|bool Position of the iterator or false on error.
*/
public function tell()
{
return $this->position;
}
/**
* @return bool
*/
public function eof()
{
if ($this->iterator instanceof Countable) {
return ($this->position === count($this->iterator));
}
return (! $this->iterator->valid());
}
/**
* @return bool
*/
public function isSeekable()
{
return true;
}
/**
* @param int $offset Stream offset
* @param int $whence Ignored.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function seek($offset, $whence = SEEK_SET)
{
if (! is_int($offset) && ! is_numeric($offset)) {
return false;
}
$offset = (int) $offset;
if ($offset < 0) {
return false;
}
$key = $this->iterator->key();
if (! is_int($key) && ! is_numeric($key)) {
$key = 0;
$this->iterator->rewind();
}
if ($key >= $offset) {
$key = 0;
$this->iterator->rewind();
}
while ($this->iterator->valid() && $key < $offset) {
$this->iterator->next();
++$key;
}
$this->position = $key;
return true;
}
/**
* @see seek()
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function rewind()
{
$this->iterator->rewind();
$this->position = 0;
return true;
}
/**
* @return bool Always returns false
*/
public function isWritable()
{
return false;
}
/**
* Non-writable
*
* @param string $string The string that is to be written.
* @return int|bool Always returns false
*/
public function write($string)
{
return false;
}
/**
* @return bool Always returns true
*/
public function isReadable()
{
return true;
}
/**
* @param int $length Read up to $length items from the object and return
* them. Fewer than $length items may be returned if underlying iterator
* has fewer items.
* @return string|false Returns the data read from the iterator, false if
* unable to read or if an error occurs.
*/
public function read($length)
{
$index = 0;
$contents = '';
while ($this->iterator->valid() && $index < $length) {
$contents .= $this->iterator->current();
$this->iterator->next();
++$this->position;
++$index;
}
return $contents;
}
/**
* @return string
*/
public function getContents()
{
$contents = '';
while ($this->iterator->valid()) {
$contents .= $this->iterator->current();
$this->iterator->next();
++$this->position;
}
return $contents;
}
/**
* @param string $key Specific metadata to retrieve.
* @return array|null Returns an empty array if no key is provided, and
* null otherwise.
*/
public function getMetadata($key = null)
{
return ($key === null) ? array() : null;
}
}