123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- <?php
- declare(strict_types=1);
- namespace GuzzleHttp\Psr7;
- use Psr\Http\Message\StreamInterface;
- /**
- * Compose stream implementations based on a hash of functions.
- *
- * Allows for easy testing and extension of a provided stream without needing
- * to create a concrete class for a simple extension point.
- */
- #[\AllowDynamicProperties]
- final class FnStream implements StreamInterface
- {
- private const SLOTS = [
- '__toString', 'close', 'detach', 'rewind',
- 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
- 'isReadable', 'read', 'getContents', 'getMetadata',
- ];
- /** @var array<string, callable> */
- private $methods;
- /**
- * @param array<string, callable> $methods Hash of method name to a callable.
- */
- public function __construct(array $methods)
- {
- $this->methods = $methods;
- // Create the functions on the class
- foreach ($methods as $name => $fn) {
- $this->{'_fn_'.$name} = $fn;
- }
- }
- /**
- * Lazily determine which methods are not implemented.
- *
- * @throws \BadMethodCallException
- */
- public function __get(string $name): void
- {
- throw new \BadMethodCallException(str_replace('_fn_', '', $name)
- .'() is not implemented in the FnStream');
- }
- /**
- * The close method is called on the underlying stream only if possible.
- */
- public function __destruct()
- {
- if (isset($this->_fn_close)) {
- ($this->_fn_close)();
- }
- }
- /**
- * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
- *
- * @throws \LogicException
- */
- public function __wakeup(): void
- {
- throw new \LogicException('FnStream should never be unserialized');
- }
- /**
- * Adds custom functionality to an underlying stream by intercepting
- * specific method calls.
- *
- * @param StreamInterface $stream Stream to decorate
- * @param array<string, callable> $methods Hash of method name to a closure
- *
- * @return FnStream
- */
- public static function decorate(StreamInterface $stream, array $methods)
- {
- // If any of the required methods were not provided, then simply
- // proxy to the decorated stream.
- foreach (array_diff(self::SLOTS, array_keys($methods)) as $diff) {
- /** @var callable $callable */
- $callable = [$stream, $diff];
- $methods[$diff] = $callable;
- }
- return new self($methods);
- }
- public function __toString(): string
- {
- try {
- /** @var string */
- return ($this->_fn___toString)();
- } catch (\Throwable $e) {
- if (\PHP_VERSION_ID >= 70400) {
- throw $e;
- }
- trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
- return '';
- }
- }
- public function close(): void
- {
- ($this->_fn_close)();
- }
- public function detach()
- {
- return ($this->_fn_detach)();
- }
- public function getSize(): ?int
- {
- return ($this->_fn_getSize)();
- }
- public function tell(): int
- {
- return ($this->_fn_tell)();
- }
- public function eof(): bool
- {
- return ($this->_fn_eof)();
- }
- public function isSeekable(): bool
- {
- return ($this->_fn_isSeekable)();
- }
- public function rewind(): void
- {
- ($this->_fn_rewind)();
- }
- public function seek($offset, $whence = SEEK_SET): void
- {
- ($this->_fn_seek)($offset, $whence);
- }
- public function isWritable(): bool
- {
- return ($this->_fn_isWritable)();
- }
- public function write($string): int
- {
- return ($this->_fn_write)($string);
- }
- public function isReadable(): bool
- {
- return ($this->_fn_isReadable)();
- }
- public function read($length): string
- {
- return ($this->_fn_read)($length);
- }
- public function getContents(): string
- {
- return ($this->_fn_getContents)();
- }
- /**
- * @return mixed
- */
- public function getMetadata($key = null)
- {
- return ($this->_fn_getMetadata)($key);
- }
- }
|