Completed
Push — 5.x ( bfcbfa...e82a2b )
by Lars
05:00
created

_resetReadHandle()   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
Metric Value
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4286
cc 2
eloc 4
nc 2
nop 0
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 2
            throw new Swift_IoException('The path cannot be empty');
79
        }
80
81 27
        $this->_path = $path;
82 27
        $this->_mode = $writable ? 'w+b' : 'rb';
83
84 27
        if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
85
            $this->_quotes = true;
86
        }
87 27
    }
88
89
    /**
90
     * Get the complete path to the file.
91
     *
92
     * @return string
93
     */
94 18
    public function getPath()
95
    {
96 18
        return $this->_path;
97
    }
98
99
    /**
100
     * Reads $length bytes from the stream into a string and moves the pointer
101
     * through the stream by $length.
102
     *
103
     * If less bytes exist than are requested the
104
     * remaining bytes are given instead. If no bytes are remaining at all, boolean
105
     * false is returned.
106
     *
107
     * @param int $length
108
     *
109
     * @throws Swift_IoException
110
     *
111
     * @return string|bool
112
     */
113 21
    public function read($length)
114
    {
115 21
        $fp = $this->_getReadHandle();
116 21
        if ($fp && !feof($fp)) {
117
118 21
            if ($this->_quotes) {
119
                ini_set('magic_quotes_runtime', 0);
120
            }
121
122
            // checking for errors v1
123 21
            if ($this->_offset != 0 && ord(fread($fp, 1)) != 0) {
124 12
                $this->_resetReadHandle();
125
126 12
                return false;
127
            }
128
129 21
            $bytes = fread($fp, $length);
130
131
            // checking for errors v2
132 21
            if ($bytes === false) {
133
                $this->_resetReadHandle();
134
135
                return false;
136
            }
137
138 21
            if ($this->_quotes) {
139
                ini_set('magic_quotes_runtime', 1);
140
            }
141
142 21
            $this->_offset = ftell($fp);
143
144
            // If we read one byte after reaching the end of the file
145
            // feof() will return false and an empty string is returned.
146 21
            if ($bytes === '' && feof($fp)) {
147
                $this->_resetReadHandle();
148
149
                return false;
150
            }
151
152 21
            return $bytes;
153
        }
154
155 16
        $this->_resetReadHandle();
156
157 16
        return false;
158
    }
159
160
    /**
161
     * Move the internal read pointer to $byteOffset in the stream.
162
     *
163
     * @param int $byteOffset
164
     *
165
     * @return bool
166
     */
167 16
    public function setReadPointer($byteOffset)
168
    {
169 16
        if (isset($this->_reader)) {
170
            $this->_seekReadStreamToPosition($byteOffset);
171
        }
172 16
        $this->_offset = $byteOffset;
173 16
    }
174
175
    /** Just write the bytes to the file */
176 15
    protected function _commit($bytes)
177
    {
178 15
        fwrite($this->_getWriteHandle(), $bytes);
179 15
        $this->_resetReadHandle();
180 15
    }
181
182
    /** Not used */
183 10
    protected function _flush()
184
    {
185 10
    }
186
187
    /** Get the resource for reading */
188 21
    private function _getReadHandle()
189
    {
190 21
        if (!isset($this->_reader)) {
191 21
            $pointer = fopen($this->_path, 'rb');
192
193 21
            if (!$pointer) {
194
                throw new Swift_IoException(
195
                    'Unable to open file for reading [' . $this->_path . ']'
196
                );
197
            }
198
199 21
            $this->_reader = $pointer;
200
201 21
            if ($this->_offset != 0) {
202 1
                $this->_getReadStreamSeekableStatus();
203 1
                $this->_seekReadStreamToPosition($this->_offset);
204
            }
205
        }
206
207 21
        return $this->_reader;
208
    }
209
210
    /** Get the resource for writing */
211 15
    private function _getWriteHandle()
212
    {
213 15
        if (!isset($this->_writer)) {
214 15
            $pointer = fopen($this->_path, $this->_mode);
215
216 15
            if (!$pointer) {
217
                throw new Swift_IoException(
218
                    'Unable to open file for writing [' . $this->_path . ']'
219
                );
220
            }
221
222 15
            $this->_writer = $pointer;
223
        }
224
225 15
        return $this->_writer;
226
    }
227
228
    /** Force a reload of the resource for reading */
229 24
    private function _resetReadHandle()
230
    {
231 24
        if (isset($this->_reader)) {
232 20
            fclose($this->_reader);
233 20
            $this->_reader = null;
234
        }
235 24
    }
236
237
    /** Check if ReadOnly Stream is seekable */
238 1
    private function _getReadStreamSeekableStatus()
239
    {
240 1
        $metas = stream_get_meta_data($this->_reader);
241 1
        $this->_seekable = $metas['seekable'];
242 1
    }
243
244
    /**
245
     * Streams in a readOnly stream ensuring copy if needed
246
     *
247
     * @param $offset
248
     *
249
     * @throws Swift_IoException
250
     */
251 1
    private function _seekReadStreamToPosition($offset)
252
    {
253 1
        if ($this->_seekable === null) {
254
            $this->_getReadStreamSeekableStatus();
255
        }
256 1
        if ($this->_seekable === false) {
257
            $currentPos = ftell($this->_reader);
258
            if ($currentPos < $offset) {
259
                $toDiscard = $offset - $currentPos;
260
                fread($this->_reader, $toDiscard);
261
262
                return;
263
            }
264
            $this->_copyReadStream();
265
        }
266 1
        fseek($this->_reader, $offset, SEEK_SET);
267 1
    }
268
269
    /** Copy a readOnly Stream to ensure seekability */
270
    private function _copyReadStream()
271
    {
272
        $tmpFile = fopen('php://temp/maxmemory:4096', 'w+b');
273
        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...
274
            /* We have opened a php:// Stream Should work without problem */
275
        } 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...
276
            /* We have opened a tmpfile */
277
        } else {
278
            throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
279
        }
280
281
        $currentPos = ftell($this->_reader);
282
283
        fclose($this->_reader);
284
285
        $source = fopen($this->_path, 'rb');
286
        if (!$source) {
287
            throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']');
288
        }
289
290
        fseek($tmpFile, 0, SEEK_SET);
291
        while (!feof($source)) {
292
            fwrite($tmpFile, fread($source, 4096));
293
        }
294
        fseek($tmpFile, $currentPos, SEEK_SET);
295
        fclose($source);
296
297
        $this->_reader = $tmpFile;
298
    }
299
}
300