Completed
Push — 5.x ( ac0e87...2b0825 )
by Lars
05:07
created

Swift_ByteStream_FileByteStream::setReadPointer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 7
rs 9.4285
c 1
b 0
f 1
ccs 5
cts 5
cp 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of SwiftMailer.
5
 * (c) 2004-2009 Chris Corbyn
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
/**
12
 * Allows reading and writing of bytes to and from a file.
13
 *
14
 * @author Chris Corbyn
15
 */
16
class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_FileStream
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * The internal pointer offset
20
     *
21
     * @var int
22
     */
23
    private $_offset = 0;
24
25
    /**
26
     * The path to the file
27
     *
28
     * @var string
29
     */
30
    private $_path;
31
32
    /**
33
     * The mode this file is opened in for writing
34
     *
35
     * @var string
36
     */
37
    private $_mode;
38
39
    /**
40
     * A lazy-loaded resource handle for reading the file
41
     *
42
     * @var resource
43
     */
44
    private $_reader;
45
46
    /**
47
     * A lazy-loaded resource handle for writing the file
48
     *
49
     * @var resource
50
     */
51
    private $_writer;
52
53
    /**
54
     * If magic_quotes_runtime is on, this will be true
55
     *
56
     * @var bool
57
     */
58
    private $_quotes = false;
59
60
    /**
61
     * If stream is seekable true/false, or null if not known
62
     *
63
     * @var null|boolean
64
     */
65
    private $_seekable = null;
66
67
    /**
68
     * Create a new FileByteStream for $path.
69
     *
70
     * @param string     $path
71
     * @param bool|false $writable
72
     *
73
     * @throws Swift_IoException
74
     */
75 29
    public function __construct($path, $writable = false)
76
    {
77 29
        if (empty($path)) {
78 1
            throw new Swift_IoException('The path cannot be empty');
79
        }
80
81 28
        if (strpos($path, '?') !== false) {
82
            $parameter = parse_url($path, PHP_URL_QUERY);
83
84
            $this->_path = str_replace('?' . $parameter, '', $path);
85
        } else {
86 28
            $this->_path = $path;
87
        }
88
        
89 28
        $this->_mode = $writable ? 'w+b' : 'rb';
90
91 28
        if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() === 1) {
92
            $this->_quotes = true;
93
        }
94 28
    }
95
96
    /**
97
     * Get the complete path to the file.
98
     *
99
     * @return string
100
     */
101 18
    public function getPath()
102
    {
103 18
        return $this->_path;
104
    }
105
106
    /**
107
     * Reads $length bytes from the stream into a string and moves the pointer
108
     * through the stream by $length.
109
     *
110
     * If less bytes exist than are requested the
111
     * remaining bytes are given instead. If no bytes are remaining at all, boolean
112
     * false is returned.
113
     *
114
     * @param int $length
115
     *
116
     * @throws Swift_IoException
117
     *
118
     * @return string|bool
119
     */
120 22
    public function read($length)
121
    {
122 22
        $fp = $this->_getReadHandle();
123 21
        if ($fp && !feof($fp)) {
124
125 21
            if ($this->_quotes) {
126
                /** @noinspection DeprecatedIniOptionsInspection */
127
                ini_set('magic_quotes_runtime', 0);
128
            }
129
130 21
            $bytes = fread($fp, $length);
131
132 21
            if ($this->_quotes) {
133
                /** @noinspection DeprecatedIniOptionsInspection */
134
                ini_set('magic_quotes_runtime', 1);
135
            }
136
137 21
            $this->_offset = ftell($fp);
138
139
            // If we read one byte after reaching the end of the file
140
            // feof() will return false and an empty string is returned.
141 21
            if ($bytes === '' && feof($fp)) {
142 1
                $this->_resetReadHandle();
143
144 1
                return false;
145
            }
146
147 21
            return $bytes;
148
        }
149
150 18
        $this->_resetReadHandle();
151
152 18
        return false;
153
    }
154
155
    /**
156
     * Move the internal read pointer to $byteOffset in the stream.
157
     *
158
     * @param int $byteOffset
159
     *
160
     * @return bool
161
     */
162 16
    public function setReadPointer($byteOffset)
