1<?php 2 3declare(strict_types=1); 4 5namespace GuzzleHttp\Psr7; 6 7use Psr\Http\Message\StreamInterface; 8 9/** 10 * Provides a buffer stream that can be written to to fill a buffer, and read 11 * from to remove bytes from the buffer. 12 * 13 * This stream returns a "hwm" metadata value that tells upstream consumers 14 * what the configured high water mark of the stream is, or the maximum 15 * preferred size of the buffer. 16 */ 17final class BufferStream implements StreamInterface 18{ 19 /** @var int */ 20 private $hwm; 21 22 /** @var string */ 23 private $buffer = ''; 24 25 /** 26 * @param int $hwm High water mark, representing the preferred maximum 27 * buffer size. If the size of the buffer exceeds the high 28 * water mark, then calls to write will continue to succeed 29 * but will return 0 to inform writers to slow down 30 * until the buffer has been drained by reading from it. 31 */ 32 public function __construct(int $hwm = 16384) 33 { 34 $this->hwm = $hwm; 35 } 36 37 public function __toString(): string 38 { 39 return $this->getContents(); 40 } 41 42 public function getContents(): string 43 { 44 $buffer = $this->buffer; 45 $this->buffer = ''; 46 47 return $buffer; 48 } 49 50 public function close(): void 51 { 52 $this->buffer = ''; 53 } 54 55 public function detach() 56 { 57 $this->close(); 58 59 return null; 60 } 61 62 public function getSize(): ?int 63 { 64 return strlen($this->buffer); 65 } 66 67 public function isReadable(): bool 68 { 69 return true; 70 } 71 72 public function isWritable(): bool 73 { 74 return true; 75 } 76 77 public function isSeekable(): bool 78 { 79 return false; 80 } 81 82 public function rewind(): void 83 { 84 $this->seek(0); 85 } 86 87 public function seek($offset, $whence = SEEK_SET): void 88 { 89 throw new \RuntimeException('Cannot seek a BufferStream'); 90 } 91 92 public function eof(): bool 93 { 94 return strlen($this->buffer) === 0; 95 } 96 97 public function tell(): int 98 { 99 throw new \RuntimeException('Cannot determine the position of a BufferStream'); 100 } 101 102 /** 103 * Reads data from the buffer. 104 */ 105 public function read($length): string 106 { 107 $currentLength = strlen($this->buffer); 108 109 if ($length >= $currentLength) { 110 // No need to slice the buffer because we don't have enough data. 111 $result = $this->buffer; 112 $this->buffer = ''; 113 } else { 114 // Slice up the result to provide a subset of the buffer. 115 $result = substr($this->buffer, 0, $length); 116 $this->buffer = substr($this->buffer, $length); 117 } 118 119 return $result; 120 } 121 122 /** 123 * Writes data to the buffer. 124 */ 125 public function write($string): int 126 { 127 $this->buffer .= $string; 128 129 if (strlen($this->buffer) >= $this->hwm) { 130 return 0; 131 } 132 133 return strlen($string); 134 } 135 136 /** 137 * @return mixed 138 */ 139 public function getMetadata($key = null) 140 { 141 if ($key === 'hwm') { 142 return $this->hwm; 143 } 144 145 return $key ? null : []; 146 } 147} 148