1<?php 2 3declare(strict_types=1); 4 5namespace GuzzleHttp\Psr7; 6 7use Psr\Http\Message\StreamInterface; 8 9/** 10 * Stream decorator trait 11 * 12 * @property StreamInterface $stream 13 */ 14trait StreamDecoratorTrait 15{ 16 /** 17 * @param StreamInterface $stream Stream to decorate 18 */ 19 public function __construct(StreamInterface $stream) 20 { 21 $this->stream = $stream; 22 } 23 24 /** 25 * Magic method used to create a new stream if streams are not added in 26 * the constructor of a decorator (e.g., LazyOpenStream). 27 * 28 * @return StreamInterface 29 */ 30 public function __get(string $name) 31 { 32 if ($name === 'stream') { 33 $this->stream = $this->createStream(); 34 35 return $this->stream; 36 } 37 38 throw new \UnexpectedValueException("$name not found on class"); 39 } 40 41 public function __toString(): string 42 { 43 try { 44 if ($this->isSeekable()) { 45 $this->seek(0); 46 } 47 48 return $this->getContents(); 49 } catch (\Throwable $e) { 50 if (\PHP_VERSION_ID >= 70400) { 51 throw $e; 52 } 53 trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR); 54 55 return ''; 56 } 57 } 58 59 public function getContents(): string 60 { 61 return Utils::copyToString($this); 62 } 63 64 /** 65 * Allow decorators to implement custom methods 66 * 67 * @return mixed 68 */ 69 public function __call(string $method, array $args) 70 { 71 /** @var callable $callable */ 72 $callable = [$this->stream, $method]; 73 $result = ($callable)(...$args); 74 75 // Always return the wrapped object if the result is a return $this 76 return $result === $this->stream ? $this : $result; 77 } 78 79 public function close(): void 80 { 81 $this->stream->close(); 82 } 83 84 /** 85 * @return mixed 86 */ 87 public function getMetadata($key = null) 88 { 89 return $this->stream->getMetadata($key); 90 } 91 92 public function detach() 93 { 94 return $this->stream->detach(); 95 } 96 97 public function getSize(): ?int 98 { 99 return $this->stream->getSize(); 100 } 101 102 public function eof(): bool 103 { 104 return $this->stream->eof(); 105 } 106 107 public function tell(): int 108 { 109 return $this->stream->tell(); 110 } 111 112 public function isReadable(): bool 113 { 114 return $this->stream->isReadable(); 115 } 116 117 public function isWritable(): bool 118 { 119 return $this->stream->isWritable(); 120 } 121 122 public function isSeekable(): bool 123 { 124 return $this->stream->isSeekable(); 125 } 126 127 public function rewind(): void 128 { 129 $this->seek(0); 130 } 131 132 public function seek($offset, $whence = SEEK_SET): void 133 { 134 $this->stream->seek($offset, $whence); 135 } 136 137 public function read($length): string 138 { 139 return $this->stream->read($length); 140 } 141 142 public function write($string): int 143 { 144 return $this->stream->write($string); 145 } 146 147 /** 148 * Implement in subclasses to dynamically create streams when requested. 149 * 150 * @throws \BadMethodCallException 151 */ 152 protected function createStream(): StreamInterface 153 { 154 throw new \BadMethodCallException('Not implemented'); 155 } 156} 157