3232class SeekableHttpStream implements File {
3333 private const PROTOCOL = 'httpseek ' ;
3434
35- private static $ registered = false ;
35+ private static bool $ registered = false ;
3636
3737 /**
3838 * Registers the stream wrapper using the `httpseek://` url scheme
@@ -73,24 +73,26 @@ public static function open(callable $callback) {
7373 /** @var callable */
7474 private $ openCallback ;
7575
76- /** @var resource */
76+ /** @var ?resource|closed- resource */
7777 private $ current ;
78- /** @var int */
79- private $ offset = 0 ;
80- /** @var int */
81- private $ length = 0 ;
78+ private int $ offset = 0 ;
79+ private int $ length = 0 ;
80+ private bool $ needReconnect = false ;
8281
83- private function reconnect (int $ start ) {
82+ private function reconnect (int $ start ): bool {
83+ $ this ->needReconnect = false ;
8484 $ range = $ start . '- ' ;
85- if ($ this ->current != null ) {
85+ if ($ this ->hasOpenStream () ) {
8686 fclose ($ this ->current );
8787 }
8888
89- $ this -> current = ($ this ->openCallback )($ range );
89+ $ stream = ($ this ->openCallback )($ range );
9090
9191 if ($ this ->current === false ) {
92+ $ this ->current = null ;
9293 return false ;
9394 }
95+ $ this ->current = $ stream ;
9496
9597 $ responseHead = stream_get_meta_data ($ this ->current )['wrapper_data ' ];
9698
@@ -109,6 +111,7 @@ private function reconnect(int $start) {
109111 return preg_match ('#^content-range:#i ' , $ v ) === 1 ;
110112 }));
111113 if (!$ rangeHeaders ) {
114+ $ this ->current = null ;
112115 return false ;
113116 }
114117 $ contentRange = $ rangeHeaders [0 ];
@@ -119,6 +122,7 @@ private function reconnect(int $start) {
119122 $ length = intval (explode ('/ ' , $ range )[1 ]);
120123
121124 if ($ begin !== $ start ) {
125+ $ this ->current = null ;
122126 return false ;
123127 }
124128
@@ -128,6 +132,28 @@ private function reconnect(int $start) {
128132 return true ;
129133 }
130134
135+ /**
136+ * @return ?resource
137+ */
138+ private function getCurrent () {
139+ if ($ this ->needReconnect ) {
140+ $ this ->reconnect ($ this ->offset );
141+ }
142+ if (is_resource ($ this ->current )) {
143+ return $ this ->current ;
144+ } else {
145+ return null ;
146+ }
147+ }
148+
149+ /**
150+ * @return bool
151+ * @psalm-assert-if-true resource $this->current
152+ */
153+ private function hasOpenStream (): bool {
154+ return is_resource ($ this ->current );
155+ }
156+
131157 public function stream_open ($ path , $ mode , $ options , &$ opened_path ) {
132158 $ options = stream_context_get_options ($ this ->context )[self ::PROTOCOL ];
133159 $ this ->openCallback = $ options ['callback ' ];
@@ -136,10 +162,10 @@ public function stream_open($path, $mode, $options, &$opened_path) {
136162 }
137163
138164 public function stream_read ($ count ) {
139- if (!$ this ->current ) {
165+ if (!$ this ->getCurrent () ) {
140166 return false ;
141167 }
142- $ ret = fread ($ this ->current , $ count );
168+ $ ret = fread ($ this ->getCurrent () , $ count );
143169 $ this ->offset += strlen ($ ret );
144170 return $ ret ;
145171 }
@@ -149,48 +175,61 @@ public function stream_seek($offset, $whence = SEEK_SET) {
149175 case SEEK_SET :
150176 if ($ offset === $ this ->offset ) {
151177 return true ;
178+ } else {
179+ $ this ->offset = $ offset ;
152180 }
153- return $ this -> reconnect ( $ offset ) ;
181+ break ;
154182 case SEEK_CUR :
155183 if ($ offset === 0 ) {
156184 return true ;
185+ } else {
186+ $ this ->offset += $ offset ;
157187 }
158- return $ this -> reconnect ( $ this -> offset + $ offset ) ;
188+ break ;
159189 case SEEK_END :
160190 if ($ this ->length === 0 ) {
161191 return false ;
162192 } elseif ($ this ->length + $ offset === $ this ->offset ) {
163193 return true ;
194+ } else {
195+ $ this ->offset = $ this ->length + $ offset ;
164196 }
165- return $ this -> reconnect ( $ this -> length + $ offset ) ;
197+ break ;
166198 }
167- return false ;
199+
200+ if ($ this ->hasOpenStream ()) {
201+ fclose ($ this ->current );
202+ }
203+ $ this ->current = null ;
204+ $ this ->needReconnect = true ;
205+ return true ;
168206 }
169207
170208 public function stream_tell () {
171209 return $ this ->offset ;
172210 }
173211
174212 public function stream_stat () {
175- if (is_resource ( $ this ->current )) {
176- return fstat ($ this ->current );
213+ if ($ this ->getCurrent ( )) {
214+ return fstat ($ this ->getCurrent () );
177215 } else {
178216 return false ;
179217 }
180218 }
181219
182220 public function stream_eof () {
183- if (is_resource ( $ this ->current )) {
184- return feof ($ this ->current );
221+ if ($ this ->getCurrent ( )) {
222+ return feof ($ this ->getCurrent () );
185223 } else {
186224 return true ;
187225 }
188226 }
189227
190228 public function stream_close () {
191- if (is_resource ( $ this ->current )) {
229+ if ($ this ->hasOpenStream ( )) {
192230 fclose ($ this ->current );
193231 }
232+ $ this ->current = null ;
194233 }
195234
196235 public function stream_write ($ data ) {
0 commit comments