Completed
Push — master ( 5c1aea...9ef1c4 )
by Luke
03:03
created

BufferStream   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 96.49%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 281
ccs 55
cts 57
cp 0.9649
rs 10
c 1
b 0
f 0
wmc 24
lcom 2
cbo 2

19 Methods

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