Passed
Push — master ( 8c77bf...be291e )
by Zaahid
04:42
created

Base64Stream::close()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the ZBateson\StreamDecorators project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
namespace ZBateson\StreamDecorators;
8
9
use Psr\Http\Message\StreamInterface;
10
use GuzzleHttp\Psr7\StreamDecoratorTrait;
11
use GuzzleHttp\Psr7\BufferStream;
12
use RuntimeException;
13
14
/**
15
 * GuzzleHttp\Psr7 stream decoder extension for base64 streams.
16
 *
17
 * @author Zaahid Bateson
18
 */
19
class Base64Stream implements StreamInterface
20
{
21
    use StreamDecoratorTrait;
22
23
    /**
24
     * @var BufferStream buffered bytes
25
     */
26
    private $buffer;
27
28
    /**
29
     * @var string remainder of write operation if the bytes didn't align to 3
30
     *      bytes
31
     */
32
    private $remainder = '';
33
34
    /**
35
     * @var int current number of read/written bytes (for tell())
36
     */
37
    private $position = 0;
38
39
    /**
40
     * @param StreamInterface $stream
41
     */
42 8
    public function __construct(StreamInterface $stream)
43
    {
44 8
        $this->stream = $stream;
45 8
        $this->buffer = new BufferStream();
46 8
    }
47
48
    /**
49
     * Returns the current position of the file read/write pointer
50
     *
51
     * @return int
52
     */
53 1
    public function tell()
54
    {
55 1
        return $this->position;
56
    }
57
58
    /**
59
     * Returns null, getSize isn't supported
60
     *
61
     * @return null
62
     */
63 2
    public function getSize()
64
    {
65 2
        return null;
66
    }
67
68
    /**
69
     * Not implemented (yet).
70
     *
71
     * Seek position can be calculated.
72
     *
73
     * @param int $offset
74
     * @param int $whence
75
     * @throws RuntimeException
76
     */
77 1
    public function seek($offset, $whence = SEEK_SET)
78
    {
79 1
        throw new RuntimeException('Cannot seek a Base64Stream');
80
    }
81
82
    /**
83
     * Overridden to return false
84
     *
85
     * @return boolean
86
     */
87 1
    public function isSeekable()
88
    {
89 1
        return false;
90
    }
91
92
    /**
93
     * Returns true if the end of stream has been reached.
94
     *
95
     * @return type
0 ignored issues
show
Bug introduced by
The type ZBateson\StreamDecorators\type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
96
     */
97 6
    public function eof()
98
    {
99 6
        return ($this->buffer->eof() && $this->stream->eof());
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->buffer->eo...&& $this->stream->eof() returns the type boolean which is incompatible with the documented return type ZBateson\StreamDecorators\type.
Loading history...
100
    }
101
102
    /**
103
     * Fills the internal byte buffer after reading and decoding data from the
104
     * underlying stream.
105
     *
106
     * @param int $length
107
     */
108 6
    private function fillBuffer($length)
109
    {
110 6
        $fill = 8192;
111 6
        while ($this->buffer->getSize() < $length) {
112 6
            $read = base64_decode($this->stream->read($fill));
113 6
            if ($read === false || $read === '') {
114 6
                break;
115
            }
116 6
            $this->buffer->write($read);
117
        }
118 6
    }
119
120
    /**
121
     * Attempts to read $length bytes after decoding them, and returns them.
122
     *
123
     * Note that reading and writing to the same stream may result in wrongly
124
     * encoded data and is not supported.
125
     *
126
     * @param int $length
127
     * @return string
128
     */
129 6
    public function read($length)
130
    {
131
        // let Guzzle decide what to do.
132 6
        if ($length <= 0 || $this->eof()) {
133 1
            return $this->stream->read($length);
134
        }
135 6
        $this->fillBuffer($length);
136 6
        $ret = $this->buffer->read($length);
137 6
        $this->position += strlen($ret);
138 6
        return $ret;
139
    }
140
141
    /**
142
     * Writes the passed string to the underlying stream after encoding it to
143
     * base64.
144
     *
145
     * Base64Stream::close or detach must be called.  Failing to do so may
146
     * result in 1-2 bytes missing from the end of the stream if there's a
147
     * remainder.  Note that the default Stream destructor calls close as well.
148
     *
149
     * Note that reading and writing to the same stream may result in wrongly
150
     * encoded data and is not supported.
151
     *
152
     * @param string $string
153
     * @return int the number of bytes written
154
     */
155 2
    public function write($string)
156
    {
157 2
        $bytes = $this->remainder . $string;
158 2
        $len = strlen($bytes);
159 2
        if (($len % 3) !== 0) {
160 2
            $this->remainder = substr($bytes, -($len % 3));
161 2
            $bytes = substr($bytes, 0, $len - ($len % 3));
162
        } else {
163 2
            $this->remainder = '';
164
        }
165 2
        $this->stream->write(base64_encode($bytes));
166 2
        $written = strlen($string);
167 2
        $this->position += $len;
168 2
        return $written;
169
    }
170
171
    /**
172
     * Writes out any remaining bytes at the end of the stream and closes.
173
     */
174 2
    private function beforeClose()
175
    {
176 2
        if ($this->isWritable() && $this->remainder !== '') {
177 2
            $this->stream->write(base64_encode($this->remainder));
178 2
            $this->remainder = '';
179
        }
180 2
    }
181
182
    /**
183
     * Closes the underlying stream after writing out any remaining bytes
184
     * needing to be encoded.
185
     */
186 1
    public function close()
187
    {
188 1
        $this->beforeClose();
189 1
        $this->stream->close();
190 1
    }
191
192
    /**
193
     * Detaches the underlying stream after writing out any remaining bytes
194
     * needing to be encoded.
195
     */
196 1
    public function detach()
197
    {
198 1
        $this->beforeClose();
199 1
        $this->stream->detach();
200 1
    }
201
}
202