Completed
Push — master ( 5f8ab7...343914 )
by Raffael
03:11
created

StreamIterator::read()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 3
nop 1
dl 0
loc 17
ccs 11
cts 11
cp 1
crap 4
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Stream iterator
7
 *
8
 * @copyright   Copryright (c) 2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     MIT https://opensource.org/licenses/MIT
10
 */
11
12
namespace StreamIterator;
13
14
use Closure;
15
use Countable;
16
use IteratorAggregate;
17
use Psr\Http\Message\StreamInterface;
18
use Traversable;
19
20
/**
21
 * Wraps an interator to iterate through and cast each entry to string via a callback.
22
 */
23
class StreamIterator implements StreamInterface
24
{
25
    /**
26
     * @var Traversable
27
     */
28
    private $iterator;
29
30
    /**
31
     * Current position in iterator.
32
     *
33
     * @var int
34
     */
35
    private $position = 0;
36
37
    /**
38
     * Stringify callback.
39
     *
40
     * @var Closure
41
     */
42
    private $stringify;
43
44
    /**
45
     * Construct a stream instance using an iterator.
46
     *
47
     * If the iterator is an IteratorAggregate, pulls the inner iterator
48
     * and composes that instead, to ensure we have access to the various
49
     * iterator capabilities.
50
     */
51 20
    public function __construct(Traversable $iterator, Closure $stringify = null)
52
    {
53 20
        if ($iterator instanceof IteratorAggregate) {
54
            $iterator = $iterator->getIterator();
55
        }
56 20
        $this->iterator = $iterator;
57 20
        $this->stringify = $stringify;
58 20
    }
59
60
    /**
61
     * @return string
62
     */
63 2
    public function __toString()
64
    {
65 2
        $this->iterator->rewind();
0 ignored issues
show
Bug introduced by
The method rewind() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as Yaf_Config_Simple or Yaf\Session or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or MongoGridFSCursor or Nette\Utils\Finder or Nette\Utils\Html or Nette\Utils\ArrayList or SimpleXMLIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

65
        $this->iterator->/** @scrutinizer ignore-call */ 
66
                         rewind();
Loading history...
66
67 2
        return $this->getContents();
68
    }
69
70
    /**
71
     * No-op.
72
     */
73 1
    public function close()
74
    {
75 1
    }
76
77
    /**
78
     * @return null|Traversable
79
     */
80 1
    public function detach()
81
    {
82 1
        $iterator = $this->iterator;
83 1
        $this->iterator = null;
84
85 1
        return $iterator;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $iterator returns the type Traversable which is incompatible with the return type mandated by Psr\Http\Message\StreamInterface::detach() of null|resource.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
86
    }
87
88
    /**
89
     * @return null|int returns the size of the iterator, or null if unknown
90
     */
91 1
    public function getSize()
92
    {
93 1
        if ($this->iterator instanceof Countable) {
94 1
            return count($this->iterator);
95
        }
96
97
        return null;
98
    }
99
100
    /**
101
     * @return int Position of the iterator
102
     */
103 2
    public function tell()
104
    {
105 2
        return $this->position;
106
    }
107
108
    /**
109
     * End of File.
110
     *
111
     * @return bool
112
     */
113 2
    public function eof()
114
    {
115 2
        if ($this->iterator instanceof Countable) {
116 2
            return $this->position === count($this->iterator);
117
        }
118
119
        return !$this->iterator->valid();
0 ignored issues
show
Bug introduced by
The method valid() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as Yaf_Config_Simple or Yaf\Session or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or MongoGridFSCursor or Nette\Utils\Finder or Nette\Utils\Html or Nette\Utils\ArrayList or SimpleXMLIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
        return !$this->iterator->/** @scrutinizer ignore-call */ valid();
Loading history...
120
    }
121
122
    /**
123
     * Check if seekable.
124
     *
125
     * @return bool
126
     */
127 1
    public function isSeekable()
128
    {
129 1
        return true;
130
    }
131
132
    /**
133
     * Seek the iterator.
134
     *
135
     * @param int $offset Stream offset
136
     * @param int $whence ignored
137
     *
138
     * @return bool returns TRUE on success or FALSE on failure
139
     */
140 2
    public function seek($offset, $whence = SEEK_SET)
141
    {
142 2
        if (!is_int($offset) && !is_numeric($offset)) {
0 ignored issues
show
introduced by
The condition is_int($offset) is always true.
Loading history...
143 1
            return false;
144
        }
145
146 1
        $offset = (int) $offset;
147 1
        if ($offset < 0) {
148
            return false;
149
        }
150
151 1
        $key = $this->iterator->key();
0 ignored issues
show
Bug introduced by
The method key() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as Yaf_Config_Simple or Yaf\Session or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or MongoGridFSCursor or Nette\Utils\Finder or Nette\Utils\Html or Nette\Utils\ArrayList or SimpleXMLIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

151
        /** @scrutinizer ignore-call */ 
152
        $key = $this->iterator->key();
Loading history...
152 1
        if (!is_int($key) && !is_numeric($key)) {
153
            $key = 0;
154
            $this->iterator->rewind();
155
        }
156
157 1
        if ($key >= $offset) {
158
            $key = 0;
159
            $this->iterator->rewind();
160
        }
161
162 1
        while ($this->iterator->valid() && $key < $offset) {
163 1
            $this->iterator->next();
0 ignored issues
show
Bug introduced by
The method next() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as IntlCodePointBreakIterator or Yaf_Config_Simple or Yaf\Session or IntlRuleBasedBreakIterator or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or IntlBreakIterator or MongoGridFSCursor or Nette\Utils\Finder or Nette\Utils\Html or Nette\Utils\ArrayList or SimpleXMLIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
            $this->iterator->/** @scrutinizer ignore-call */ 
164
                             next();
Loading history...
164 1
            ++$key;
165
        }
166
167 1
        $this->position = $key;
168
169 1
        return true;
170
    }
171
172
    /**
173
     * @see seek()
174
     *
175
     * @return bool returns true on success or false on failure
176
     */
177 1
    public function rewind()
178
    {
179 1
        $this->iterator->rewind();
180 1
        $this->position = 0;
181
182 1
        return true;
183
    }
184
185
    /**
186
     * Non-writable.
187
     *
188
     * @return bool Always returns false
189
     */
190 1
    public function isWritable()
191
    {
192 1
        return false;
193
    }
194
195
    /**
196
     * Non-writable.
197
     *
198
     * @param string $string the string that is to be written
199
     *
200
     * @return bool|int Always returns false
201
     */
202 1
    public function write($string)
203
    {
204 1
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the return type mandated by Psr\Http\Message\StreamInterface::write() of integer.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
205
    }
206
207
    /**
208
     * @return bool
209
     */
210 1
    public function isReadable()
211
    {
212 1
        return true;
213
    }
214
215
    /**
216
     * Read content from iterator with a lenght limit (number of entries).
217
     *
218
     * @param int $length Read up to $length items from the iterator
219
     *
220
     * @return string
221
     */
222 5
    public function read($length)
223
    {
224 5
        $index = 0;
225 5
        $contents = '';
226 5
        while ($this->iterator->valid() && $index < $length) {
227 5
            if ($this->stringify !== null) {
228 1
                $contents .= $this->stringify->call($this, $this->iterator->current());
0 ignored issues
show
Bug introduced by
The method current() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as IntlCodePointBreakIterator or Yaf_Config_Simple or Yaf\Session or IntlRuleBasedBreakIterator or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or IntlBreakIterator or MongoGridFSCursor or Nette\Utils\Finder or Nette\Utils\Html or Nette\Utils\ArrayList or SimpleXMLIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

228
                $contents .= $this->stringify->call($this, $this->iterator->/** @scrutinizer ignore-call */ current());
Loading history...
229
            } else {
230 4
                $contents .= $this->iterator->current();
231
            }
232
233 5
            $this->iterator->next();
234 5
            ++$this->position;
235 5
            ++$index;
236
        }
237
238 5
        return $contents;
239
    }
240
241
    /**
242
     * @return string
243
     */
244 4
    public function getContents()
245
    {
246 4
        $contents = '';
247 4
        while ($this->iterator->valid()) {
248 4
            if ($this->stringify !== null) {
249 1
                $contents .= $this->stringify->call($this, $this->iterator->current());
250
            } else {
251 3
                $contents .= $this->iterator->current();
252
            }
253
254 4
            $this->iterator->next();
255 4
            ++$this->position;
256
        }
257
258 4
        return $contents;
259
    }
260
261
    /**
262
     * @param string $key specific metadata to retrieve
263
     *
264
     * @return null|array returns an empty array if no key is provided, and
265
     *                    null otherwise
266
     */
267 2
    public function getMetadata($key = null)
268
    {
269 2
        return ($key === null) ? [] : null;
270
    }
271
}
272