163
    {
164 16
        if (isset($this->_reader)) {
165 8
            $this->_seekReadStreamToPosition($byteOffset);
166
        }
167 16
        $this->_offset = $byteOffset;
168 16
    }
169
170
    /**
171
     * Just write the bytes to the file
172
     *
173
     * @param string $bytes
174
     *
175
     * @throws Swift_IoException
176
     */
177 15
    protected function _commit($bytes)
178
    {
179 15
        fwrite($this->_getWriteHandle(), $bytes);
180 15
        $this->_resetReadHandle();
181 15
    }
182
183
    /** Not used */
184 10
    protected function _flush()
185
    {
186 10
    }
187
188
    /**
189
     * Get the resource for reading
190
     *
191
     * @return resource
192
     * @throws Swift_IoException
193
     */
194 22
    private function _getReadHandle()
195
    {
196 22
        if (!isset($this->_reader)) {
197 22
            $pointer = @fopen($this->_path, 'rb');
198
199 22
            if (!$pointer) {
200 1
                throw new Swift_IoException(
201 1
                    'Unable to open file for reading [' . $this->_path . ']'
202
                );
203
            }
204
205 21
            $this->_reader = $pointer;
206
207 21
            if ($this->_offset != 0) {
208 1
                $this->_getReadStreamSeekableStatus();
209 1
                $this->_seekReadStreamToPosition($this->_offset);
210
            }
211
        }
212
213 21
        return $this->_reader;
214
    }
215
216
    /** Get the resource for writing */
217 15
    private function _getWriteHandle()
218
    {
219 15
        if (!isset($this->_writer)) {
220 15
            $pointer = @fopen($this->_path, $this->_mode);
221
222 15
            if (!$pointer) {
223
                throw new Swift_IoException(
224
                    'Unable to open file for writing [' . $this->_path . ']'
225
                );
226
            }
227
228 15
            $this->_writer = $pointer;
229
        }
230
231 15
        return $this->_writer;
232
    }
233
234
    /** Force a reload of the resource for reading */
235 24
    private function _resetReadHandle()
236
    {
237 24
        if (isset($this->_reader)) {
238 20
            fclose($this->_reader);
239 20
            $this->_reader = null;
240
        }
241 24
    }
242
243
    /** Check if ReadOnly Stream is seekable */
244 9
    private function _getReadStreamSeekableStatus()
245
    {
246 9
        $metas = stream_get_meta_data($this->_reader);
247 9
        $this->_seekable = $metas['seekable'];
248 9
    }
249
250
    /**
251
     * Streams in a readOnly stream ensuring copy if needed
252
     *
253
     * @param $offset
254
     *
255
     * @throws Swift_IoException
256
     */
257 9
    private function _seekReadStreamToPosition($offset)
258
    {
259 9
        if ($this->_seekable === null) {
260 8
            $this->_getReadStreamSeekableStatus();
261
        }
262 9
        if ($this->_seekable === false) {
263
            $currentPos = ftell($this->_reader);
264
            if ($currentPos < $offset) {
265
                $toDiscard = $offset - $currentPos;
266
                fread($this->_reader, $toDiscard);
267
268
                return;
269
            }
270
            $this->_copyReadStream();
271
        }
272 9
        fseek($this->_reader, $offset, SEEK_SET);
273 9
    }
274
275
    /** Copy a readOnly Stream to ensure seekability */
276
    private function _copyReadStream()
277
    {
278
        $tmpFile = fopen('php://temp/maxmemory:4096', 'w+b');
279
280
        if ($tmpFile) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
281
            /* We have opened a php:// Stream Should work without problem */
282
        } elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
283
            /* We have opened a tmpfile */
284
        } else {
285
            throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
286
        }
287
288
        $currentPos = ftell($this->_reader);
289
290
        fclose($this->_reader);
291
292
        $source = @fopen($this->_path, 'rb');
293
        if (!$source) {
294
            throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']');
295
        }
296
297
        fseek($tmpFile, 0, SEEK_SET);
298
        while (!feof($source)) {
299
            fwrite($tmpFile, fread($source, 4096));
300
        }
301
        fseek($tmpFile, $currentPos, SEEK_SET);
302
        fclose($source);
303
304
        $this->_reader = $tmpFile;
305
    }
306
}
307