BufferStream   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 96.55%

Importance

Changes 0
Metric Value
dl 0
loc 296
ccs 56
cts 58
cp 0.9655
rs 10
c 0
b 0
f 0
wmc 24
lcom 2
cbo 2

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A __toString() 0 4 1
A isEmpty() 0 4 1
A isFull() 0 4 1
A isReadable() 0 4 1
A read() 0 4 1
A readChunk() 0 12 2
A getContents() 0 7 1
A getSize() 0 4 1
A tell() 0 4 1
A eof() 0 4 1
A rewind() 0 4 1
A getMetadata() 0 8 3
A close() 0 6 1
A detach() 0 7 1
A isWritable() 0 4 1
A write() 0 9 2
A isSeekable() 0 4 1
A seek() 0 4 1
1
<?php
2
3
/*
4
 * CSVelte: Slender, elegant CSV for PHP
5
 * Inspired by Python's CSV module and Frictionless Data and the W3C's CSV
6
 * standardization efforts, CSVelte was written in an effort to take all the
7
 * suck out of working with CSV.
8
 *
9
 * @version   {version}
10
 * @copyright Copyright (c) 2016 Luke Visinoni <[email protected]>
11
 * @author    Luke Visinoni <[email protected]>
12
 * @license   https://github.com/deni-zen/csvelte/blob/master/LICENSE The MIT License (MIT)
13
 */
14
namespace CSVelte\IO;
15
16
use CSVelte\Contract\Streamable;
17
use CSVelte\Traits\IsReadable;
18
use CSVelte\Traits\IsWritable;
19
20
/**
21
 * Buffered Stream.
22
 *
23
 * Read operations pull from the buffer, write operations fill up the buffer.
24
 * When the buffer reaches a
25
 *
26
 * @package    CSVelte
27
 * @subpackage CSVelte\IO
28
 *
29
 * @copyright  (c) 2016, Luke Visinoni <[email protected]>
30
 * @author     Luke Visinoni <[email protected]>
31
 *
32
 * @since      v0.2.1
33
 *
34
 * @todo       Add methods to convert KB and MB to bytes so that you don't have
35
 *             to actually know how many bytes are in 16KB. You would just do
36
 *             $buffer = new BufferStream('16KB');
37
 */
