Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • ObjectHelpers
  • ObjectMixin
  • Paginator
  • Random
  • Reflection
  • SafeStream
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

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