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