Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • SafeStream
  • Overview
  • Namespace
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\IO;
 13: 
 14: use Nette;
 15: 
 16: 
 17: 
 18: /**
 19:  * Thread safe / atomic file manipulation. Stream safe://
 20:  *
 21:  * <code>
 22:  * file_put_contents('safe://myfile.txt', $content);
 23:  *
 24:  * $content = file_get_contents('safe://myfile.txt');
 25:  *
 26:  * unlink('safe://myfile.txt');
 27:  * </code>
 28:  *
 29:  * @author     David Grudl
 30:  */
 31: final class SafeStream
 32: {
 33:     /**
 34:      * Name of stream protocol - safe://
 35:      */
 36:     const PROTOCOL = 'safe';
 37: 
 38:     /**
 39:      * Current file handle.
 40:      */
 41:     private $handle;
 42: 
 43:     /**
 44:      * Renaming of temporary file.
 45:      */
 46:     private $filePath;
 47:     private $tempFile;
 48: 
 49:     /**
 50:      * Starting position in file (for appending).
 51:      */
 52:     private $startPos = 0;
 53: 
 54:     /**
 55:      * Write-error detected?
 56:      */
 57:     private $writeError = FALSE;
 58: 
 59: 
 60: 
 61:     /**
 62:      * Registers protocol 'safe://'.
 63:      * @return bool
 64:      */
 65:     public static function register()
 66:     {
 67:         return stream_wrapper_register(self::PROTOCOL, __CLASS__);
 68:     }
 69: 
 70: 
 71: 
 72:     /**
 73:      * Opens file.
 74:      * @param  string    file name with stream protocol
 75:      * @param  string    mode - see fopen()
 76:      * @param  int       STREAM_USE_PATH, STREAM_REPORT_ERRORS
 77:      * @param  string    full path
 78:      * @return bool      TRUE on success or FALSE on failure
 79:      */
 80:     public function stream_open($path, $mode, $options, &$opened_path)
 81:     {
 82:         $path = substr($path, strlen(self::PROTOCOL)+3);  // trim protocol safe://
 83: 
 84:         $flag = trim($mode, 'rwax+');  // text | binary mode
 85:         $mode = trim($mode, 'tb');     // mode
 86:         $use_path = (bool) (STREAM_USE_PATH & $options); // use include_path?
 87: 
 88:         $append = FALSE;
 89: 
 90:         switch ($mode) {
 91:         case 'r':
 92:         case 'r+':
 93:             // enter critical section: open and lock EXISTING file for reading/writing
 94:             $handle = @fopen($path, $mode.$flag, $use_path); // intentionally @
 95:             if (!$handle) return FALSE;
 96:             if (flock($handle, $mode == 'r' ? LOCK_SH : LOCK_EX)) {
 97:                 $this->handle = $handle;
 98:                 return TRUE;
 99:             }
100:             fclose($handle);
101:             return FALSE;
102: 
103:         case 'a':
104:         case 'a+': $append = TRUE;
105:         case 'w':
106:         case 'w+':
107:             // try enter critical section: open and lock EXISTING file for rewriting
108:             $handle = @fopen($path, 'r+'.$flag, $use_path); // intentionally @
109: 
110:             if ($handle) {
111:                 if (flock($handle, LOCK_EX)) {
112:                     if ($append) {
113:                         fseek($handle, 0, SEEK_END);
114:                         $this->startPos = ftell($handle);
115:                     } else {
116:                         ftruncate($handle, 0);
117:                     }
118:                     $this->handle = $handle;
119:                     return TRUE;
120:                 }
121:                 fclose($handle);
122:             }
123:             // file doesn't exists, continue...
124:             $mode{0} = 'x'; // x || x+
125: 
126:         case 'x':
127:         case 'x+':
128:             if (file_exists($path)) return FALSE;
129: 
130:             // create temporary file in the same directory
131:             $tmp = '~~' . time() . '.tmp';
132: 
133:             // enter critical section: create temporary file
134:             $handle = @fopen($path . $tmp, $mode . $flag, $use_path); // intentionally @
135:             if ($handle) {
136:                 if (flock($handle, LOCK_EX)) {
137:                     $this->handle = $handle;
138:                     if (!@rename($path . $tmp, $path)) { // intentionally @
139:                         // rename later - for windows
140:                         $this->tempFile = realpath($path . $tmp);
141:                         $this->filePath = substr($this->tempFile, 0, -strlen($tmp));
142:                     }
143:                     return TRUE;
144:                 }
145:                 fclose($handle);
146:                 unlink($path . $tmp);
147:             }
148:             return FALSE;
149: 
150:         default:
151:             trigger_error("Unsupported mode $mode", E_USER_WARNING);
152:             return FALSE;
153:         } // switch
154: 
155:     } // stream_open
156: 
157: 
158: 
159:     /**
160:      * Closes file.
161:      * @return void
162:      */
163:     public function stream_close()
164:     {
165:         if ($this->writeError) {
166:             ftruncate($this->handle, $this->startPos);
167:         }
168: 
169:         flock($this->handle, LOCK_UN);
170:         fclose($this->handle);
171: 
172:         // are we working with temporary file?
173:         if ($this->tempFile) {
174:             // try to rename temp file, otherwise delete temp file
175:             if (!@rename($this->tempFile, $this->filePath)) { // intentionally @
176:                 unlink($this->tempFile);
177:             }
178:         }
179:     }
180: 
181: 
182: 
183:     /**
184:      * Reads up to length bytes from the file.
185:      * @param  int    length
186:      * @return string
187:      */
188:     public function stream_read($length)
189:     {
190:         return fread($this->handle, $length);
191:     }
192: 
193: 
194: 
195:     /**
196:      * Writes the string to the file.
197:      * @param  string    data to write
198:      * @return int      number of bytes that were successfully stored
199:      */
200:     public function stream_write($data)
201:     {
202:         $len = strlen($data);
203:         $res = fwrite($this->handle, $data, $len);
204: 
205:         if ($res !== $len) { // disk full?
206:             $this->writeError = TRUE;
207:         }
208: 
209:         return $res;
210:     }
211: 
212: 
213: 
214:     /**
215:      * Returns the position of the file.
216:      * @return int
217:      */
218:     public function stream_tell()
219:     {
220:         return ftell($this->handle);
221:     }
222: 
223: 
224: 
225:     /**
226:      * Returns TRUE if the file pointer is at end-of-file.
227:      * @return bool
228:      */
229:     public function stream_eof()
230:     {
231:         return feof($this->handle);
232:     }
233: 
234: 
235: 
236:     /**
237:      * Sets the file position indicator for the file.
238:      * @param  int    position
239:      * @param  int    see fseek()
240:      * @return int   Return TRUE on success
241:      */
242:     public function stream_seek($offset, $whence)
243:     {
244:         return fseek($this->handle, $offset, $whence) === 0; // ???
245:     }
246: 
247: 
248: 
249:     /**
250:      * Gets information about a file referenced by $this->handle.
251:      * @return array
252:      */
253:     public function stream_stat()
254:     {
255:         return fstat($this->handle);
256:     }
257: 
258: 
259: 
260:     /**
261:      * Gets information about a file referenced by filename.
262:      * @param  string    file name
263:      * @param  int       STREAM_URL_STAT_LINK, STREAM_URL_STAT_QUIET
264:      * @return array
265:      */
266:     public function url_stat($path, $flags)
267:     {
268:         // This is not thread safe
269:         $path = substr($path, strlen(self::PROTOCOL)+3);
270:         return ($flags & STREAM_URL_STAT_LINK) ? @lstat($path) : @stat($path); // intentionally @
271:     }
272: 
273: 
274: 
275:     /**
276:      * Deletes a file.
277:      * On Windows unlink is not allowed till file is opened
278:      * @param  string    file name with stream protocol
279:      * @return bool      TRUE on success or FALSE on failure
280:      */
281:     public function unlink($path)
282:     {
283:         $path = substr($path, strlen(self::PROTOCOL)+3);
284:         return unlink($path);
285:     }
286: 
287: }
288: 
Nette Framework 0.9.7 API documentation generated by ApiGen 2.3.0