1: <?php
2: /**
3: * This file contains the the static file handler class.
4: *
5: * @package Core
6: * @subpackage Util
7: * @author Mischa Holz
8: * @copyright four for business AG <www.4fb.de>
9: * @license http://www.contenido.org/license/LIZENZ.txt
10: * @link http://www.4fb.de
11: * @link http://www.contenido.org
12: */
13: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
14:
15: /**
16: * Class for file handling.
17: * Provides functions for dealing with files.
18: *
19: * @package Core
20: * @subpackage Util
21: */
22: class cFileHandler {
23:
24: /**
25: * default permissions for new files
26: *
27: * @see CON-2770
28: * @var int
29: */
30: const DEFAULT_MODE = 0664;
31:
32: /**
33: * Creates a new file
34: *
35: * @param string $filename
36: * the name and path of the new file
37: * @param string $content [optional]
38: * content of the new file
39: *
40: * @return bool
41: * true on success. Otherwise false.
42: *
43: * @throws cInvalidArgumentException
44: */
45: public static function create($filename, $content = '') {
46: $success = file_put_contents($filename, $content) === cString::getStringLength($content);
47: if ($success) {
48: self::setDefaultPermissions($filename);
49: }
50:
51: return $success;
52: }
53:
54: /**
55: * Reads bytes from a file
56: *
57: * @param string $filename
58: * the name and path of the file
59: * @param int $length [optional]
60: * the number of bytes to read.
61: * @param int $offset [optional]
62: * this will be the first byte which is read.
63: * @param bool $reverse [optional]
64: * if true, the function will start from the back of the file.
65: *
66: * @return string|bool
67: * On success it returns the bytes which have been read.
68: * Otherwise false.
69: *
70: * @throws cInvalidArgumentException
71: * if the file with the given filename does not exist
72: */
73: public static function read($filename, $length = 0, $offset = 0, $reverse = false) {
74: if (!cFileHandler::exists($filename)) {
75: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
76: }
77:
78: if ($reverse) {
79: return file_get_contents($filename, false, NULL, filesize($filename) - $length - $offset, $length);
80: } else if ($length > 0 && $offset == 0) {
81: return file_get_contents($filename, false, NULL, 0, $length);
82: } else if ($offset > 0 && $length == 0) {
83: return file_get_contents($filename, false, NULL, $offset);
84: } else if ($offset > 0 && $length > 0) {
85: return file_get_contents($filename, false, NULL, $offset, $length);
86: } else {
87: return file_get_contents($filename);
88: }
89: }
90:
91: /**
92: * Reads a file line by line
93: *
94: * @param string $filename
95: * the name and path of the file
96: * @param int $lines [optional]
97: * the number of lines to be read.
98: * @param int $lineoffset [optional]
99: * this will be the first line which is read.
100: *
101: * @return string|array|bool
102: * If one line was read the function will return it.
103: * If more than one line was read the function will return an array
104: * containing the lines. Otherwise false is returned
105: *
106: * @throws cInvalidArgumentException
107: * if the file with the given filename does not exist
108: */
109: public static function readLine($filename, $lines = 0, $lineoffset = 0) {
110: if (!cFileHandler::exists($filename)) {
111: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
112: }
113:
114: $f = fopen($filename, 'r');
115:
116: if ($f === false) {
117: fclose($f);
118: return false;
119: }
120: if ($lines == 0) {
121: $lines = 1;
122: }
123:
124: for ($i = 0; $i < $lineoffset; $i++) {
125: $waste = fgets($f);
126: }
127:
128: $ret = NULL;
129: if ($lines > 1) {
130: $ret = array();
131: for ($i = 0; $i < $lines; $i++) {
132: $temp = fgets($f);
133: if ($temp === false) {
134: fclose($f);
135: return false;
136: }
137: $ret[] = cString::getPartOfString($temp, 0, cString::getStringLength($temp) - 1);
138: }
139: } else {
140: $ret = fgets($f);
141: $ret = cString::getPartOfString($ret, 0, cString::getStringLength($ret) - 1);
142: }
143:
144: fclose($f);
145: return $ret;
146: }
147:
148: /**
149: * Writes data to a file
150: *
151: * @param string $filename
152: * the name and path of the file
153: * @param string $content
154: * the data which should be written
155: * @param bool $append [optional]
156: * if true the data will be appended to the file.
157: *
158: * @return bool
159: * true on success, false otherwise
160: *
161: * @throws cInvalidArgumentException
162: */
163: public static function write($filename, $content, $append = false) {
164: $flag = 0;
165: if ($append && self::exists($filename)) {
166: $flag = FILE_APPEND;
167: }
168:
169: $success = file_put_contents($filename, $content, $flag);
170: if ((int) $success != 0) {
171: self::setDefaultPermissions($filename);
172: }
173:
174: return !($success === false);
175: }
176:
177: /**
178: * Writes a line to a file (this is similar to
179: * cFileHandler::write($filename, $data."\n", $apppend)
180: *
181: * @see cFileHandler::write($filename, $content, $append)
182: *
183: * @param string $filename
184: * the name and path to the file
185: * @param string $content
186: * the data of the line
187: * @param bool $append [optional]
188: * if true the data will be appended to file.
189: *
190: * @return bool
191: * true on success, false otherwise
192: *
193: * @throws cInvalidArgumentException
194: */
195: public static function writeLine($filename, $content, $append = false) {
196: return self::write($filename, $content . "\n", $append);
197: }
198:
199: /**
200: * Checks if a file or a directory exists
201: *
202: * @param string $filename
203: * the name and path of the file or to the directory
204: * @return bool
205: * true if the file or the directory exists.
206: */
207: public static function exists($filename) {
208: return file_exists($filename);
209: }
210:
211: /**
212: * Checks if a file exists and is not a directory.
213: *
214: * @param string $filename
215: * the name and path of the file
216: * @return bool
217: * true if the file exists and is not a directory
218: */
219: public static function isFile($filename) {
220: return is_file($filename);
221: }
222:
223: /**
224: * Checks if the file is writable for the PHP user
225: *
226: * @param string $filename
227: * the name and path of the file
228: * @return bool
229: * true if the file can be written
230: */
231: public static function writeable($filename) {
232: return is_writable($filename);
233: }
234:
235: /**
236: * Checks if a file is readable for the PHP user
237: *
238: * @param string $filename
239: * the name and path of the file
240: *
241: * @return bool
242: * true if the file is readable
243: *
244: * @throws cInvalidArgumentException
245: * if the file with the given filename does not exist
246: */
247: public static function readable($filename) {
248: if (!cFileHandler::exists($filename)) {
249: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
250: }
251:
252: return is_readable($filename);
253: }
254:
255: /**
256: * Removes a file from the filesystem
257: *
258: * @param string $filename
259: * the name and path of the file
260: *
261: * @return bool
262: * true on success
263: *
264: * @throws cInvalidArgumentException
265: * if the file with the given filename does not exist
266: */
267: public static function remove($filename) {
268: if (!cFileHandler::exists($filename)) {
269: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
270: }
271:
272: return unlink($filename);
273: }
274:
275: /**
276: * Truncates a file so that it is empty
277: *
278: * @param string $filename
279: * the name and path of the file
280: *
281: * @return bool
282: * true on success
283: *
284: * @throws cInvalidArgumentException
285: * if the file with the given filename does not exist
286: */
287: public static function truncate($filename) {
288: if (!cFileHandler::exists($filename)) {
289: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
290: }
291: $success = file_put_contents($filename, '') === 0;
292: if ($success) {
293: self::setDefaultPermissions($filename);
294: }
295:
296: return $success;
297: }
298:
299: /**
300: * Moves a file
301: *
302: * @param string $filename
303: * the name of the source file
304: * @param string $destination
305: * the destination. Note that the file can also be renamed in the
306: * process of moving it
307: *
308: * @return bool
309: * true on success
310: *
311: * @throws cInvalidArgumentException
312: * if the file with the given filename does not exist
313: */
314: public static function move($filename, $destination) {
315: if (!cFileHandler::exists($filename)) {
316: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
317: }
318: $success = rename($filename, $destination);
319: if ($success) {
320: self::setDefaultPermissions($destination);
321: }
322:
323: return $success;
324: }
325:
326: /**
327: * Renames a file
328: *
329: * @param string $filename
330: * the name and path of the file
331: * @param string $new_filename
332: * the new name of the file
333: *
334: * @return bool
335: * true on success
336: *
337: * @throws cInvalidArgumentException
338: * if the file with the given filename does not exist
339: */
340: public static function rename($filename, $new_filename) {
341: if (!cFileHandler::exists($filename)) {
342: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
343: }
344: $success = rename($filename, dirname($filename) . '/' . $new_filename);
345: if ($success) {
346: self::setDefaultPermissions(dirname($filename) . '/' . $new_filename);
347: }
348:
349: return $success;
350: }
351:
352: /**
353: * Copies a file
354: *
355: * @param string $filename
356: * the name and path of the file
357: * @param string $destination
358: * the destination. Note that existing files get overwritten
359: *
360: * @return bool
361: * true on success
362: *
363: * @throws cInvalidArgumentException
364: * if the file with the given filename does not exist
365: */
366: public static function copy($filename, $destination) {
367: if (!cFileHandler::exists($filename)) {
368: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
369: }
370: $success = copy($filename, $destination);
371: if ($success) {
372: self::setDefaultPermissions($destination);
373: }
374:
375: return $success;
376: }
377:
378: /**
379: * Changes the file permissions
380: *
381: * @param string $filename
382: * the name and path of the file
383: * @param int $mode
384: * the new access mode : php chmod needs octal value
385: *
386: * @return bool
387: * true on success
388: *
389: * @throws cInvalidArgumentException
390: * if the file with the given filename does not exist
391: */
392: public static function chmod($filename, $mode) {
393: if (!cFileHandler::exists($filename)) {
394: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
395: }
396: // chmod needs octal value for correct execution.
397: $mode = intval($mode, 8);
398: return chmod($filename, $mode);
399: }
400:
401: /**
402: * Returns an array containing information about the file.
403: * Currently
404: * following elements are in it: 'size' - the file size (in byte) 'atime' -
405: * the time the file was last accessed (unix timestamp) 'ctime' - time the
406: * file was created (unix timestamp) 'mtime' - time the file was last
407: * modified (unix timestamp) 'perms' - permissions of the file represented
408: * in 4 octal digits 'extension' - the file extension or '' if there's no
409: * extension 'mime' - the mime type of the file
410: *
411: * @param string $filename
412: * the name and path to the file
413: *
414: * @return array
415: * Returns an array containing information about the file
416: *
417: * @throws cInvalidArgumentException
418: * if the file with the given filename does not exist
419: */
420: public static function info($filename) {
421: if (!cFileHandler::exists($filename)) {
422: throw new cInvalidArgumentException('The file ' . $filename . ' could not be accessed because it does not exist.');
423: }
424:
425: $ret = array();
426:
427: $ret['size'] = @filesize($filename);
428: $ret['atime'] = @fileatime($filename);
429: $ret['ctime'] = @filectime($filename);
430: $ret['mtime'] = @filemtime($filename);
431:
432: $temp = @decoct(fileperms($filename));
433: $ret['perms'] = cString::getPartOfString($temp, cString::getStringLength($temp) - 4);
434:
435: $ret['extension'] = cString::getPartOfString(basename($filename), (int) cString::findLastPos(basename($filename), '.') + 1);
436: if ($ret['extension'] == basename($filename)) {
437: $ret['extension'] = '';
438: }
439:
440: if (function_exists('finfo_open')) {
441: // extension has to be installed seperately in versions prior to 5.3
442: $finfo = @finfo_open(FILEINFO_MIME_TYPE);
443: $ret['mime'] = @finfo_file($finfo, $filename);
444: } else {
445: $ret['mime'] = '';
446: }
447:
448: return $ret;
449: }
450:
451: /**
452: * Returns the extension of passed filename
453: *
454: * @param string $basename
455: * @return string
456: */
457: public static function getExtension($basename) {
458: return pathinfo($basename, PATHINFO_EXTENSION);
459: }
460:
461: /**
462: * Determines the default permissions for new files.
463: * These can be configured using the setting "default_perms/file" in "data/config/<ENV>/config.misc.php".
464: * If no configuration can be found 0664 is assumed.
465: *
466: * @return int
467: */
468: public static function getDefaultPermissions()
469: {
470: $mode = cRegistry::getConfigValue('default_perms', 'file', self::DEFAULT_MODE);
471:
472: return intval($mode, 8);
473: }
474:
475: /**
476: * Sets the default permissions for the given file.
477: *
478: * @param string $filename
479: * the name of the file
480: *
481: * @return bool
482: * true on success or false on failure
483: *
484: * @throws cInvalidArgumentException
485: */
486: public static function setDefaultPermissions($filename)
487: {
488: return self::chmod($filename, self::getDefaultPermissions());
489: }
490:
491: /**
492: * Sets the default permissions for the given file.
493: *
494: * @deprecated use setDefaultPermissions() instead
495: * @param string $filename
496: * the name of the file
497: *
498: * @return bool
499: * true on success or false on failure
500: *
501: * @throws cInvalidArgumentException
502: */
503: public static function setDefaultFilePerms($filename)
504: {
505: return self::setDefaultPermissions($filename);
506: }
507:
508: /**
509: * Validates the given filename.
510: *
511: * @param string $filename
512: * the filename to validate
513: * @param bool $notifyAndExitOnFailure [optional]
514: * if set, function will show a notification and will exit the script
515: *
516: * @return bool
517: * true if the given filename is valid, false otherwise
518: *
519: * @throws cInvalidArgumentException
520: */
521: public static function validateFilename($filename, $notifyAndExitOnFailure = true) {
522: // check if filename only contains valid characters
523: if (preg_match('/[^a-z0-9._-]/i', $filename)) {
524: // validation failure...
525: if ($notifyAndExitOnFailure) {
526: // display notification and exit
527: cRegistry::addErrorMessage(i18n('Wrong file name.'));
528: $page = new cGuiPage('generic_page');
529: $page->abortRendering();
530: $page->render();
531: exit();
532: }
533:
534: return false;
535: }
536:
537: // check if filename is empty
538: if (cString::getStringLength(trim($filename)) == 0) {
539: // validation failure...
540: if ($notifyAndExitOnFailure) {
541: // display notification and exit
542: $notification = new cGuiNotification();
543: $notification->displayNotification("error", i18n("Please insert file name."));
544: exit();
545: }
546:
547: return false;
548: }
549:
550: return true;
551: }
552:
553: /**
554: * Check if given filename is either '.' or '..'.
555: *
556: * @param string $fileName
557: * @return bool
558: */
559: public static function fileNameIsDot($fileName) {
560: // bugfix: function must work with full paths of files
561: $parts = explode('/', $fileName);
562: $name = end($parts);
563: if ($name != '.' && $name != '..') {
564: return false;
565: } else {
566: return true;
567: }
568: }
569:
570: /**
571: * Check if file name begins with a period.
572: *
573: * @param string $fileName
574: * @return bool
575: */
576: public static function fileNameBeginsWithDot($fileName) {
577: return cString::findFirstPos(end(explode('/', $fileName)), ".") === 0;
578: }
579: }
580: