Completed
Push — 5.x ( e82a2b...3cd537 )
by Lars
04:29
created

Swift_ByteStream_FileByteStream::read()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7.1161
Metric Value
dl 0
loc 32
ccs 13
cts 15
cp 0.8667
rs 6.7273
cc 7
eloc 15
nc 9
nop 1
crap 7.1161
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 21
            $bytes = fread($fp, $length);
123
124 21
            if ($this->_quotes) {
125
                ini_set('magic_quotes_runtime', 1);
126
            }
127
128 21
            $this->_offset = ftell($fp);
129
130
            // If we read one byte after reaching the end of the file
131
            // feof() will return false and an empty string is returned.
132 21
            if ($bytes === '' && feof($fp)) {
133 1
                $this->_resetReadHandle();
134
135 1
                return false;
136
            }
137
138 21
            return $bytes;
139
        }
140
141 18
        $this->_resetReadHandle();
142
143 18
        return false;
144
    }
145
146
    /**
147
     * Move the internal read pointer to $byteOffset in the stream.
148
     *
149
     * @param int $byteOffset
150
     *
151
     * @return bool
152
     */
153 16
    public function setReadPointer($byteOffset)
154
    {
155 16
        if (isset($this->_reader)) {
156 8
            $this->_seekReadStreamToPosition($byteOffset);
157
        }
158 16
        $this->_offset = $byteOffset;
159 16
    }
160
161
    /** Just write the bytes to the file */
162 15
    protected function _commit($bytes)
163
    {
164 15
        fwrite($this->_getWriteHandle(), $bytes);
165 15
        $this->_resetReadHandle();
166 15
    }
167
168
    /** Not used */
169 10
    protected function _flush()
170
    {
171 10
    }
172
173
    /** Get the resource for reading */
174 21
    private function _getReadHandle()
175
    {
176 21
        if (!isset($this->_reader)) {
177 21
            $pointer = fopen($this->_path, 'rb');
178
179 21
            if (!$pointer) {
180
                throw new Swift_IoException(
181
                    'Unable to open file for reading [' . $this->_path . ']'
182
                );
183
            }
184
185 21
            $this->_reader = $pointer;
186
187 21
            if ($this->_offset != 0) {
188 1
                $this->_getReadStreamSeekableStatus();
189 1
                $this->_seekReadStreamToPosition($this->_offset);
190
            }
191
        }
192
193 21
        return $this->_reader;
194
    }
195
196
    /** Get the resource for writing */
197 15
    private function _getWriteHandle()
198
    {
199 15
        if (!isset($this->_writer)) {
200 15
            $pointer = fopen($this->_path, $this->_mode);
201
202 15
            if (!$pointer) {
203
                throw new Swift_IoException(
204
                    'Unable to open file for writing [' . $this->_path . ']'
205
                );
206
            }
207
208 15
            $this->_writer = $pointer;
209
        }
210
211 15
        return $this->_writer;
212
    }
213
214
    /** Force a reload of the resource for reading */
215 24
    private function _resetReadHandle()
216
    {
217 24
        if (isset($this->_reader)) {
218 20
            fclose($this->_reader);
219 20
            $this->_reader = null;
220
        }
221 24
    }
222
223
    /** Check if ReadOnly Stream is seekable */
224 9
    private function _getReadStreamSeekableStatus()
225
    {
226 9
        $metas = stream_get_meta_data($this->_reader);
227 9
        $this->_seekable = $metas['seekable'];
228 9
    }
229
230
    /**
231
     * Streams in a readOnly stream ensuring copy if needed
232
     *
233
     * @param $offset
234
     *
235
     * @throws Swift_IoException
236
     */
237 9
    private function _seekReadStreamToPosition($offset)
238
    {
239 9
        if ($this->_seekable === null) {
240 8
            $this->_getReadStreamSeekableStatus();
241
        }
242 9
        if ($this->_seekable === false) {
243
            $currentPos = ftell($this->_reader);
244
            if ($currentPos < $offset) {
245
                $toDiscard = $offset - $currentPos;
246
                fread($this->_reader, $toDiscard);
247
248
                return;
249
            }
250
            $this->_copyReadStream();
251
        }
252 9
        fseek($this->_reader, $offset, SEEK_SET);
253 9
    }
254
255
    /** Copy a readOnly Stream to ensure seekability */
256
    private function _copyReadStream()
257
    {
258
        $tmpFile = fopen('php://temp/maxmemory:4096', 'w+b');
259
        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...
260
            /* We have opened a php:// Stream Should work without problem */
261
        } 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...
262
            /* We have opened a tmpfile */
263
        } else {
264
            throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available');
265
        }
266
267
        $currentPos = ftell($this->_reader);
268
269
        fclose($this->_reader);
270
271
        $source = fopen($this->_path, 'rb');
272
        if (!$source) {
273
            throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']');
274
        }
275
276
        fseek($tmpFile, 0, SEEK_SET);
277
        while (!feof($source)) {
278
            fwrite($tmpFile, fread($source, 4096));
279
        }
280
        fseek($tmpFile, $currentPos, SEEK_SET);
281
        fclose($source);
282
283
        $this->_reader = $tmpFile;
284
    }
285
}
286