PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / vendor_prefixed / guzzlehttp / psr7 / src / CachingStream.php

CachingStream.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at vendor_prefixed/guzzlehttp/psr7/src/CachingStream.php

191 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 namespace WCPOS\Vendor\GuzzleHttp\Psr7;
5
6 use WCPOS\Vendor\Psr\Http\Message\StreamInterface;
7 /**
8 * Stream decorator that can cache previously read bytes from a sequentially
9 * read stream.
10 */
11 final class CachingStream implements StreamInterface
12 {
13 use StreamDecoratorTrait;
14 use NonSerializableStreamTrait;
15 /** @var StreamInterface Stream being wrapped */
16 private StreamInterface $remoteStream;
17 /** @var int Number of bytes to skip reading due to a write on the buffer */
18 private int $skipReadBytes = 0;
19 private StreamInterface $stream;
20 private bool $detached = \false;
21 private bool $closed = \false;
22 /**
23 * We will treat the buffer object as the body of the stream
24 *
25 * @param StreamInterface $stream Stream to cache. The cursor is assumed to be at the beginning of the stream.
26 * @param StreamInterface $target Optionally specify where data is cached. Defaults to a "php://temp"
27 * stream. A custom target is used as a random-access byte buffer to
28 * replay the remote stream, so it must be readable, writable, and
29 * seekable, report an accurate position and size, and store writes
30 * losslessly. Lossy or non-seekable streams such as BufferStream and
31 * DroppingStream are not valid targets.
32 */
33 public function __construct(StreamInterface $stream, ?StreamInterface $target = null)
34 {
35 $this->remoteStream = $stream;
36 $this->stream = $target ?: new Stream(Utils::tryFopen('php://temp', 'r+'));
37 }
38 public function getSize() : ?int
39 {
40 if ($this->detached) {
41 return null;
42 }
43 $remoteSize = $this->remoteStream->getSize();
44 if (null === $remoteSize) {
45 return null;
46 }
47 return \max($this->stream->getSize(), $remoteSize);
48 }
49 public function rewind() : void
50 {
51 $this->seek(0);
52 }
53 public function seek(int $offset, int $whence = \SEEK_SET) : void
54 {
55 if ($whence === \SEEK_SET) {
56 $byte = $offset;
57 } elseif ($whence === \SEEK_CUR) {
58 $byte = Integers::addSigned($this->tell(), $offset);
59 } elseif ($whence === \SEEK_END) {
60 $size = $this->remoteStream->getSize();
61 if ($size === null) {
62 // Discovering the size reads the remote stream to EOF and
63 // moves the cursor, so restore the cursor if the computed
64 // target is rejected to keep a failed seek side-effect free.
65 $position = $this->tell();
66 $size = $this->cacheEntireStream();
67 try {
68 $byte = Integers::addSigned($size, $offset);
69 } catch (\Throwable $e) {
70 $this->stream->seek($position);
71 throw $e;
72 }
73 } else {
74 $byte = Integers::addSigned($size, $offset);
75 }
76 } else {
77 throw new \InvalidArgumentException('Invalid whence');
78 }
79 $diff = $byte - $this->stream->getSize();
80 if ($diff > 0) {
81 // Read the remoteStream until we have read in at least the amount
82 // of bytes requested, or we reach the end of the file.
83 while ($diff > 0 && !$this->remoteStream->eof()) {
84 $previousSize = $this->stream->getSize();
85 $previousSkipReadBytes = $this->skipReadBytes;
86 $data = $this->read($diff);
87 $currentSize = $this->stream->getSize();
88 if ($data === '' && $currentSize === $previousSize && $this->skipReadBytes === $previousSkipReadBytes) {
89 break;
90 }
91 $diff = $byte - $currentSize;
92 }
93 } else {
94 // We can just do a normal seek since we've already seen this byte.
95 $this->stream->seek($byte);
96 }
97 }
98 public function read(int $length) : string
99 {
100 if ($length < 0) {
101 throw new \RuntimeException('Length parameter cannot be negative');
102 }
103 // Perform a regular read on any previously read data from the buffer
104 $data = $this->stream->read($length);
105 $remaining = $length - \strlen($data);
106 // More data was requested so read from the remote stream
107 if ($remaining) {
108 // If data was written to the buffer in a position that would have
109 // been filled from the remote stream, then we must skip bytes on
110 // the remote stream to emulate overwriting bytes from that
111 // position. This mimics the behavior of other PHP stream wrappers.
112 $remoteData = StreamTimeout::read($this->remoteStream, Integers::add($remaining, $this->skipReadBytes), 'Unable to read from stream: timed out');
113 if ($this->skipReadBytes) {
114 $len = \strlen($remoteData);
115 $remoteData = \substr($remoteData, $this->skipReadBytes);
116 $this->skipReadBytes = \max(0, $this->skipReadBytes - $len);
117 }
118 $data .= $remoteData;
119 // A short cache write would silently corrupt later replays, so fail loudly.
120 if ($this->stream->write($remoteData) !== \strlen($remoteData)) {
121 throw new \RuntimeException('Unable to cache the entire read from the remote stream');
122 }
123 }
124 return $data;
125 }
126 public function write(string $string) : int
127 {
128 // When appending to the end of the currently read stream, you'll want
129 // to skip bytes from being read from the remote stream to emulate
130 // other stream wrappers. Basically replacing bytes of data of a fixed
131 // length.
132 $overflow = Integers::add(\strlen($string), $this->tell()) - $this->remoteStream->tell();
133 if ($overflow > 0) {
134 $this->skipReadBytes = Integers::add($this->skipReadBytes, $overflow);
135 }
136 return $this->stream->write($string);
137 }
138 public function eof() : bool
139 {
140 return $this->stream->eof() && $this->remoteStream->eof();
141 }
142 public function detach()
143 {
144 if ($this->detached) {
145 return null;
146 }
147 $position = $this->tell();
148 $this->cacheEntireStream();
149 $this->stream->seek($position);
150 $resource = $this->stream->detach();
151 $this->detached = \true;
152 return $resource;
153 }
154 /**
155 * Close the remote stream and any attached cache stream.
156 */
157 public function close() : void
158 {
159 if ($this->closed) {
160 return;
161 }
162 $closeCache = !$this->detached;
163 $this->closed = \true;
164 $this->detached = \true;
165 $exception = null;
166 try {
167 $this->remoteStream->close();
168 } catch (\Throwable $e) {
169 $exception = $e;
170 }
171 if ($closeCache) {
172 try {
173 $this->stream->close();
174 } catch (\Throwable $e) {
175 if ($exception === null) {
176 $exception = $e;
177 }
178 }
179 }
180 if ($exception !== null) {
181 throw $exception;
182 }
183 }
184 private function cacheEntireStream() : int
185 {
186 $target = new FnStream(['write' => 'strlen']);
187 Utils::copyToStream($this, $target);
188 return $this->tell();
189 }
190 }
191