1 | <?php |
||||||||
2 | |||||||||
3 | namespace PhpZip\IO\Stream; |
||||||||
4 | |||||||||
5 | use PhpZip\Exception\ZipException; |
||||||||
6 | use PhpZip\Model\ZipEntry; |
||||||||
7 | |||||||||
8 | /** |
||||||||
9 | * The class provides stream reuse functionality. |
||||||||
10 | * |
||||||||
11 | * Stream will not be closed at {@see fclose}. |
||||||||
12 | * |
||||||||
13 | * @see https://www.php.net/streamwrapper |
||||||||
14 | */ |
||||||||
15 | final class ZipEntryStreamWrapper |
||||||||
16 | { |
||||||||
17 | /** @var string the registered protocol */ |
||||||||
18 | const PROTOCOL = 'zipentry'; |
||||||||
19 | |||||||||
20 | /** @var resource */ |
||||||||
21 | public $context; |
||||||||
22 | |||||||||
23 | /** @var resource */ |
||||||||
24 | private $fp; |
||||||||
25 | |||||||||
26 | /** |
||||||||
27 | * @return bool |
||||||||
28 | */ |
||||||||
29 | 8 | public static function register() |
|||||||
30 | { |
||||||||
31 | 8 | $protocol = self::PROTOCOL; |
|||||||
32 | |||||||||
33 | 8 | if (!\in_array($protocol, stream_get_wrappers(), true)) { |
|||||||
34 | 1 | if (!stream_wrapper_register($protocol, self::class)) { |
|||||||
35 | throw new \RuntimeException("Failed to register '{$protocol}://' protocol"); |
||||||||
36 | } |
||||||||
37 | |||||||||
38 | 1 | return true; |
|||||||
39 | } |
||||||||
40 | |||||||||
41 | 8 | return false; |
|||||||
42 | } |
||||||||
43 | |||||||||
44 | public static function unregister() |
||||||||
45 | { |
||||||||
46 | stream_wrapper_unregister(self::PROTOCOL); |
||||||||
47 | } |
||||||||
48 | |||||||||
49 | /** |
||||||||
50 | * @param ZipEntry $entry |
||||||||
51 | * |
||||||||
52 | * @return resource |
||||||||
53 | */ |
||||||||
54 | 8 | public static function wrap(ZipEntry $entry) |
|||||||
55 | { |
||||||||
56 | 8 | self::register(); |
|||||||
57 | |||||||||
58 | 8 | $context = stream_context_create( |
|||||||
59 | [ |
||||||||
60 | 8 | self::PROTOCOL => [ |
|||||||
61 | 8 | 'entry' => $entry, |
|||||||
62 | ], |
||||||||
63 | ] |
||||||||
64 | ); |
||||||||
65 | |||||||||
66 | 8 | $uri = self::PROTOCOL . '://' . $entry->getName(); |
|||||||
67 | 8 | $fp = fopen($uri, 'r+b', false, $context); |
|||||||
68 | |||||||||
69 | 8 | if ($fp === false) { |
|||||||
70 | throw new \RuntimeException('Error open ' . $uri); |
||||||||
71 | } |
||||||||
72 | |||||||||
73 | 8 | return $fp; |
|||||||
74 | } |
||||||||
75 | |||||||||
76 | /** |
||||||||
77 | * Opens file or URL. |
||||||||
78 | * |
||||||||
79 | * This method is called immediately after the wrapper is |
||||||||
80 | * initialized (f.e. by {@see fopen()} and {@see file_get_contents()}). |
||||||||
81 | * |
||||||||
82 | * @param string $path specifies the URL that was passed to |
||||||||
83 | * the original function |
||||||||
84 | * @param string $mode the mode used to open the file, as detailed |
||||||||
85 | * for {@see fopen()} |
||||||||
86 | * @param int $options Holds additional flags set by the streams |
||||||||
87 | * API. It can hold one or more of the |
||||||||
88 | * following values OR'd together. |
||||||||
89 | * @param string $opened_path if the path is opened successfully, and |
||||||||
90 | * STREAM_USE_PATH is set in options, |
||||||||
91 | * opened_path should be set to the |
||||||||
92 | * full path of the file/resource that |
||||||||
93 | * was actually opened |
||||||||
94 | * |
||||||||
95 | * @throws ZipException |
||||||||
96 | * |
||||||||
97 | * @return bool |
||||||||
98 | * |
||||||||
99 | * @see https://www.php.net/streamwrapper.stream-open |
||||||||
100 | */ |
||||||||
101 | 8 | public function stream_open($path, $mode, $options, &$opened_path) |
|||||||
0 ignored issues
–
show
The parameter
$opened_path is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$options is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() The parameter
$path is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
102 | { |
||||||||
103 | 8 | if ($this->context === null) { |
|||||||
104 | throw new \RuntimeException('stream context is null'); |
||||||||
105 | } |
||||||||
106 | 8 | $streamOptions = stream_context_get_options($this->context); |
|||||||
107 | |||||||||
108 | 8 | if (!isset($streamOptions[self::PROTOCOL]['entry'])) { |
|||||||
109 | throw new \RuntimeException('no stream option ["' . self::PROTOCOL . '"]["entry"]'); |
||||||||
110 | } |
||||||||
111 | 8 | $zipEntry = $streamOptions[self::PROTOCOL]['entry']; |
|||||||
112 | |||||||||
113 | 8 | if (!$zipEntry instanceof ZipEntry) { |
|||||||
114 | throw new \RuntimeException('invalid stream context'); |
||||||||
115 | } |
||||||||
116 | |||||||||
117 | 8 | $zipData = $zipEntry->getData(); |
|||||||
118 | |||||||||
119 | 8 | if ($zipData === null) { |
|||||||
120 | throw new ZipException(sprintf('No data for zip entry "%s"', $zipEntry->getName())); |
||||||||
121 | } |
||||||||
122 | 8 | $this->fp = $zipData->getDataAsStream(); |
|||||||
123 | |||||||||
124 | 8 | return $this->fp !== false; |
|||||||
125 | } |
||||||||
126 | |||||||||
127 | /** |
||||||||
128 | * Read from stream. |
||||||||
129 | * |
||||||||
130 | * This method is called in response to {@see fread()} and {@see fgets()}. |
||||||||
131 | * |
||||||||
132 | * Note: Remember to update the read/write position of the stream |
||||||||
133 | * (by the number of bytes that were successfully read). |
||||||||
134 | * |
||||||||
135 | * @param int $count how many bytes of data from the current |
||||||||
136 | * position should be returned |
||||||||
137 | * |
||||||||
138 | * @return false|string If there are less than count bytes available, |
||||||||
139 | * return as many as are available. If no more data |
||||||||
140 | * is available, return either FALSE or |
||||||||
141 | * an empty string. |
||||||||
142 | * |
||||||||
143 | * @see https://www.php.net/streamwrapper.stream-read |
||||||||
144 | */ |
||||||||
145 | 8 | public function stream_read($count) |
|||||||
146 | { |
||||||||
147 | 8 | return fread($this->fp, $count); |
|||||||
148 | } |
||||||||
149 | |||||||||
150 | /** |
||||||||
151 | * Seeks to specific location in a stream. |
||||||||
152 | * |
||||||||
153 | * This method is called in response to {@see fseek()}. |
||||||||
154 | * The read/write position of the stream should be updated according |
||||||||
155 | * to the offset and whence. |
||||||||
156 | * |
||||||||
157 | * @param int $offset the stream offset to seek to |
||||||||
158 | * @param int $whence Possible values: |
||||||||
159 | * {@see \SEEK_SET} - Set position equal to offset bytes. |
||||||||
160 | * {@see \SEEK_CUR} - Set position to current location plus offset. |
||||||||
161 | * {@see \SEEK_END} - Set position to end-of-file plus offset. |
||||||||
162 | * |
||||||||
163 | * @return bool return TRUE if the position was updated, FALSE otherwise |
||||||||
164 | * |
||||||||
165 | * @see https://www.php.net/streamwrapper.stream-seek |
||||||||
166 | */ |
||||||||
167 | 8 | public function stream_seek($offset, $whence = \SEEK_SET) |
|||||||
168 | { |
||||||||
169 | 8 | return fseek($this->fp, $offset, $whence) === 0; |
|||||||
170 | } |
||||||||
171 | |||||||||
172 | /** |
||||||||
173 | * Retrieve the current position of a stream. |
||||||||
174 | * |
||||||||
175 | * This method is called in response to {@see fseek()} to determine |
||||||||
176 | * the current position. |
||||||||
177 | * |
||||||||
178 | * @return int should return the current position of the stream |
||||||||
179 | * |
||||||||
180 | * @see https://www.php.net/streamwrapper.stream-tell |
||||||||
181 | */ |
||||||||
182 | 8 | public function stream_tell() |
|||||||
183 | { |
||||||||
184 | 8 | $pos = ftell($this->fp); |
|||||||
185 | |||||||||
186 | 8 | if ($pos === false) { |
|||||||
187 | throw new \RuntimeException('Cannot get stream position.'); |
||||||||
188 | } |
||||||||
189 | |||||||||
190 | 8 | return $pos; |
|||||||
191 | } |
||||||||
192 | |||||||||
193 | /** |
||||||||
194 | * Tests for end-of-file on a file pointer. |
||||||||
195 | * |
||||||||
196 | * This method is called in response to {@see feof()}. |
||||||||
197 | * |
||||||||
198 | * @return bool should return TRUE if the read/write position is at |
||||||||
199 | * the end of the stream and if no more data is available |
||||||||
200 | * to be read, or FALSE otherwise |
||||||||
201 | * |
||||||||
202 | * @see https://www.php.net/streamwrapper.stream-eof |
||||||||
203 | */ |
||||||||
204 | 8 | public function stream_eof() |
|||||||
205 | { |
||||||||
206 | 8 | return feof($this->fp); |
|||||||
207 | } |
||||||||
208 | |||||||||
209 | /** |
||||||||
210 | * Retrieve information about a file resource. |
||||||||
211 | * |
||||||||
212 | * This method is called in response to {@see fstat()}. |
||||||||
213 | * |
||||||||
214 | * @return array |
||||||||
215 | * |
||||||||
216 | * @see https://www.php.net/streamwrapper.stream-stat |
||||||||
217 | * @see https://www.php.net/stat |
||||||||
218 | * @see https://www.php.net/fstat |
||||||||
219 | */ |
||||||||
220 | 8 | public function stream_stat() |
|||||||
221 | { |
||||||||
222 | 8 | return fstat($this->fp); |
|||||||
223 | } |
||||||||
224 | |||||||||
225 | /** |
||||||||
226 | * Flushes the output. |
||||||||
227 | * |
||||||||
228 | * This method is called in response to {@see fflush()} and when the |
||||||||
229 | * stream is being closed while any unflushed data has been written to |
||||||||
230 | * it before. |
||||||||
231 | * If you have cached data in your stream but not yet stored it into |
||||||||
232 | * the underlying storage, you should do so now. |
||||||||
233 | * |
||||||||
234 | * @return bool should return TRUE if the cached data was successfully |
||||||||
235 | * stored (or if there was no data to store), or FALSE |
||||||||
236 | * if the data could not be stored |
||||||||
237 | * |
||||||||
238 | * @see https://www.php.net/streamwrapper.stream-flush |
||||||||
239 | */ |
||||||||
240 | public function stream_flush() |
||||||||
241 | { |
||||||||
242 | return fflush($this->fp); |
||||||||
243 | } |
||||||||
244 | |||||||||
245 | /** |
||||||||
246 | * Truncate stream. |
||||||||
247 | * |
||||||||
248 | * Will respond to truncation, e.g., through {@see ftruncate()}. |
||||||||
249 | * |
||||||||
250 | * @param int $new_size the new size |
||||||||
251 | * |
||||||||
252 | * @return bool returns TRUE on success or FALSE on failure |
||||||||
253 | * |
||||||||
254 | * @see https://www.php.net/streamwrapper.stream-truncate |
||||||||
255 | */ |
||||||||
256 | public function stream_truncate($new_size) |
||||||||
257 | { |
||||||||
258 | return ftruncate($this->fp, (int) $new_size); |
||||||||
259 | } |
||||||||
260 | |||||||||
261 | /** |
||||||||
262 | * Write to stream. |
||||||||
263 | * |
||||||||
264 | * This method is called in response to {@see fwrite().} |
||||||||
265 | * |
||||||||
266 | * Note: Remember to update the current position of the stream by |
||||||||
267 | * number of bytes that were successfully written. |
||||||||
268 | * |
||||||||
269 | * @param string $data should be stored into the underlying stream |
||||||||
270 | * |
||||||||
271 | * @return int should return the number of bytes that were successfully stored, or 0 if none could be stored |
||||||||
272 | * |
||||||||
273 | * @see https://www.php.net/streamwrapper.stream-write |
||||||||
274 | */ |
||||||||
275 | public function stream_write($data) |
||||||||
276 | { |
||||||||
277 | $bytes = fwrite($this->fp, $data); |
||||||||
278 | |||||||||
279 | return $bytes === false ? 0 : $bytes; |
||||||||
280 | } |
||||||||
281 | |||||||||
282 | /** |
||||||||
283 | * Retrieve the underlaying resource. |
||||||||
284 | * |
||||||||
285 | * This method is called in response to {@see stream_select()}. |
||||||||
286 | * |
||||||||
287 | * @param int $cast_as can be {@see STREAM_CAST_FOR_SELECT} when {@see stream_select()} |
||||||||
288 | * is callingstream_cast() or {@see STREAM_CAST_AS_STREAM} when |
||||||||
289 | * stream_cast() is called for other uses |
||||||||
290 | * |
||||||||
291 | * @return resource |
||||||||
292 | */ |
||||||||
293 | public function stream_cast($cast_as) |
||||||||
0 ignored issues
–
show
The parameter
$cast_as is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||||
294 | { |
||||||||
295 | return $this->fp; |
||||||||
296 | } |
||||||||
297 | |||||||||
298 | /** |
||||||||
299 | * Close a resource. |
||||||||
300 | * |
||||||||
301 | * This method is called in response to {@see fclose()}. |
||||||||
302 | * All resources that were locked, or allocated, by the wrapper should be released. |
||||||||
303 | * |
||||||||
304 | * @see https://www.php.net/streamwrapper.stream-close |
||||||||
305 | */ |
||||||||
306 | 8 | public function stream_close() |
|||||||
307 | { |
||||||||
308 | 8 | } |
|||||||
309 | } |
||||||||
310 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.