ILIAS  release_7 Revision v7.30-3-g800a261c036
Stream.php
Go to the documentation of this file.
1<?php
2declare(strict_types=1);
3
5
7
15class Stream implements FileStream
16{
17 const MASK_ACCESS_READ = 01;
20
21 private static $accessMap = [
42 ];
43
47 private $readable;
51 private $writeable;
55 private $seekable;
59 private $stream;
63 private $size;
67 private $uri;
72
78 public function __construct($stream, StreamOptions $options = null)
79 {
80 if (!is_resource($stream)) {
81 throw new \InvalidArgumentException('Stream must be a valid resource but "' . gettype($stream) . '" was given.');
82 }
83
84 if ($options !== null) {
85 $this->customMetadata = $options->getMetadata();
86 $this->size = ($options->getSize() !== -1) ? $options->getSize() : null;
87 } else {
88 $this->customMetadata = [];
89 }
90
91 $this->stream = $stream;
92
93 $meta = stream_get_meta_data($this->stream);
94 $mode = $meta['mode'];
95
96 $this->readable = array_key_exists($mode,
97 self::$accessMap) && boolval(self::$accessMap[$mode]&self::MASK_ACCESS_READ);
98 $this->writeable = array_key_exists($mode,
99 self::$accessMap) && boolval(self::$accessMap[$mode]&self::MASK_ACCESS_WRITE);
100 $this->seekable = boolval($meta['seekable']);
101 $this->uri = $this->getMetadata('uri');
102 }
103
107 public function close()
108 {
109 if ($this->stream !== null && is_resource($this->stream)) {
110 PHPStreamFunctions::fclose($this->stream);
111 }
112
113 $this->detach();
114 }
115
119 public function detach()
120 {
122 $this->stream = $this->size = $this->uri = null;
123
124 return $stream;
125 }
126
130 public function getSize()
131 {
132
133 //check if we know the size
134 if ($this->size !== null) {
135 return $this->size;
136 }
137
138 //check if stream is detached
139 if ($this->stream === null) {
140 return null;
141 }
142
143 //clear stat cache if we got a uri (indicates that we have a file resource)
144 if ($this->uri !== null) {
145 clearstatcache(true, $this->uri);
146 }
147
148 $stats = fstat($this->stream);
149 if (array_key_exists('size', $stats)) {
150 $this->size = $stats['size'];
151 return $this->size;
152 }
153
154 //unable to determine stream size
155 return null;
156 }
157
161 public function tell()
162 {
163 $this->assertStreamAttached();
164
165 $result = PHPStreamFunctions::ftell($this->stream);
166
167 if ($result === false) {
168 throw new \RuntimeException('Unable to determine stream position');
169 }
170
171 return $result;
172 }
173
177 public function eof()
178 {
179 $this->assertStreamAttached();
180
181 return feof($this->stream);
182 }
183
187 public function isSeekable()
188 {
189 return $this->seekable;
190 }
191
195 public function seek($offset, $whence = SEEK_SET)
196 {
197 $this->assertStreamAttached();
198
199 if (!$this->isSeekable()) {
200 throw new \RuntimeException('Stream is not seekable');
201 }
202
203 if (PHPStreamFunctions::fseek($this->stream, $offset, $whence) === -1) {
204 throw new \RuntimeException("Unable to seek to stream position \"$offset\" with whence \"$whence\"");
205 }
206 }
207
211 public function rewind()
212 {
213 $this->seek(0);
214 }
215
219 public function isWritable()
220 {
221 return $this->writeable;
222 }
223
227 public function write($string)
228 {
229 $this->assertStreamAttached();
230
231 if (!$this->isWritable()) {
232 throw new \RuntimeException('Can not write to a non-writable stream');
233 }
234
235 //we can't know the new size
236 $this->size = null;
237 $result = PHPStreamFunctions::fwrite($this->stream, $string);
238
239 if ($result === false) {
240 throw new \RuntimeException('Unable to write to stream');
241 }
242
243 return $result;
244 }
245
249 public function isReadable()
250 {
251 return $this->readable;
252 }
253
257 public function read($length)
258 {
259 $this->assertStreamAttached();
260
261 if (!$this->isReadable()) {
262 throw new \RuntimeException('Can not read from non-readable stream');
263 }
264
265 if ($length < 0) {
266 throw new \RuntimeException('Length parameter must not be negative');
267 }
268
269 if ($length === 0) {
270 return '';
271 }
272
273 $junk = PHPStreamFunctions::fread($this->stream, $length);
274 if ($junk === false) {
275 throw new \RuntimeException('Unable to read from stream');
276 }
277
278 return $junk;
279 }
280
284 public function getContents()
285 {
286 $this->assertStreamAttached();
287
288 $content = PHPStreamFunctions::stream_get_contents($this->stream);
289
290 if ($content === false) {
291 throw new \RuntimeException('Unable to read stream contents');
292 }
293
294 return $content;
295 }
296
300 public function getMetadata($key = null)
301 {
302
303 //return empty array if stream is detached
304 if ($this->stream === null) {
305 return [];
306 }
307
308 //return merged metadata if key is missing
309 if ($key === null) {
310 return array_merge(stream_get_meta_data($this->stream), $this->customMetadata);
311 }
312
313 //return value if key was provided
314
315 //try fetch data from custom metadata
316 if (array_key_exists($key, $this->customMetadata)) {
317 return $this->customMetadata[$key];
318 }
319
320 //try to fetch data from php resource metadata
321 $meta = stream_get_meta_data($this->stream);
322 if (array_key_exists($key, $meta)) {
323 return $meta[$key];
324 }
325
326 //the key was not found in standard and custom metadata.
327 return null;
328 }
329
333 public function __toString()
334 {
335 try {
336 $this->rewind();
337 return strval($this->getContents());
338 } catch (\Exception $ex) {
339 //to string must not throw an error.
340 return '';
341 }
342 }
343
347 public function __destruct()
348 {
349
350 //cleanup the resource on object destruction if the stream is not detached.
351 if (!is_null($this->stream)) {
352 $this->close();
353 }
354 }
355
361 private function assertStreamAttached()
362 {
363 if ($this->stream === null) {
364 throw new \RuntimeException('Stream is detached');
365 }
366 }
367}
$result
An exception for terminatinating execution or to throw for unit testing.
Class StreamOptions The streaming options are used by the stream implementation.
getMetadata($key=null)
@inheritDoc
Definition: Stream.php:300
__construct($stream, StreamOptions $options=null)
Stream constructor.
Definition: Stream.php:78
assertStreamAttached()
Checks if the stream is attached to the wrapper.
Definition: Stream.php:361
write($string)
@inheritDoc
Definition: Stream.php:227
read($length)
@inheritDoc
Definition: Stream.php:257
seek($offset, $whence=SEEK_SET)
@inheritDoc
Definition: Stream.php:195
Class PHPFunctions The purpose of this class is to wrap all stream handling php functions.
static fwrite($handle, $string, $length=null)
fwrite wrapper
static fread($handle, $length)
fread wrapper
static stream_get_contents($handle, $length=-1)
stream_get_contents wrapper
static fclose($handle)
fclose wrapper
static fseek($stream, $offset, $whence)
fseek wrapper.
Interface FileStream The base interface for all filesystem streams.
Definition: FileStream.php:18