PluginProbe
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress / 11.0.5
AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress v11.0.5
11.1.1 11.1.0 11.0.5 11.0.4 11.0.3 11.0.2 11.0.1 11.0.0 10.11.1 10.11.0 10.10.2 10.10.1 10.10.0 10.9.1 trunk 10.0.0 10.0.1 10.1.0 10.1.1 10.1.2 10.1.3 10.1.4 10.2.0 10.2.1 10.2.2 All 62 releases
acymailing / vendor / zbateson / stream-decorators / src / SeekingLimitStream.php

SeekingLimitStream.php in AcyMailing – An Ultimate Newsletter Plugin and Marketing Automation Solution for WordPress 11.0.5, at vendor/zbateson/stream-decorators/src/SeekingLimitStream.php

196 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This file is part of the ZBateson\StreamDecorators project.
4 *
5 * @license http://opensource.org/licenses/bsd-license.php BSD
6 */
7
8 namespace ZBateson\StreamDecorators;
9
10 use GuzzleHttp\Psr7\StreamDecoratorTrait;
11 use Psr\Http\Message\StreamInterface;
12
13 /**
14 * Maintains an internal 'read' position, and seeks to it before reading, then
15 * seeks back to the original position of the underlying stream after reading if
16 * the attached stream supports seeking.
17 *
18 * Although based on LimitStream, it's not inherited from it since $offset and
19 * $limit are set to private on LimitStream, and most other functions are re-
20 * implemented anyway. This also decouples the implementation from upstream
21 * changes.
22 *
23 * @author Zaahid Bateson
24 */
25 class SeekingLimitStream implements StreamInterface
26 {
27 use StreamDecoratorTrait;
28
29 /** @var int Offset to start reading from */
30 private $offset;
31
32 /** @var int Limit the number of bytes that can be read */
33 private $limit;
34
35 /**
36 * @var int Number of bytes written, and importantly, if non-zero, writes a
37 * final $lineEnding on close (and so maintained instead of using
38 * tell() directly)
39 */
40 private $position = 0;
41
42 /**
43 * @var StreamInterface $stream
44 */
45 private $stream;
46
47 /**
48 * @param StreamInterface $stream Stream to wrap
49 * @param int $limit Total number of bytes to allow to be read
50 * from the stream. Pass -1 for no limit.
51 * @param int $offset Position to seek to before reading (only
52 * works on seekable streams).
53 */
54 public function __construct(StreamInterface $stream, int $limit = -1, int $offset = 0)
55 {
56 $this->stream = $stream;
57 $this->setLimit($limit);
58 $this->setOffset($offset);
59 }
60
61 /**
62 * Returns the current relative read position of this stream subset.
63 */
64 public function tell() : int
65 {
66 return $this->position;
67 }
68
69 /**
70 * Returns the size of the limited subset of data, or null if the wrapped
71 * stream returns null for getSize.
72 */
73 public function getSize() : ?int
74 {
75 $size = $this->stream->getSize();
76 if ($size === null) {
77 // this shouldn't happen on a seekable stream I don't think...
78 $pos = $this->stream->tell();
79 $this->stream->seek(0, SEEK_END);
80 $size = $this->stream->tell();
81 $this->stream->seek($pos);
82 }
83 if ($this->limit === -1) {
84 return $size - $this->offset;
85 }
86
87 return \min([$this->limit, $size - $this->offset]);
88 }
89
90 /**
91 * Returns true if the current read position is at the end of the limited
92 * stream
93 */
94 public function eof() : bool
95 {
96 $size = $this->limit;
97 if ($size === -1) {
98 $size = $this->getSize();
99 }
100 return ($this->position >= $size);
101 }
102
103 /**
104 * Ensures the seek position specified is within the stream's bounds, and
105 * sets the internal position pointer (doesn't actually seek).
106 */
107 private function doSeek(int $pos) : void
108 {
109 if ($this->limit !== -1) {
110 $pos = \min([$pos, $this->limit]);
111 }
112 $this->position = \max([0, $pos]);
113 }
114
115 /**
116 * Seeks to the passed position within the confines of the limited stream's
117 * bounds.
118 *
119 * For SeekingLimitStream, no actual seek is performed on the underlying
120 * wrapped stream. Instead, an internal pointer is set, and the stream is
121 * 'seeked' on read operations
122 *
123 * @param int $offset
124 * @param int $whence
125 */
126 public function seek($offset, $whence = SEEK_SET) : void
127 {
128 $pos = $offset;
129 switch ($whence) {
130 case SEEK_CUR:
131 $pos = $this->position + $offset;
132 break;
133 case SEEK_END:
134 $pos = $this->limit + $offset;
135 break;
136 default:
137 break;
138 }
139 $this->doSeek($pos);
140 }
141
142 /**
143 * Sets the offset to start reading from the wrapped stream.
144 */
145 public function setOffset(int $offset) : void
146 {
147 $this->offset = $offset;
148 $this->position = 0;
149 }
150
151 /**
152 * Sets the length of the stream to the passed $limit.
153 */
154 public function setLimit(int $limit) : void
155 {
156 $this->limit = $limit;
157 }
158
159 /**
160 * Seeks to the current position and reads up to $length bytes, or less if
161 * it would result in reading past $this->limit
162 */
163 public function seekAndRead(int $length) : string
164 {
165 $this->stream->seek($this->offset + $this->position);
166 if ($this->limit !== -1) {
167 $length = \min($length, $this->limit - $this->position);
168 if ($length <= 0) {
169 return '';
170 }
171 }
172 return $this->stream->read($length);
173 }
174
175 /**
176 * Reads from the underlying stream after seeking to the position within the
177 * bounds set for this limited stream. After reading, the wrapped stream is
178 * 'seeked' back to its position prior to the call to read().
179 *
180 * @param int $length
181 * @return string
182 */
183 public function read($length) : string
184 {
185 $pos = $this->stream->tell();
186 $ret = $this->seekAndRead($length);
187 $this->position += \strlen($ret);
188 $this->stream->seek($pos);
189 if ($this->limit !== -1 && $this->position > $this->limit) {
190 $ret = \substr($ret, 0, -($this->position - $this->limit));
191 $this->position = $this->limit;
192 }
193 return $ret;
194 }
195 }
196