File indexing completed on 2025-01-26 05:29:13
0001 <?php 0002 namespace GuzzleHttp\Psr7; 0003 0004 use Psr\Http\Message\StreamInterface; 0005 0006 /** 0007 * PHP stream implementation. 0008 * 0009 * @var $stream 0010 */ 0011 class Stream implements StreamInterface 0012 { 0013 private $stream; 0014 private $size; 0015 private $seekable; 0016 private $readable; 0017 private $writable; 0018 private $uri; 0019 private $customMetadata; 0020 0021 /** @var array Hash of readable and writable stream types */ 0022 private static $readWriteHash = [ 0023 'read' => [ 0024 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, 0025 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 0026 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, 0027 'x+t' => true, 'c+t' => true, 'a+' => true 0028 ], 0029 'write' => [ 0030 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 0031 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, 0032 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, 0033 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true 0034 ] 0035 ]; 0036 0037 /** 0038 * This constructor accepts an associative array of options. 0039 * 0040 * - size: (int) If a read stream would otherwise have an indeterminate 0041 * size, but the size is known due to foreknowledge, then you can 0042 * provide that size, in bytes. 0043 * - metadata: (array) Any additional metadata to return when the metadata 0044 * of the stream is accessed. 0045 * 0046 * @param resource $stream Stream resource to wrap. 0047 * @param array $options Associative array of options. 0048 * 0049 * @throws \InvalidArgumentException if the stream is not a stream resource 0050 */ 0051 public function __construct($stream, $options = []) 0052 { 0053 if (!is_resource($stream)) { 0054 throw new \InvalidArgumentException('Stream must be a resource'); 0055 } 0056 0057 if (isset($options['size'])) { 0058 $this->size = $options['size']; 0059 } 0060 0061 $this->customMetadata = isset($options['metadata']) 0062 ? $options['metadata'] 0063 : []; 0064 0065 $this->stream = $stream; 0066 $meta = stream_get_meta_data($this->stream); 0067 $this->seekable = $meta['seekable']; 0068 $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); 0069 $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); 0070 $this->uri = $this->getMetadata('uri'); 0071 } 0072 0073 public function __get($name) 0074 { 0075 if ($name == 'stream') { 0076 throw new \RuntimeException('The stream is detached'); 0077 } 0078 0079 throw new \BadMethodCallException('No value for ' . $name); 0080 } 0081 0082 /** 0083 * Closes the stream when the destructed 0084 */ 0085 public function __destruct() 0086 { 0087 $this->close(); 0088 } 0089 0090 public function __toString() 0091 { 0092 try { 0093 $this->seek(0); 0094 return (string) stream_get_contents($this->stream); 0095 } catch (\Exception $e) { 0096 return ''; 0097 } 0098 } 0099 0100 public function getContents() 0101 { 0102 $contents = stream_get_contents($this->stream); 0103 0104 if ($contents === false) { 0105 throw new \RuntimeException('Unable to read stream contents'); 0106 } 0107 0108 return $contents; 0109 } 0110 0111 public function close() 0112 { 0113 if (isset($this->stream)) { 0114 if (is_resource($this->stream)) { 0115 fclose($this->stream); 0116 } 0117 $this->detach(); 0118 } 0119 } 0120 0121 public function detach() 0122 { 0123 if (!isset($this->stream)) { 0124 return null; 0125 } 0126 0127 $result = $this->stream; 0128 unset($this->stream); 0129 $this->size = $this->uri = null; 0130 $this->readable = $this->writable = $this->seekable = false; 0131 0132 return $result; 0133 } 0134 0135 public function getSize() 0136 { 0137 if ($this->size !== null) { 0138 return $this->size; 0139 } 0140 0141 if (!isset($this->stream)) { 0142 return null; 0143 } 0144 0145 // Clear the stat cache if the stream has a URI 0146 if ($this->uri) { 0147 clearstatcache(true, $this->uri); 0148 } 0149 0150 $stats = fstat($this->stream); 0151 if (isset($stats['size'])) { 0152 $this->size = $stats['size']; 0153 return $this->size; 0154 } 0155 0156 return null; 0157 } 0158 0159 public function isReadable() 0160 { 0161 return $this->readable; 0162 } 0163 0164 public function isWritable() 0165 { 0166 return $this->writable; 0167 } 0168 0169 public function isSeekable() 0170 { 0171 return $this->seekable; 0172 } 0173 0174 public function eof() 0175 { 0176 return !$this->stream || feof($this->stream); 0177 } 0178 0179 public function tell() 0180 { 0181 $result = ftell($this->stream); 0182 0183 if ($result === false) { 0184 throw new \RuntimeException('Unable to determine stream position'); 0185 } 0186 0187 return $result; 0188 } 0189 0190 public function rewind() 0191 { 0192 $this->seek(0); 0193 } 0194 0195 public function seek($offset, $whence = SEEK_SET) 0196 { 0197 if (!$this->seekable) { 0198 throw new \RuntimeException('Stream is not seekable'); 0199 } elseif (fseek($this->stream, $offset, $whence) === -1) { 0200 throw new \RuntimeException('Unable to seek to stream position ' 0201 . $offset . ' with whence ' . var_export($whence, true)); 0202 } 0203 } 0204 0205 public function read($length) 0206 { 0207 if (!$this->readable) { 0208 throw new \RuntimeException('Cannot read from non-readable stream'); 0209 } 0210 if ($length < 0) { 0211 throw new \RuntimeException('Length parameter cannot be negative'); 0212 } 0213 0214 if (0 === $length) { 0215 return ''; 0216 } 0217 0218 $string = fread($this->stream, $length); 0219 if (false === $string) { 0220 throw new \RuntimeException('Unable to read from stream'); 0221 } 0222 0223 return $string; 0224 } 0225 0226 public function write($string) 0227 { 0228 if (!$this->writable) { 0229 throw new \RuntimeException('Cannot write to a non-writable stream'); 0230 } 0231 0232 // We can't know the size after writing anything 0233 $this->size = null; 0234 $result = fwrite($this->stream, $string); 0235 0236 if ($result === false) { 0237 throw new \RuntimeException('Unable to write to stream'); 0238 } 0239 0240 return $result; 0241 } 0242 0243 public function getMetadata($key = null) 0244 { 0245 if (!isset($this->stream)) { 0246 return $key ? null : []; 0247 } elseif (!$key) { 0248 return $this->customMetadata + stream_get_meta_data($this->stream); 0249 } elseif (isset($this->customMetadata[$key])) { 0250 return $this->customMetadata[$key]; 0251 } 0252 0253 $meta = stream_get_meta_data($this->stream); 0254 0255 return isset($meta[$key]) ? $meta[$key] : null; 0256 } 0257 }