Passed
Pull Request — master (#56)
by Charis
01:50
created

PumpStream   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 28
eloc 52
dl 0
loc 190
rs 10
c 0
b 0
f 0

17 Methods

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