1: <?php
2: /* SVN FILE: $Id$ */
3: /**
4: * Convenience class for reading, writing and appending to files.
5: *
6: * PHP versions 4 and 5
7: *
8: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
9: * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
10: *
11: * Licensed under The MIT License
12: * Redistributions of files must retain the above copyright notice.
13: *
14: * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
15: * @link http://cakephp.org CakePHP(tm) Project
16: * @package cake
17: * @subpackage cake.cake.libs
18: * @since CakePHP(tm) v 0.2.9
19: * @version $Revision$
20: * @modifiedby $LastChangedBy$
21: * @lastmodified $Date$
22: * @license http://www.opensource.org/licenses/mit-license.php The MIT License
23: */
24: /**
25: * Included libraries.
26: *
27: */
28: if (!class_exists('Object')) {
29: uses('object');
30: }
31: if (!class_exists('Folder')) {
32: require LIBS . 'folder.php';
33: }
34: /**
35: * Convenience class for reading, writing and appending to files.
36: *
37: * @package cake
38: * @subpackage cake.cake.libs
39: */
40: class File extends Object {
41: /**
42: * Folder object of the File
43: *
44: * @var Folder
45: * @access public
46: */
47: var $Folder = null;
48: /**
49: * Filename
50: *
51: * @var string
52: * @access public
53: */
54: var $name = null;
55: /**
56: * file info
57: *
58: * @var string
59: * @access public
60: */
61: var $info = array();
62: /**
63: * Holds the file handler resource if the file is opened
64: *
65: * @var resource
66: * @access public
67: */
68: var $handle = null;
69: /**
70: * enable locking for file reading and writing
71: *
72: * @var boolean
73: * @access public
74: */
75: var $lock = null;
76: /**
77: * path property
78: *
79: * Current file's absolute path
80: *
81: * @var mixed null
82: * @access public
83: */
84: var $path = null;
85: /**
86: * Constructor
87: *
88: * @param string $path Path to file
89: * @param boolean $create Create file if it does not exist (if true)
90: * @param integer $mode Mode to apply to the folder holding the file
91: * @access public
92: */
93: function __construct($path, $create = false, $mode = 0755) {
94: parent::__construct();
95: $this->Folder =& new Folder(dirname($path), $create, $mode);
96: if (!is_dir($path)) {
97: $this->name = basename($path);
98: }
99: $this->pwd();
100: !$this->exists() && $create && $this->safe($path) && $this->create();
101: }
102: /**
103: * Closes the current file if it is opened
104: *
105: * @access public
106: */
107: function __destruct() {
108: $this->close();
109: }
110: /**
111: * Creates the File.
112: *
113: * @return boolean Success
114: * @access public
115: */
116: function create() {
117: $dir = $this->Folder->pwd();
118: if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
119: $old = umask(0);
120: if (touch($this->path)) {
121: umask($old);
122: return true;
123: }
124: }
125: return false;
126: }
127: /**
128: * Opens the current file with a given $mode
129: *
130: * @param string $mode A valid 'fopen' mode string (r|w|a ...)
131: * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
132: * @return boolean True on success, false on failure
133: * @access public
134: */
135: function open($mode = 'r', $force = false) {
136: if (!$force && is_resource($this->handle)) {
137: return true;
138: }
139: clearstatcache();
140: if ($this->exists() === false) {
141: if ($this->create() === false) {
142: return false;
143: }
144: }
145:
146: $this->handle = fopen($this->path, $mode);
147: if (is_resource($this->handle)) {
148: return true;
149: }
150: return false;
151: }
152: /**
153: * Return the contents of this File as a string.
154: *
155: * @param string $bytes where to start
156: * @param string $mode
157: * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
158: * @return mixed string on success, false on failure
159: * @access public
160: */
161: function read($bytes = false, $mode = 'rb', $force = false) {
162: if ($bytes === false && $this->lock === null) {
163: return file_get_contents($this->path);
164: }
165: if ($this->open($mode, $force) === false) {
166: return false;
167: }
168: if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
169: return false;
170: }
171: if (is_int($bytes)) {
172: return fread($this->handle, $bytes);
173: }
174:
175: $data = '';
176: while (!feof($this->handle)) {
177: $data .= fgets($this->handle, 4096);
178: }
179: $data = trim($data);
180:
181: if ($this->lock !== null) {
182: flock($this->handle, LOCK_UN);
183: }
184: if ($bytes === false) {
185: $this->close();
186: }
187: return $data;
188: }
189: /**
190: * Sets or gets the offset for the currently opened file.
191: *
192: * @param mixed $offset The $offset in bytes to seek. If set to false then the current offset is returned.
193: * @param integer $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
194: * @return mixed True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
195: * @access public
196: */
197: function offset($offset = false, $seek = SEEK_SET) {
198: if ($offset === false) {
199: if (is_resource($this->handle)) {
200: return ftell($this->handle);
201: }
202: } elseif ($this->open() === true) {
203: return fseek($this->handle, $offset, $seek) === 0;
204: }
205: return false;
206: }
207: /**
208: * Prepares a ascii string for writing
209: * fixes line endings
210: *
211: * @param string $data Data to prepare for writing.
212: * @return string
213: * @access public
214: */
215: function prepare($data, $forceWindows = false) {
216: $lineBreak = "\n";
217: if (DIRECTORY_SEPARATOR == '\\' || $forceWindows === true) {
218: $lineBreak = "\r\n";
219: }
220: return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak));
221: }
222:
223: /**
224: * Write given data to this File.
225: *
226: * @param string $data Data to write to this File.
227: * @param string $mode Mode of writing. {@link http://php.net/fwrite See fwrite()}.
228: * @param string $force force the file to open
229: * @return boolean Success
230: * @access public
231: */
232: function write($data, $mode = 'w', $force = false) {
233: $success = false;
234: if ($this->open($mode, $force) === true) {
235: if ($this->lock !== null) {
236: if (flock($this->handle, LOCK_EX) === false) {
237: return false;
238: }
239: }
240:
241: if (fwrite($this->handle, $data) !== false) {
242: $success = true;
243: }
244: if ($this->lock !== null) {
245: flock($this->handle, LOCK_UN);
246: }
247: }
248: return $success;
249: }
250: /**
251: * Append given data string to this File.
252: *
253: * @param string $data Data to write
254: * @param string $force force the file to open
255: * @return boolean Success
256: * @access public
257: */
258: function append($data, $force = false) {
259: return $this->write($data, 'a', $force);
260: }
261: /**
262: * Closes the current file if it is opened.
263: *
264: * @return boolean True if closing was successful or file was already closed, otherwise false
265: * @access public
266: */
267: function close() {
268: if (!is_resource($this->handle)) {
269: return true;
270: }
271: return fclose($this->handle);
272: }
273: /**
274: * Deletes the File.
275: *
276: * @return boolean Success
277: * @access public
278: */
279: function delete() {
280: clearstatcache();
281: if (is_resource($this->handle)) {
282: fclose($this->handle);
283: $this->handle = null;
284: }
285: if ($this->exists()) {
286: return unlink($this->path);
287: }
288: return false;
289: }
290: /**
291: * Returns the File extension.
292: *
293: * @return string The File extension
294: * @access public
295: */
296: function info() {
297: if ($this->info == null) {
298: $this->info = pathinfo($this->path);
299: }
300: if (!isset($this->info['filename'])) {
301: $this->info['filename'] = $this->name();
302: }
303: return $this->info;
304: }
305: /**
306: * Returns the File extension.
307: *
308: * @return string The File extension
309: * @access public
310: */
311: function ext() {
312: if ($this->info == null) {
313: $this->info();
314: }
315: if (isset($this->info['extension'])) {
316: return $this->info['extension'];
317: }
318: return false;
319: }
320: /**
321: * Returns the File name without extension.
322: *
323: * @return string The File name without extension.
324: * @access public
325: */
326: function name() {
327: if ($this->info == null) {
328: $this->info();
329: }
330: if (isset($this->info['extension'])) {
331: return basename($this->name, '.'.$this->info['extension']);
332: } elseif ($this->name) {
333: return $this->name;
334: }
335: return false;
336: }
337: /**
338: * makes filename safe for saving
339: *
340: * @param string $name the name of the file to make safe if different from $this->name
341: * @return string $ext the extension of the file
342: * @access public
343: */
344: function safe($name = null, $ext = null) {
345: if (!$name) {
346: $name = $this->name;
347: }
348: if (!$ext) {
349: $ext = $this->ext();
350: }
351: return preg_replace( "/(?:[^\w\.-]+)/", "_", basename($name, $ext));
352: }
353: /**
354: * Get md5 Checksum of file with previous check of Filesize
355: *
356: * @param mixed $maxsize in MB or true to force
357: * @return string md5 Checksum {@link http://php.net/md5_file See md5_file()}
358: * @access public
359: */
360: function md5($maxsize = 5) {
361: if ($maxsize === true) {
362: return md5_file($this->path);
363: }
364:
365: $size = $this->size();
366: if ($size && $size < ($maxsize * 1024) * 1024) {
367: return md5_file($this->path);
368: }
369:
370: return false;
371: }
372: /**
373: * Returns the full path of the File.
374: *
375: * @return string Full path to file
376: * @access public
377: */
378: function pwd() {
379: if (is_null($this->path)) {
380: $this->path = $this->Folder->slashTerm($this->Folder->pwd()) . $this->name;
381: }
382: return $this->path;
383: }
384: /**
385: * Returns true if the File exists.
386: *
387: * @return boolean true if it exists, false otherwise
388: * @access public
389: */
390: function exists() {
391: return (file_exists($this->path) && is_file($this->path));
392: }
393: /**
394: * Returns the "chmod" (permissions) of the File.
395: *
396: * @return string Permissions for the file
397: * @access public
398: */
399: function perms() {
400: if ($this->exists()) {
401: return substr(sprintf('%o', fileperms($this->path)), -4);
402: }
403: return false;
404: }
405: /**
406: * Returns the Filesize
407: *
408: * @return integer size of the file in bytes, or false in case of an error
409: * @access public
410: */
411: function size() {
412: if ($this->exists()) {
413: return filesize($this->path);
414: }
415: return false;
416: }
417: /**
418: * Returns true if the File is writable.
419: *
420: * @return boolean true if its writable, false otherwise
421: * @access public
422: */
423: function writable() {
424: return is_writable($this->path);
425: }
426: /**
427: * Returns true if the File is executable.
428: *
429: * @return boolean true if its executable, false otherwise
430: * @access public
431: */
432: function executable() {
433: return is_executable($this->path);
434: }
435: /**
436: * Returns true if the File is readable.
437: *
438: * @return boolean true if file is readable, false otherwise
439: * @access public
440: */
441: function readable() {
442: return is_readable($this->path);
443: }
444: /**
445: * Returns the File's owner.
446: *
447: * @return integer the Fileowner
448: * @access public
449: */
450: function owner() {
451: if ($this->exists()) {
452: return fileowner($this->path);
453: }
454: return false;
455: }
456: /**
457: * Returns the File group.
458: *
459: * @return integer the Filegroup
460: * @access public
461: */
462: function group() {
463: if ($this->exists()) {
464: return filegroup($this->path);
465: }
466: return false;
467: }
468: /**
469: * Returns last access time.
470: *
471: * @return integer timestamp Timestamp of last access time
472: * @access public
473: */
474: function lastAccess() {
475: if ($this->exists()) {
476: return fileatime($this->path);
477: }
478: return false;
479: }
480: /**
481: * Returns last modified time.
482: *
483: * @return integer timestamp Timestamp of last modification
484: * @access public
485: */
486: function lastChange() {
487: if ($this->exists()) {
488: return filemtime($this->path);
489: }
490: return false;
491: }
492: /**
493: * Returns the current folder.
494: *
495: * @return Folder Current folder
496: * @access public
497: */
498: function &Folder() {
499: return $this->Folder;
500: }
501: }
502: ?>
503: