PHP Classes

File: src/Adapter/Generic/Stream.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   sapient   src/Adapter/Generic/Stream.php   Download  
File: src/Adapter/Generic/Stream.php
Role: Class source
Content type: text/plain
Description: Class source
Class: sapient
Add a security layer to server to server requests
Author: By
Last change:
Date: 7 years ago
Size: 14,122 bytes
 

Contents

Class file image Download
<?php declare(strict_types=1); namespace ParagonIE\Sapient\Adapter\Generic; use Psr\Http\Message\StreamInterface; /** * Class Stream * @package ParagonIE\Sapient\Adapter\Generic * * This was copied from Slim Framework's implementation. * * Slim Framework (https://slimframework.com) * * @link https://github.com/slimphp/Slim * @copyright Copyright (c) 2011-2017 Josh Lockhart * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) */ class Stream implements StreamInterface { /** * Bit mask to determine if the stream is a pipe * * This is octal as per header stat.h */ const FSTAT_MODE_S_IFIFO = 0010000; /** * Resource modes * * @var array * @link http://php.net/manual/function.fopen.php */ protected static $modes = [ 'readable' => ['r', 'r+', 'w+', 'a+', 'x+', 'c+'], 'writable' => ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'], ]; /** * The underlying stream resource * * @var resource|null */ protected $stream; /** * Stream metadata * * @var array|null */ protected $meta; /** * Is this stream readable? * * @var bool|null */ protected $readable; /** * Is this stream writable? * * @var bool|null */ protected $writable; /** * Is this stream seekable? * * @var bool|null */ protected $seekable; /** * The size of the stream if known * * @var null|int */ protected $size; /** * Is this stream a pipe? * * @var bool|null */ protected $isPipe; /** * Create a new Stream. * * @param resource $stream A PHP resource handle. * * @throws \InvalidArgumentException If argument is not a resource. */ public function __construct($stream) { $this->stream = null; $this->attach($stream); } /** * Get stream metadata as an associative array or retrieve a specific key. * * The keys returned are identical to the keys returned from PHP's * stream_get_meta_data() function. * * @link http://php.net/manual/en/function.stream-get-meta-data.php * * @param string $key Specific metadata to retrieve. * * @return array|mixed|null Returns an associative array if no key is * provided. Returns a specific key value if a key is provided and the * value is found, or null if the key is not found. */ public function getMetadata($key = null) { if (!\is_resource($this->stream)) { return null; } $this->meta = \stream_get_meta_data($this->stream); if (\is_null($key) === true) { return $this->meta; } return isset($this->meta[$key]) ? $this->meta[$key] : null; } /** * Is a resource attached to this stream? * * Note: This method is not part of the PSR-7 standard. * * @return bool */ protected function isAttached() { return is_resource($this->stream); } /** * Attach new resource to this object. * * Note: This method is not part of the PSR-7 standard. * * @param resource $newStream A PHP resource handle. * @return void * * @throws \InvalidArgumentException If argument is not a valid PHP resource. */ protected function attach($newStream) { if (\is_resource($newStream) === false) { throw new \InvalidArgumentException(__METHOD__ . ' argument must be a valid PHP resource'); } if ($this->isAttached() === true) { $this->detach(); } $this->stream = $newStream; } /** * Separates any underlying resources from the stream. * * After the stream has been detached, the stream is in an unusable state. * * @return resource|null Underlying PHP stream, if any */ public function detach() { if (!\is_resource($this->stream)) { return null; } $oldResource = $this->stream; $this->stream = null; $this->meta = null; $this->readable = null; $this->writable = null; $this->seekable = null; $this->size = null; $this->isPipe = null; return $oldResource; } /** * Reads all data from the stream into a string, from the beginning to end. * * This method MUST attempt to seek to the beginning of the stream before * reading data and read the stream until the end is reached. * * Warning: This could attempt to load a large amount of data into memory. * * This method MUST NOT raise an exception in order to conform with PHP's * string casting operations. * * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring * @return string */ public function __toString() { if (!$this->isAttached()) { return ''; } try { $this->rewind(); return $this->getContents(); } catch (\RuntimeException $e) { return ''; } } /** * Closes the stream and any underlying resources. * @throws \TypeError * @return void */ public function close() { if (!\is_resource($this->stream)) { throw new \TypeError(); } if ($this->isAttached() === true) { if ($this->isPipe()) { \pclose($this->stream); } else { \fclose($this->stream); } } $this->detach(); } /** * Get the size of the stream if known. * * @return int|null Returns the size in bytes if known, or null if unknown. * @throws \TypeError */ public function getSize() { if (!$this->size && $this->isAttached() === true) { if (!\is_resource($this->stream)) { throw new \TypeError(); } $stats = \fstat($this->stream); $this->size = isset($stats['size']) && !$this->isPipe() ? $stats['size'] : null; } return $this->size; } /** * Returns the current position of the file read/write pointer * * @return int Position of the file pointer * * @throws \RuntimeException on error. * @throws \TypeError */ public function tell() { if (!\is_resource($this->stream)) { throw new \TypeError(); } if (!$this->isAttached() || ($position = \ftell($this->stream)) === false || $this->isPipe()) { throw new \RuntimeException('Could not get the position of the pointer in stream'); } if (!isset($position)) { $position = 0; } return $position; } /** * Returns true if the stream is at the end of the stream. * * @return bool * @throws \TypeError */ public function eof() { if (!\is_resource($this->stream)) { throw new \TypeError(); } return $this->isAttached() ? \feof($this->stream) : true; } /** * Returns whether or not the stream is readable. * * @return bool */ public function isReadable() { if ($this->readable === null) { if ($this->isPipe()) { $this->readable = true; } else { $this->readable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); foreach (self::$modes['readable'] as $mode) { if (strpos($meta['mode'], $mode) === 0) { $this->readable = true; break; } } } } } return $this->readable; } /** * Returns whether or not the stream is writable. * * @return bool */ public function isWritable() { if ($this->writable === null) { $this->writable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); foreach (self::$modes['writable'] as $mode) { if (\strpos($meta['mode'], $mode) === 0) { $this->writable = true; break; } } } } return $this->writable; } /** * Returns whether or not the stream is seekable. * * @return bool */ public function isSeekable() { if ($this->seekable === null) { $this->seekable = false; if ($this->isAttached()) { $meta = $this->getMetadata(); $this->seekable = !$this->isPipe() && $meta['seekable']; } } return $this->seekable; } /** * Seek to a position in the stream. * * @link http://www.php.net/manual/en/function.fseek.php * * @param int $offset Stream offset * @param int $whence Specifies how the cursor position will be calculated * based on the seek offset. Valid values are identical to the built-in * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to * offset bytes SEEK_CUR: Set position to current location plus offset * SEEK_END: Set position to end-of-stream plus offset. * @return void * * @throws \RuntimeException on failure. * @throws \TypeError */ public function seek($offset, $whence = SEEK_SET) { // Note that fseek returns 0 on success! if (!\is_resource($this->stream)) { throw new \TypeError(); } if (!$this->isSeekable() || \fseek($this->stream, $offset, $whence) === -1) { throw new \RuntimeException('Could not seek in stream'); } } /** * Seek to the beginning of the stream. * * If the stream is not seekable, this method will raise an exception; * otherwise, it will perform a seek(0). * * @see seek() * * @link http://www.php.net/manual/en/function.fseek.php * * @return void * @throws \RuntimeException on failure. * @throws \TypeError */ public function rewind() { if (!\is_resource($this->stream)) { throw new \TypeError(); } if (!$this->isSeekable() || \rewind($this->stream) === false) { throw new \RuntimeException('Could not rewind stream'); } } /** * Read data from the stream. * * @param int $length Read up to $length bytes from the object and return * them. Fewer than $length bytes may be returned if underlying stream * call returns fewer bytes. * * @return string Returns the data read from the stream, or an empty string * if no bytes are available. * * @throws \RuntimeException if an error occurs. * @throws \TypeError */ public function read($length) { if (!\is_resource($this->stream)) { throw new \TypeError(); } if (!$this->isReadable() || ($data = \fread($this->stream, $length)) === false) { throw new \RuntimeException('Could not read from stream'); } if (!isset($data)) { $data = ''; } return $data; } /** * Write data to the stream. * * @param string $string The string that is to be written. * * @return int Returns the number of bytes written to the stream. * * @throws \RuntimeException on failure. * @throws \TypeError */ public function write($string) { if (!\is_resource($this->stream)) { throw new \TypeError(); } if (!$this->isWritable() || ($written = \fwrite($this->stream, $string)) === false) { throw new \RuntimeException('Could not write to stream'); } if (!isset($written)) { $written = 0; } // reset size so that it will be recalculated on next call to getSize() $this->size = null; return $written; } /** * Returns the remaining contents in a string * * @return string * * @throws \RuntimeException if unable to read or an error occurs while * reading. * @throws \TypeError */ public function getContents() { if (!\is_resource($this->stream)) { throw new \TypeError(); } if (!$this->isReadable() || ($contents = \stream_get_contents($this->stream)) === false) { throw new \RuntimeException('Could not get contents of stream'); } if (!isset($contents)) { $contents = ''; } return $contents; } /** * Returns whether or not the stream is a pipe. * * @return bool * @throws \TypeError */ public function isPipe() { if ($this->isPipe === null) { $this->isPipe = false; if ($this->isAttached()) { if (!\is_resource($this->stream)) { throw new \TypeError(); } $mode = \fstat($this->stream)['mode']; $this->isPipe = ($mode & self::FSTAT_MODE_S_IFIFO) !== 0; } } return $this->isPipe; } /** * Create a Stream object from a string. * * @param string $input * @return StreamInterface * @throws \Error */ public static function fromString(string $input): StreamInterface { /** @var resource $stream */ $stream = \fopen('php://temp', 'w+'); if (!\is_resource($stream)) { throw new \Error('Could not create stream'); } \fwrite($stream, $input); \rewind($stream); return new static($stream); } }