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