PumpStream::isSeekable()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php declare(strict_types=1);
2
3
namespace One\Http;
4
5
use Psr\Http\Message\StreamInterface;
6
7
/**
8
 * Provides a read only stream that pumps data from a PHP callable.
9
 *
10
 * When invoking the provided callable, the PumpStream will pass the amount of
11
 * data requested to read to the callable. The callable can choose to ignore
12
 * this value and return fewer or more bytes than requested. Any extra data
13
 * returned by the provided callable is buffered internally until drained using
14
 * the read() function of the PumpStream. The provided callable MUST return
15
 * false when there is no more data to read.
16
 * @property mixed $source
17
 * @property int $size
18
 * @property mixed $tellPos
19
 * @property mixed[] $metadata
20
 * @property mixed $buffer
21
 */
22
class PumpStream implements StreamInterface
23
{
24
    /**
25
     * Source
26
     * @var mixed
27
     */
28
    private $source;
29
30
    /**
31
     * size
32
     * @var int
33
     */
34
    private $size;
35
36
    /**
37
     * tell pos
38
     * @var mixed
39
     */
40
    private $tellPos;
41
42
    /**
43
     * Metadata
44
     * @var mixed
45
     */
46
    private $metadata;
47
48
    /**
49
     * buffer
50
     * @var mixed
51
     */
52
    private $buffer;
53
54
    /**
55
     * @param callable $source Source of the stream data. The callable MAY
56
     *                         accept an integer argument used to control the
57
     *                         amount of data to return. The callable MUST
58
     *                         return a string when called, or false on error
59
     *                         or EOF.
60
     * @param array $options   Stream options:
61
     *                         - metadata: Hash of metadata to use with stream.
62
     *                         - size: Size of the stream, if known.
63
     */
64
    public function __construct(callable $source, array $options = [])
65
    {
66
        $this->source = $source;
67
        $this->size = $options['size'] ?? null;
68
        $this->tellPos = 0;
69
        $this->metadata = $options['metadata'] ?? [];
70
        $this->buffer = new BufferStream();
71
    }
72
73
    /**
74
     * @inheritDoc
75
     */
76
    public function __toString()
77
    {
78
        try {
79
            return \One\copy_to_string($this);
80
        } catch (\Throwable $e) {
81
            return '';
82
        }
83
    }
84
85
    /**
86
     * @inheritDoc
87
     */
88
    public function close(): void
89
    {
90
        $this->detach();
91
    }
92
93
    /**
94
     * @inheritDoc
95
     */
96
    public function detach(): void
97
    {
98
        $this->tellPos = false;
99
        $this->source = null;
100
    }
101
102
    /**
103
     * @inheritDoc
104
     */
105
    public function getSize()
106
    {
107
        return $this->size;
108
    }
109
110
    /**
111
     * @inheritDoc
112
     */
113
    public function tell()
114
    {
115
        return $this->tellPos;
116
    }
117
118
    /**
119
     * @inheritDoc
120
     */
121
    public function eof()
122
    {
123
        return ! $this->source;
124
    }
125
126
    /**
127
     * @inheritDoc
128
     */
129
    public function isSeekable()
130
    {
131
        return false;
132
    }
133
134
    /**
135
     * @inheritDoc
136
     */
137
    public function rewind(): void
138
    {
139
        $this->seek(0);
140
    }
141
142
    /**
143
     * @inheritDoc
144
     */
145
    public function seek($offset, $whence = SEEK_SET): void
146
    {
147
        throw new \RuntimeException('Cannot seek a PumpStream');
148
    }
149
150
    /**
151
     * @inheritDoc
152
     */
153
    public function isWritable()
154
    {
155
        return false;
156
    }
157
158
    /**
159
     * @inheritDoc
160
     */
161
    public function write($string): void
162
    {
163
        throw new \RuntimeException('Cannot write to a PumpStream');
164
    }
165
166
    /**
167
     * @inheritDoc
168
     */
169
    public function isReadable()
170
    {
171
        return true;
172
    }
173
174
    /**
175
     * @inheritDoc
176
     */
177
    public function read($length)
178
    {
179
        $data = $this->buffer->read($length);
180
        $readLen = strlen($data);
181
        $this->tellPos += $readLen;
182
        $remaining = $length - $readLen;
183
184
        if ($remaining) {
185
            $this->pump($remaining);
186
            $data .= $this->buffer->read($remaining);
187
            $this->tellPos += strlen($data) - $readLen;
188
        }
189
190
        return $data;
191
    }
192
193
    /**
194
     * @inheritDoc
195
     */
196
    public function getContents()
197
    {
198
        $result = '';
199
        while (! $this->eof()) {
200
            $result .= $this->read(1000000);
201
        }
202
203
        return $result;
204
    }
205
206
    /**
207
     * @inheritDoc
208
     */
209
    public function getMetadata($key = null)
210
    {
211
        if (! $key) {
212
            return $this->metadata;
213
        }
214
215
        return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
216
    }
217
218
    /**
219
     * @inheritdoc
220
     */
221
    private function pump($length): void
222
    {
223
        if ($this->source) {
224
            do {
225
                $data = call_user_func($this->source, $length);
226
                if ($data === false || $data === null) {
227
                    $this->source = null;
228
                    return;
229
                }
230
                $this->buffer->write($data);
231
                $length -= strlen($data);
232
            } while ($length > 0);
233
        }
234
    }
235
}
236