1 <?php 2 3 declare(strict_types=1); 4 5 namespace GuzzleHttp\Psr7; 6 7 use Psr\Http\Message\StreamInterface; 8 9 /** 10 * Compose stream implementations based on a hash of functions. 11 * 12 * Allows for easy testing and extension of a provided stream without needing 13 * to create a concrete class for a simple extension point. 14 */ 15 #[\AllowDynamicProperties] 16 final class FnStream implements StreamInterface 17 { 18 private const SLOTS = [ 19 '__toString', 'close', 'detach', 'rewind', 20 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write', 21 'isReadable', 'read', 'getContents', 'getMetadata' 22 ]; 23 24 /** @var array<string, callable> */ 25 private $methods; 26 27 /** 28 * @param array<string, callable> $methods Hash of method name to a callable. 29 */ 30 public function __construct(array $methods) 31 { 32 $this->methods = $methods; 33 34 // Create the functions on the class 35 foreach ($methods as $name => $fn) { 36 $this->{'_fn_' . $name} = $fn; 37 } 38 } 39 40 /** 41 * Lazily determine which methods are not implemented. 42 * 43 * @throws \BadMethodCallException 44 */ 45 public function __get(string $name): void 46 { 47 throw new \BadMethodCallException(str_replace('_fn_', '', $name) 48 . '() is not implemented in the FnStream'); 49 } 50 51 /** 52 * The close method is called on the underlying stream only if possible. 53 */ 54 public function __destruct() 55 { 56 if (isset($this->_fn_close)) { 57 call_user_func($this->_fn_close); 58 } 59 } 60 61 /** 62 * An unserialize would allow the __destruct to run when the unserialized value goes out of scope. 63 * 64 * @throws \LogicException 65 */ 66 public function __wakeup(): void 67 { 68 throw new \LogicException('FnStream should never be unserialized'); 69 } 70 71 /** 72 * Adds custom functionality to an underlying stream by intercepting 73 * specific method calls. 74 * 75 * @param StreamInterface $stream Stream to decorate 76 * @param array<string, callable> $methods Hash of method name to a closure 77 * 78 * @return FnStream 79 */ 80 public static function decorate(StreamInterface $stream, array $methods) 81 { 82 // If any of the required methods were not provided, then simply 83 // proxy to the decorated stream. 84 foreach (array_diff(self::SLOTS, array_keys($methods)) as $diff) { 85 /** @var callable $callable */ 86 $callable = [$stream, $diff]; 87 $methods[$diff] = $callable; 88 } 89 90 return new self($methods); 91 } 92 93 public function __toString(): string 94 { 95 try { 96 return call_user_func($this->_fn___toString); 97 } catch (\Throwable $e) { 98 if (\PHP_VERSION_ID >= 70400) { 99 throw $e; 100 } 101 trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); 102 return ''; 103 } 104 } 105 106 public function close(): void 107 { 108 call_user_func($this->_fn_close); 109 } 110 111 public function detach() 112 { 113 return call_user_func($this->_fn_detach); 114 } 115 116 public function getSize(): ?int 117 { 118 return call_user_func($this->_fn_getSize); 119 } 120 121 public function tell(): int 122 { 123 return call_user_func($this->_fn_tell); 124 } 125 126 public function eof(): bool 127 { 128 return call_user_func($this->_fn_eof); 129 } 130 131 public function isSeekable(): bool 132 { 133 return call_user_func($this->_fn_isSeekable); 134 } 135 136 public function rewind(): void 137 { 138 call_user_func($this->_fn_rewind); 139 } 140 141 public function seek($offset, $whence = SEEK_SET): void 142 { 143 call_user_func($this->_fn_seek, $offset, $whence); 144 } 145 146 public function isWritable(): bool 147 { 148 return call_user_func($this->_fn_isWritable); 149 } 150 151 public function write($string): int 152 { 153 return call_user_func($this->_fn_write, $string); 154 } 155 156 public function isReadable(): bool 157 { 158 return call_user_func($this->_fn_isReadable); 159 } 160 161 public function read($length): string 162 { 163 return call_user_func($this->_fn_read, $length); 164 } 165 166 public function getContents(): string 167 { 168 return call_user_func($this->_fn_getContents); 169 } 170 171 /** 172 * {@inheritdoc} 173 * 174 * @return mixed 175 */ 176 public function getMetadata($key = null) 177 { 178 return call_user_func($this->_fn_getMetadata, $key); 179 } 180 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body