38
class BufferStream implements Streamable
39
{
40
    use IsReadable, IsWritable;
41
42
    /**
43
     * Buffer contents.
44
     *
45
     * @var string|false A string containing the buffer contents
46
     */
47
    protected $buffer = '';
48
49
    /**
50
     * Is stream readable?
51
     *
52
     * @var bool Whether stream is readable
53
     */
54
    protected $readable = true;
55
56
    /**
57
     * Is stream writable?
58
     *
59
     * @var bool Whether stream is writable
60
     */
61
    protected $writable = true;
62
63
    /**
64
     * Is stream seekable?
65
     *
66
     * @var bool Whether stream is seekable
67
     */
68
    protected $seekable = false;
69
70
    /**
71
     * @var array Stream meta data
72
     *            hwm: "high water mark" - once buffer reaches this number (in bytes)
73
     *            write() operations will begin returning false defaults to 16384 bytes (16KB)
74
     */
75
    protected $meta = [
76
        'hwm' => 16384,
77
    ];
78
79
    /**
80
     * Instantiate a buffer stream.
81
     *
82
     * Instantiate a new buffer stream, optionally changing the high water mark
83
     * from its default of 16384 bytes (16KB). Once buffer reaches high water
84
     * mark, write operations will begin returning false. It's possible for buffer
85
     * size to exceed this level since it is only AFTER it is reached that writes
86
     * begin returning false.
87
     *
88
     * @param int Number (in bytes) representing buffer "high water mark"
89
     * @param null|mixed $hwm
90
     */
91 31
    public function __construct($hwm = null)
92
    {
93 31
        if (!is_null($hwm)) {
94 9
            $this->meta['hwm'] = $hwm;
95 9
        }
96 31
    }
97
98
    /**
99
     * Read the entire stream, beginning to end.
100
     *
101
     * In most stream implementations, __toString() differs from getContents()
102
     * in that it returns the entire stream rather than just the remainder, but
103
     * due to the way this stream works (sort of like a conveyor belt), this
104
     * method is an alias to getContents()
105
     *
106
     * @return string The entire stream, beginning to end
107
     */
108
    public function __toString()
109
    {
110
        return (string) $this->getContents();
111
    }
112
113 13
    public function isEmpty()
114
    {
115 13
        return $this->getSize() === 0;
116
    }
117
118 13
    public function isFull()
119
    {
120 13
        return $this->getSize() >= $this->getMetadata('hwm');
121
    }
122
123
    /**
124
     * Readability accessor.
125
     *
126
     * Despite the fact that any class that implements this interface must also
127
     * define methods such as read and readLine, that is no guarantee that an
128
     * object will necessarily be readable. This method should tell the user
129
     * whether a stream is, in fact, readable.
130
     *
131
     * @return bool True if readable, false otherwise
132
     */
133 1
    public function isReadable()
134
    {
135 1
        return $this->readable;
136
    }
137
138
    /**
139
     * Read in the specified amount of characters from the input source.
140
     *
141
     * @param int $chars Amount of characters to read from input source
142
     *
143
     * @return string|bool The specified amount of characters read from input source
144
     */
145 19
    public function read($chars)
146
    {
147 19
        return $this->readChunk(null, $chars);
148
    }
149
150
    /**
151
     * Read a chunk of buffer data.
152
     *
153
     * Removes a specific chunk of data from the buffer and return it.
154
     *
155
     * @param int|null $start
156
     * @param int|null $length
157
     *
158
     * @return string The chunk of data read from the buffer
159
     */
160 20
    public function readChunk($start = null, $length = null)
161
    {
162 20
        if ($this->buffer === false) {
163 2
            return false;
164
        }
165 19
        $top          = substr($this->buffer, 0, $start);
166 19
        $data         = substr($this->buffer, $start, $length);
167 19
        $bottom       = substr($this->buffer, $start + $length);
168 19
        $this->buffer = $top . $bottom;
169
170 19
        return $data;
171
    }
172
173
    /**
174
     * Read the remainder of the stream.
175
     *
176
     * @return string The remainder of the stream
177
     */
178 3
    public function getContents()
179
    {
180 3
        $buffer       = $this->buffer;
181 3
        $this->buffer = '';
182
183 3
        return (string) $buffer;
184
    }
185
186
    /**
187
     * Return the size (in bytes) of this readable (if known).
188
     *
189
     * @return int|null Size (in bytes) of this readable
190
     */
191 22
    public function getSize()
192
    {
193 22
        return strlen($this->buffer);
194
    }
195
196
    /**
197
     * Return the current position within the stream/readable.
198
     *
199
     * @return int|false The current position within readable
200
     */
201 2
    public function tell()
202
    {
203 2
        return false;
204
    }
205
206
    /**
207
     * Determine whether the end of the readable resource has been reached.
208
     *
209
     * @return bool Whether we're at the end of the readable
210
     */
211 7
    public function eof()
212
    {
213 7
        return empty($this->buffer);
214
    }
215
216
    /**
217
     * File must be able to be rewound when the end is reached.
218
     */
219 4
    public function rewind()
220
    {
221 4
        $this->buffer = '';
222 4
    }
223
224
    /**
225
     * Get stream metadata as an associative array or retrieve a specific key.
226
     *
227
     * The keys returned are identical to the keys returned from PHP's
228
     * stream_get_meta_data() function.
229
     *
230
     * @param string $key Specific metadata to retrieve.
231
     *
232
     * @return array|mixed|null Returns an associative array if no key is
233
     *                          provided. Returns a specific key value if a key is provided and the
234
     *                          value is found, or null if the key is not found.
235
     *
236
     * @see http://php.net/manual/en/function.stream-get-meta-data.php
237
     */
238 23
    public function getMetadata($key = null)
239
    {
240 23
        if (!is_null($key)) {
241 23
            return isset($this->meta[$key]) ? $this->meta[$key] : null;
242
        }
243
244 2
        return $this->meta;
245
    }
246
247
    /**
248
     * Closes the stream and any underlying resources.
249
     *
250
     * @return true
251
     */
252 2
    public function close()
253
    {
254 2
        $this->buffer = false;
255
256 2
        return true;
257
    }
258
259
    /**
260
     * Separates any underlying resources from the stream.
261
     *
262
     * After the stream has been detached, the stream is in an unusable state.
263
     *
264
     * @return BufferStream|null Underlying PHP stream, if any
265
     */
266 1
    public function detach()
267
    {
268 1
        $buffer       = $this->buffer;
269 1
        $this->buffer = false;
270
271 1
        return $buffer;
272
    }
273
274
    /**
275
     * Writability accessor.
276
     *
277
     * Despite the fact that any class that implements this interface must also
278
     * define methods such as write and writeLine, that is no guarantee that an
279
     * object will necessarily be writable. This method should tell the user
280
     * whether a stream is, in fact, writable.
281
     *
282
     * @return bool True if writable, false otherwise
283
     */
284 1
    public function isWritable()
285
    {
286 1
        return $this->writable;
287
    }
288
289
    /**
290
     * Write data to the output.
291
     *
292
     * @param string $data The data to write
293
     *
294
     * @return false|int The number of bytes written
295
     */
296 22
    public function write($data)
297
    {
298 22
        if ($this->getSize() >= $this->getMetadata('hwm')) {
299 1
            return false;
300
        }
301 22
        $this->buffer .= $data;
302
303 22
        return strlen($data);
304
    }
305
306
    /**
307
     * Seekability accessor.
308
     *
309
     * Despite the fact that any class that implements this interface must also
310
     * define methods such as seek, that is no guarantee that an
311
     * object will necessarily be seekable. This method should tell the user
312
     * whether a stream is, in fact, seekable.
313
     *
314
     * @return bool True if seekable, false otherwise
315
     */
316 1
    public function isSeekable()
317
    {
318 1
        return $this->seekable;
319
    }
320
321
    /**
322
     * Seek to specified offset.
323
     *
324
     * @param int $offset Offset to seek to
325
     * @param int $whence Position from whence the offset should be applied
326
     *
327
     * @return bool True if seek was successful
328
     */
329 1
    public function seek($offset, $whence = SEEK_SET)
330
    {
331 1
        return $this->seekable;
332
    }
333
}
334