Swift_ByteStream_FileByteStream::_copyReadStream()   C
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 7
nop 0
dl 0
loc 32
ccs 0
cts 19
cp 0
crap 56
rs 6.7272
c 0
b 0
f 0
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 30
    public function __construct($path, $writable = false)
76
    {
77 30
        if (empty($path)) {
78 1
            throw new Swift_IoException('The path cannot be empty');
79
        }
80
81 29
        if (strpos($path, '?') !== false) {
82
            $parameter = parse_url($path, PHP_URL_QUERY);
83
84
            $this->_path = str_replace('?' . $parameter, '', $path);
85
        } else {
86 29
            $this->_path = $path;
87
        }
88
        
89 29
        $this->_mode = $writable ? 'w+b' : 'rb';
90
91
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
92
        if (
93 29
            function_exists('get_magic_quotes_runtime')
94 29
            &&
95 29
            @get_magic_quotes_runtime() === 1
96 29
        ) {
97
            $this->_quotes = true;
98
        }
99 29
    }
100
101
    /**
102
     * Get the complete path to the file.
103
     *
104
     * @return string
105
     */
106 18
    public function getPath()
107
    {
108 18
        return $this->_path;
109
    }
110
111
    /**
112
     * Reads $length bytes from the stream into a string and moves the pointer
113
     * through the stream by $length.
114
     *
115
     * If less bytes exist than are requested the
116
     * remaining bytes are given instead. If no bytes are remaining at all, boolean
117
     * false is returned.
118
     *
119
     * @param int $length
120
     *
121
     * @throws Swift_IoException
122
     *
123
     * @return string|bool
124
     */
125 22
    public function read($length)
126
    {
127 22
        $fp = $this->_getReadHandle();
128 21
        if ($fp && !feof($fp)) {
129
130 21
            if ($this->_quotes) {
131
                /** @noinspection DeprecatedIniOptionsInspection */
132
                ini_set('magic_quotes_runtime', 0);
133
            }
134
135 21
            $bytes = fread($fp, $length);
136
137 21
            if ($this->_quotes) {
138
                /** @noinspection DeprecatedIniOptionsInspection */
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 1
                $this->_resetReadHandle();
148
149 1
                return false;
150
            }
151
152 21
            return $bytes;
153
        }
154
155 18
        $this->_resetReadHandle();
156
157 18
        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 8
            $this->_seekReadStreamToPosition($byteOffset);
171 8
        }
172 16
        $this->_offset = $byteOffset;
173 16
    }
174
175
    /**
176
     * Just write the bytes to the file
177
     *
178
     * @param string $bytes
179
     *
180
     * @throws Swift_IoException
181
     */
182 16
    protected function _commit($bytes)
183
    {
184 16
        fwrite($this->_getWriteHandle(), $bytes);
185 16
        $this->_resetReadHandle();
186 16
    }
187
188
    /** Not used */
189 11
    protected function _flush()
190
    {
191 11
    }
192
193
    /**
194
     * Get the resource for reading
195
     *
196
     * @return resource
197
     * @throws Swift_IoException
198
     */
199 22
    private function _getReadHandle()
200
    {
201 22
        if (!isset($this->_reader)) {
202
203
            $opts = array(
204
                'ssl' => array(
205 22
                    'verify_peer' => false,
206 22
                    'verify_peer_name' => false,
207 22
                    'allow_self_signed' => true,
208 22
                ),
209 22
            );
210
211
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
212 22
            $pointer = @fopen($this->_path, 'rb', false, stream_context_create($opts));
213
214 22
            if (!$pointer) {
215 1
                throw new Swift_IoException('Unable to open file for reading [' . $this->_path . ']');
216
            }
217
218 21
            $this->_reader = $pointer;
219
220 21
            if ($this->_offset != 0) {
221 1
                $this->_getReadStreamSeekableStatus();
222 1
                $this->_seekReadStreamToPosition($this->_offset);
223 1
            }
224 21
        }
225
226 21
        return $this->_reader;
227
    }
228
229
    /** Get the resource for writing */
230 16
    private function _getWriteHandle()
231
    {
232 16
        if (!isset($this->_writer)) {
233
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
234 16
            $pointer = @fopen($this->_path, $this->_mode);
235
236 16
            if (!$pointer) {
237
                throw new Swift_IoException('Unable to open file for writing [' . $this->_path . ']');
238
            }
239
240 16
            $this->_writer = $pointer;
241 16
        }
242
243 16
        return $this->_writer;
244
    }
245
246
    /** Force a reload of the resource for reading */
247 25
    private function _resetReadHandle()
248
    {
249 25
        if (isset($this->_reader)) {
250 20
            fclose($this->_reader);
251 20
            $this->_reader = null;
252 20
        }
253 25
    }
254
255
    /** Check if ReadOnly Stream is seekable */
256 9
    private function _getReadStreamSeekableStatus()
257
    {
258 9
        $metas = stream_get_meta_data($this->_reader);
259 9
        $this->_seekable = $metas['seekable'];
260 9
    }
261
262
    /**
263
     * Streams in a readOnly stream ensuring copy if needed
264
     *
265
     * @param $offset
266
     *
267
     * @throws Swift_IoException
268
     */
269 9
    private function _seekReadStreamToPosition($offset)
270
    {
271 9
        if ($this->_seekable === null) {
272 8
            $this->_getReadStreamSeekableStatus();
273 8
        }
274 9
        if ($this->_seekable === false) {
275
            $currentPos = ftell($this->_reader);
276
            if ($currentPos < $offset) {
277
                $toDiscard = $offset - $currentPos;
278
                fread($this->_reader, $toDiscard);
279
280
                return;
281
            }
282
            $this->_copyReadStream();
283
        }
284 9
        fseek($this->_reader, $offset, SEEK_SET);
285 9
    }
286
287
    /** Copy a readOnly Stream to ensure seekability */
288
    private function _copyReadStream()
289
    {
290
        $tmpFile = fopen('php://temp/maxmemory:4096', 'w+b');
291
292
        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...
293
            /* We have opened a php:// Stream Should work without problem */
294
        } 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...
295
            /* We have opened a tmpfile */
296
        } else {
297
            throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
298
        }
299
300
        $currentPos = ftell($this->_reader);
301
302
        fclose($this->_reader);
303
304
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
305
        $source = @fopen($this->_path, 'rb');
306
307
        if (!$source) {
308
            throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']');
309
        }
310
311
        fseek($tmpFile, 0, SEEK_SET);
312
        while (!feof($source)) {
313
            fwrite($tmpFile, fread($source, 4096));
314
        }
315
        fseek($tmpFile, $currentPos, SEEK_SET);
316
        fclose($source);
317
318
        $this->_reader = $tmpFile;
319
    }
320
}
321