ZipEntryStreamWrapper   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 293
Duplicated Lines 0 %

Test Coverage

Coverage 66.67%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 46
dl 0
loc 293
ccs 38
cts 57
cp 0.6667
rs 10
c 1
b 0
f 1
wmc 23

14 Methods

Rating   Name   Duplication   Size   Complexity  
A stream_stat() 0 3 1
A stream_truncate() 0 3 1
A stream_flush() 0 3 1
A stream_read() 0 3 1
A register() 0 13 3
A stream_seek() 0 3 1
A stream_close() 0 2 1
A unregister() 0 3 1
A stream_open() 0 24 5
A wrap() 0 20 2
A stream_cast() 0 3 1
A stream_eof() 0 3 1
A stream_tell() 0 9 2
A stream_write() 0 5 2
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
Unused Code introduced by
The parameter $mode 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 ignore-unused  annotation

101
    public function stream_open($path, /** @scrutinizer ignore-unused */ $mode, $options, &$opened_path)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
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 ignore-unused  annotation

101
    public function stream_open($path, $mode, $options, /** @scrutinizer ignore-unused */ &$opened_path)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
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 ignore-unused  annotation

101
    public function stream_open($path, $mode, /** @scrutinizer ignore-unused */ $options, &$opened_path)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
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 ignore-unused  annotation

101
    public function stream_open(/** @scrutinizer ignore-unused */ $path, $mode, $options, &$opened_path)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
Unused Code introduced by
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 ignore-unused  annotation

293
    public function stream_cast(/** @scrutinizer ignore-unused */ $cast_as)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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