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