ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Stream.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types=1);
3 
5 
7 
18 class Stream implements FileStream
19 {
20  const MASK_ACCESS_READ = 01;
21  const MASK_ACCESS_WRITE = 02;
23 
24  private static $accessMap = [
25  'r' => self::MASK_ACCESS_READ,
26  'w+' => self::MASK_ACCESS_READ_WRITE,
27  'r+' => self::MASK_ACCESS_READ_WRITE,
28  'x+' => self::MASK_ACCESS_READ_WRITE,
29  'c+' => self::MASK_ACCESS_READ_WRITE,
30  'rb' => self::MASK_ACCESS_READ,
31  'w+b' => self::MASK_ACCESS_READ_WRITE,
32  'r+b' => self::MASK_ACCESS_READ_WRITE,
33  'x+b' => self::MASK_ACCESS_READ_WRITE,
34  'c+b' => self::MASK_ACCESS_READ_WRITE,
35  'rt' => self::MASK_ACCESS_READ,
36  'w+t' => self::MASK_ACCESS_READ_WRITE,
37  'r+t' => self::MASK_ACCESS_READ_WRITE,
38  'x+t' => self::MASK_ACCESS_READ_WRITE,
39  'c+t' => self::MASK_ACCESS_READ_WRITE,
40  'a+' => self::MASK_ACCESS_READ_WRITE,
41  'w' => self::MASK_ACCESS_WRITE,
42  'rw' => self::MASK_ACCESS_WRITE,
43  'wb' => self::MASK_ACCESS_WRITE,
44  'a' => self::MASK_ACCESS_WRITE
45  ];
46 
50  private $readable;
54  private $writeable;
58  private $seekable;
62  private $stream;
66  private $size;
70  private $uri;
74  private $customMetadata;
75 
76 
83  public function __construct($stream, StreamOptions $options = null)
84  {
85  if (!is_resource($stream)) {
86  throw new \InvalidArgumentException('Stream must be a valid resource but "' . gettype($stream) . '" was given.');
87  }
88 
89  if ($options !== null) {
90  $this->customMetadata = $options->getMetadata();
91  $this->size = ($options->getSize() !== -1) ? $options->getSize() : null;
92  } else {
93  $this->customMetadata = [];
94  }
95 
96  $this->stream = $stream;
97 
98  $meta = stream_get_meta_data($this->stream);
99  $mode = $meta['mode'];
100 
101  $this->readable = array_key_exists($mode, self::$accessMap) && boolval(self::$accessMap[$mode] & self::MASK_ACCESS_READ);
102  $this->writeable = array_key_exists($mode, self::$accessMap) && boolval(self::$accessMap[$mode] & self::MASK_ACCESS_WRITE);
103  $this->seekable = boolval($meta['seekable']);
104  $this->uri = $this->getMetadata('uri');
105  }
106 
107 
111  public function close()
112  {
113  if ($this->stream !== null && is_resource($this->stream)) {
114  PHPStreamFunctions::fclose($this->stream);
115  }
116 
117  $this->detach();
118  }
119 
120 
124  public function detach()
125  {
127  $this->stream = $this->size = $this->uri = null;
128 
129  return $stream;
130  }
131 
132 
136  public function getSize()
137  {
138 
139  //check if we know the size
140  if ($this->size !== null) {
141  return $this->size;
142  }
143 
144  //check if stream is detached
145  if ($this->stream === null) {
146  return null;
147  }
148 
149  //clear stat cache if we got a uri (indicates that we have a file resource)
150  if ($this->uri !== null) {
151  clearstatcache(true, $this->uri);
152  }
153 
154  $stats = fstat($this->stream);
155  if (array_key_exists('size', $stats)) {
156  $this->size = $stats['size'];
157  return $this->size;
158  }
159 
160  //unable to determine stream size
161  return null;
162  }
163 
164 
168  public function tell()
169  {
170  $this->assertStreamAttached();
171 
172  $result = PHPStreamFunctions::ftell($this->stream);
173 
174  if ($result === false) {
175  throw new \RuntimeException('Unable to determine stream position');
176  }
177 
178  return $result;
179  }
180 
181 
185  public function eof()
186  {
187  $this->assertStreamAttached();
188 
189  return feof($this->stream);
190  }
191 
192 
196  public function isSeekable()
197  {
198  return $this->seekable;
199  }
200 
201 
205  public function seek($offset, $whence = SEEK_SET)
206  {
207  $this->assertStreamAttached();
208 
209  if (!$this->isSeekable()) {
210  throw new \RuntimeException('Stream is not seekable');
211  }
212 
213  if (PHPStreamFunctions::fseek($this->stream, $offset, $whence) === -1) {
214  throw new \RuntimeException("Unable to seek to stream position \"$offset\" with whence \"$whence\"");
215  }
216  }
217 
218 
222  public function rewind()
223  {
224  $this->seek(0);
225  }
226 
227 
231  public function isWritable()
232  {
233  return $this->writeable;
234  }
235 
236 
240  public function write($string)
241  {
242  $this->assertStreamAttached();
243 
244  if (!$this->isWritable()) {
245  throw new \RuntimeException('Can not write to a non-writable stream');
246  }
247 
248  //we can't know the new size
249  $this->size = null;
250  $result = PHPStreamFunctions::fwrite($this->stream, $string);
251 
252  if ($result === false) {
253  throw new \RuntimeException('Unable to write to stream');
254  }
255 
256  return $result;
257  }
258 
259 
263  public function isReadable()
264  {
265  return $this->readable;
266  }
267 
268 
272  public function read($length)
273  {
274  $this->assertStreamAttached();
275 
276  if (!$this->isReadable()) {
277  throw new \RuntimeException('Can not read from non-readable stream');
278  }
279 
280  if ($length < 0) {
281  throw new \RuntimeException('Length parameter must not be negative');
282  }
283 
284  if ($length === 0) {
285  return '';
286  }
287 
288  $junk = PHPStreamFunctions::fread($this->stream, $length);
289  if ($junk === false) {
290  throw new \RuntimeException('Unable to read from stream');
291  }
292 
293  return $junk;
294  }
295 
296 
300  public function getContents()
301  {
302  $this->assertStreamAttached();
303 
304  $content = PHPStreamFunctions::stream_get_contents($this->stream);
305 
306  if ($content === false) {
307  throw new \RuntimeException('Unable to read stream contents');
308  }
309 
310  return $content;
311  }
312 
313 
317  public function getMetadata($key = null)
318  {
319 
320  //return empty array if stream is detached
321  if ($this->stream === null) {
322  return [];
323  }
324 
325  //return merged metadata if key is missing
326  if ($key === null) {
327  return array_merge(stream_get_meta_data($this->stream), $this->customMetadata);
328  }
329 
330  //return value if key was provided
331 
332  //try fetch data from custom metadata
333  if (array_key_exists($key, $this->customMetadata)) {
334  return $this->customMetadata[$key];
335  }
336 
337  //try to fetch data from php resource metadata
338  $meta = stream_get_meta_data($this->stream);
339  if (array_key_exists($key, $meta)) {
340  return $meta[$key];
341  }
342 
343  //the key was not found in standard and custom metadata.
344  return null;
345  }
346 
350  public function __toString()
351  {
352  try {
353  $this->rewind();
354  return strval($this->getContents());
355  } catch (\Exception $ex) {
356  //to string must not throw an error.
357  return '';
358  }
359  }
360 
361 
365  public function __destruct()
366  {
367 
368  //cleanup the resource on object destruction if the stream is not detached.
369  if (!is_null($this->stream)) {
370  $this->close();
371  }
372  }
373 
374 
381  private function assertStreamAttached()
382  {
383  if ($this->stream === null) {
384  throw new \RuntimeException('Stream is detached');
385  }
386  }
387 }
$result
seek($offset, $whence=SEEK_SET)
Definition: Stream.php:205
assertStreamAttached()
Checks if the stream is attached to the wrapper.
Definition: Stream.php:381
static fseek($stream, $offset, $whence)
fseek wrapper.
static fread($handle, $length)
fread wrapper
static fclose($handle)
fclose wrapper
__construct($stream, StreamOptions $options=null)
Stream constructor.
Definition: Stream.php:83
font size
Definition: langcheck.php:162
static fwrite($handle, $string, $length=null)
fwrite wrapper
static stream_get_contents($handle, $length=-1)
stream_get_contents wrapper
$key
Definition: croninfo.php:18
Interface FileStream.
Definition: FileStream.php:20