|
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 /** |
|
13 * Allows reading and writing of bytes to and from a file. |
|
14 * @package Swift |
|
15 * @subpackage ByteStream |
|
16 * @author Chris Corbyn |
|
17 */ |
|
18 class Swift_ByteStream_FileByteStream |
|
19 extends Swift_ByteStream_AbstractFilterableInputStream |
|
20 implements Swift_FileStream |
|
21 { |
|
22 |
|
23 /** The internal pointer offset */ |
|
24 private $_offset = 0; |
|
25 |
|
26 /** The path to the file */ |
|
27 private $_path; |
|
28 |
|
29 /** The mode this file is opened in for writing */ |
|
30 private $_mode; |
|
31 |
|
32 /** A lazy-loaded resource handle for reading the file */ |
|
33 private $_reader; |
|
34 |
|
35 /** A lazy-loaded resource handle for writing the file */ |
|
36 private $_writer; |
|
37 |
|
38 /** If magic_quotes_runtime is on, this will be true */ |
|
39 private $_quotes = false; |
|
40 |
|
41 /** If stream is seekable true/false, or null if not known */ |
|
42 private $_seekable = null; |
|
43 |
|
44 /** |
|
45 * Create a new FileByteStream for $path. |
|
46 * @param string $path |
|
47 * @param string $writable if true |
|
48 */ |
|
49 public function __construct($path, $writable = false) |
|
50 { |
|
51 $this->_path = $path; |
|
52 $this->_mode = $writable ? 'w+b' : 'rb'; |
|
53 $this->_quotes = ini_get('magic_quotes_runtime'); |
|
54 } |
|
55 |
|
56 /** |
|
57 * Get the complete path to the file. |
|
58 * @return string |
|
59 */ |
|
60 public function getPath() |
|
61 { |
|
62 return $this->_path; |
|
63 } |
|
64 |
|
65 /** |
|
66 * Reads $length bytes from the stream into a string and moves the pointer |
|
67 * through the stream by $length. If less bytes exist than are requested the |
|
68 * remaining bytes are given instead. If no bytes are remaining at all, boolean |
|
69 * false is returned. |
|
70 * @param int $length |
|
71 * @return string |
|
72 * @throws Swift_IoException |
|
73 */ |
|
74 public function read($length) |
|
75 { |
|
76 $fp = $this->_getReadHandle(); |
|
77 if (!feof($fp)) |
|
78 { |
|
79 if ($this->_quotes) |
|
80 { |
|
81 ini_set('magic_quotes_runtime', 0); |
|
82 } |
|
83 $bytes = fread($fp, $length); |
|
84 if ($this->_quotes) |
|
85 { |
|
86 ini_set('magic_quotes_runtime', 1); |
|
87 } |
|
88 $this->_offset = ftell($fp); |
|
89 return $bytes; |
|
90 } |
|
91 else |
|
92 { |
|
93 return false; |
|
94 } |
|
95 } |
|
96 |
|
97 /** |
|
98 * Move the internal read pointer to $byteOffset in the stream. |
|
99 * @param int $byteOffset |
|
100 * @return boolean |
|
101 */ |
|
102 public function setReadPointer($byteOffset) |
|
103 { |
|
104 if (isset($this->_reader)) |
|
105 { |
|
106 $this->_seekReadStreamToPosition($byteOffset); |
|
107 } |
|
108 $this->_offset = $byteOffset; |
|
109 } |
|
110 |
|
111 // -- Private methods |
|
112 |
|
113 /** Just write the bytes to the file */ |
|
114 protected function _commit($bytes) |
|
115 { |
|
116 fwrite($this->_getWriteHandle(), $bytes); |
|
117 $this->_resetReadHandle(); |
|
118 } |
|
119 |
|
120 /** Not used */ |
|
121 protected function _flush() |
|
122 { |
|
123 } |
|
124 |
|
125 /** Get the resource for reading */ |
|
126 private function _getReadHandle() |
|
127 { |
|
128 if (!isset($this->_reader)) |
|
129 { |
|
130 if (!$this->_reader = fopen($this->_path, 'rb')) |
|
131 { |
|
132 throw new Swift_IoException( |
|
133 'Unable to open file for reading [' . $this->_path . ']' |
|
134 ); |
|
135 } |
|
136 if ($this->_offset <> 0) |
|
137 { |
|
138 $this->_getReadStreamSeekableStatus(); |
|
139 $this->_seekReadStreamToPosition($this->_offset); |
|
140 } |
|
141 } |
|
142 return $this->_reader; |
|
143 } |
|
144 |
|
145 /** Get the resource for writing */ |
|
146 private function _getWriteHandle() |
|
147 { |
|
148 if (!isset($this->_writer)) |
|
149 { |
|
150 if (!$this->_writer = fopen($this->_path, $this->_mode)) |
|
151 { |
|
152 throw new Swift_IoException( |
|
153 'Unable to open file for writing [' . $this->_path . ']' |
|
154 ); |
|
155 } |
|
156 } |
|
157 return $this->_writer; |
|
158 } |
|
159 |
|
160 /** Force a reload of the resource for reading */ |
|
161 private function _resetReadHandle() |
|
162 { |
|
163 if (isset($this->_reader)) |
|
164 { |
|
165 fclose($this->_reader); |
|
166 $this->_reader = null; |
|
167 } |
|
168 } |
|
169 |
|
170 /** Check if ReadOnly Stream is seekable */ |
|
171 private function _getReadStreamSeekableStatus() |
|
172 { |
|
173 $metas = stream_get_meta_data($this->_reader); |
|
174 $this->_seekable = $metas['seekable']; |
|
175 } |
|
176 |
|
177 /** Streams in a readOnly stream ensuring copy if needed */ |
|
178 private function _seekReadStreamToPosition($offset) |
|
179 { |
|
180 if ($this->_seekable===null) |
|
181 { |
|
182 $this->_getReadStreamSeekableStatus(); |
|
183 } |
|
184 if ($this->_seekable === false) |
|
185 { |
|
186 $currentPos = ftell($this->_reader); |
|
187 if ($currentPos<$offset) |
|
188 { |
|
189 $toDiscard = $offset-$currentPos; |
|
190 fread($this->_reader, $toDiscard); |
|
191 return; |
|
192 } |
|
193 $this->_copyReadStream(); |
|
194 } |
|
195 fseek($this->_reader, $offset, SEEK_SET); |
|
196 } |
|
197 |
|
198 /** Copy a readOnly Stream to ensure seekability */ |
|
199 private function _copyReadStream() |
|
200 { |
|
201 if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) |
|
202 { |
|
203 /* We have opened a php:// Stream Should work without problem */ |
|
204 } |
|
205 elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) |
|
206 { |
|
207 /* We have opened a tmpfile */ |
|
208 } |
|
209 else |
|
210 { |
|
211 throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); |
|
212 } |
|
213 $currentPos = ftell($this->_reader); |
|
214 fclose($this->_reader); |
|
215 $source = fopen($this->_path, 'rb'); |
|
216 if (!$source) |
|
217 { |
|
218 throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']'); |
|
219 } |
|
220 fseek($tmpFile, 0, SEEK_SET); |
|
221 while (!feof($source)) |
|
222 { |
|
223 fwrite($tmpFile, fread($source, 4096)); |
|
224 } |
|
225 fseek($tmpFile, $currentPos, SEEK_SET); |
|
226 fclose($source); |
|
227 $this->_reader = $tmpFile; |
|
228 } |
|
229 } |