1: <?php
2:
3: namespace Liberty;
4:
5: use finfo;
6:
7: /**
8: * Convenience class for reading, writing and appending to files.
9: *
10: * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11: *
12: * Licensed under The MIT License
13: * For full copyright and license information, please see the LICENSE.txt
14: * Redistributions of files must retain the above copyright notice.
15: *
16: * @category Cryptocurrency
17: * @package Liberty
18: * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
19: * @link http://cakephp.org CakePHP(tm) Project
20: * @since 0.2.9
21: * @license http://www.opensource.org/licenses/mit-license.php MIT License
22: */
23:
24:
25:
26: class File
27: {
28: /**
29: * Folder object of the file
30: *
31: * @var \Liberty\Folder
32: * @link http://book.cakephp.org/3.0/en/core-libraries/file-folder.html
33: */
34: public $Folder;
35:
36:
37:
38: /**
39: * File name
40: *
41: * @var string
42: * http://book.cakephp.org/3.0/en/core-libraries/file-folder.html#Cake\Filesystem\File::$name
43: */
44: public $name;
45:
46:
47:
48: /**
49: * File info
50: *
51: * @var array
52: * http://book.cakephp.org/3.0/en/core-libraries/file-folder.html#Cake\Filesystem\File::$info
53: */
54: public $info = [];
55:
56:
57:
58: /**
59: * Holds the file handler resource if the file is opened
60: *
61: * @var resource
62: * http://book.cakephp.org/3.0/en/core-libraries/file-folder.html#Cake\Filesystem\File::$handle
63: */
64: public $handle;
65:
66:
67:
68: /**
69: * Enable locking for file reading and writing
70: *
71: * @var bool
72: * http://book.cakephp.org/3.0/en/core-libraries/file-folder.html#Cake\Filesystem\File::$lock
73: */
74: public $lock;
75:
76:
77:
78: /**
79: * Path property
80: *
81: * Current file's absolute path
82: *
83: * @var mixed
84: * http://book.cakephp.org/3.0/en/core-libraries/file-folder.html#Cake\Filesystem\File::$path
85: */
86: public $path;
87:
88:
89:
90:
91:
92: /**
93: * Constructor
94: *
95: * @param string $path Path to file
96: * @param bool $create Create file if it does not exist (if true)
97: * @param int $mode Mode to apply to the folder holding the file
98: * @link http://book.cakephp.org/3.0/en/core-libraries/file-folder.html#file-api
99: */
100: public function __construct($path, $create = false, $mode = 0755)
101: {
102: $this->Folder = new Folder(dirname($path), $create, $mode);
103: if (!is_dir($path)) {
104: $this->name = basename($path);
105: }
106: $this->pwd();
107: $create && !$this->exists() && $this->safe($path) && $this->create();
108: }
109:
110:
111:
112: /**
113: * Closes the current file if it is opened
114: */
115: public function __destruct()
116: {
117: $this->close();
118: }
119:
120:
121:
122:
123: /**
124: * Creates the file.
125: *
126: * @return bool Success
127: */
128: public function create()
129: {
130: $dir = $this->Folder->pwd();
131:
132: if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
133: if (touch($this->path)) {
134: return true;
135: }
136: }
137:
138: return false;
139: }
140:
141:
142:
143: /**
144: * Opens the current file with a given $mode
145: *
146: * @param string $mode A valid 'fopen' mode string (r|w|a ...)
147: * @param bool $force If true then the file will be re-opened even if its already opened, otherwise it won't
148: * @return bool True on success, false on failure
149: */
150: public function open($mode = 'r', $force = false)
151: {
152: if (!$force && is_resource($this->handle)) {
153: return true;
154: }
155: if ($this->exists() === false && $this->create() === false) {
156: return false;
157: }
158:
159: $this->handle = fopen($this->path, $mode);
160:
161: return is_resource($this->handle);
162: }
163:
164:
165:
166: /**
167: * Return the contents of this file as a string.
168: *
169: * @param string|bool $bytes where to start
170: * @param string $mode A `fread` compatible mode.
171: * @param bool $force If true then the file will be re-opened even if its already opened, otherwise it won't
172: * @return string|false string on success, false on failure
173: */
174: public function read($bytes = false, $mode = 'rb', $force = false)
175: {
176: if ($bytes === false && $this->lock === null) {
177: return file_get_contents($this->path);
178: }
179: if ($this->open($mode, $force) === false) {
180: return false;
181: }
182: if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
183: return false;
184: }
185: if (is_int($bytes)) {
186: return fread($this->handle, $bytes);
187: }
188:
189: $data = '';
190: while (!feof($this->handle)) {
191: $data .= fgets($this->handle, 4096);
192: }
193:
194: if ($this->lock !== null) {
195: flock($this->handle, LOCK_UN);
196: }
197: if ($bytes === false) {
198: $this->close();
199: }
200:
201: return trim($data);
202: }
203:
204:
205:
206: /**
207: * Sets or gets the offset for the currently opened file.
208: *
209: * @param int|bool $offset The $offset in bytes to seek. If set to false then the current offset is returned.
210: * @param int $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
211: * @return int|bool True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
212: */
213: public function offset($offset = false, $seek = SEEK_SET)
214: {
215: if ($offset === false) {
216: if (is_resource($this->handle)) {
217: return ftell($this->handle);
218: }
219: } elseif ($this->open() === true) {
220: return fseek($this->handle, $offset, $seek) === 0;
221: }
222:
223: return false;
224: }
225:
226:
227:
228:
229:
230: /**
231: * Prepares an ASCII string for writing. Converts line endings to the
232: * correct terminator for the current platform. If Windows, "\r\n" will be used,
233: * all other platforms will use "\n"
234: *
235: * @param string $data Data to prepare for writing.
236: * @param bool $forceWindows If true forces Windows new line string.
237: * @return string The with converted line endings.
238: */
239: public static function prepare($data, $forceWindows = false)
240: {
241: $lineBreak = "\n";
242: if (DIRECTORY_SEPARATOR === '\\' || $forceWindows === true) {
243: $lineBreak = "\r\n";
244: }
245:
246: return strtr($data, ["\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak]);
247: }
248:
249:
250:
251:
252:
253: /**
254: * Write given data to this file.
255: *
256: * @param string $data Data to write to this File.
257: * @param string $mode Mode of writing. {@link http://php.net/fwrite See fwrite()}.
258: * @param bool $force Force the file to open
259: * @return bool Success
260: */
261: public function write($data, $mode = 'w', $force = false)
262: {
263: $success = false;
264: if ($this->open($mode, $force) === true) {
265: if ($this->lock !== null && flock($this->handle, LOCK_EX) === false) {
266: return false;
267: }
268:
269: if (fwrite($this->handle, $data) !== false) {
270: $success = true;
271: }
272: if ($this->lock !== null) {
273: flock($this->handle, LOCK_UN);
274: }
275: }
276:
277: return $success;
278: }
279:
280: /**
281: * Append given data string to this file.
282: *
283: * @param string $data Data to write
284: * @param bool $force Force the file to open
285: * @return bool Success
286: */
287: public function append($data, $force = false)
288: {
289: return $this->write($data, 'a', $force);
290: }
291:
292: /**
293: * Closes the current file if it is opened.
294: *
295: * @return bool True if closing was successful or file was already closed, otherwise false
296: */
297: public function close()
298: {
299: if (!is_resource($this->handle)) {
300: return true;
301: }
302:
303: return fclose($this->handle);
304: }
305:
306: /**
307: * Deletes the file.
308: *
309: * @return bool Success
310: */
311: public function delete()
312: {
313: if (is_resource($this->handle)) {
314: fclose($this->handle);
315: $this->handle = null;
316: }
317: if ($this->exists()) {
318: return unlink($this->path);
319: }
320:
321: return false;
322: }
323:
324: /**
325: * Returns the file info as an array with the following keys:
326: *
327: * - dirname
328: * - basename
329: * - extension
330: * - filename
331: * - filesize
332: * - mime
333: *
334: * @return array File information.
335: */
336: public function info()
337: {
338: if (!$this->info) {
339: $this->info = pathinfo($this->path);
340: }
341: if (!isset($this->info['filename'])) {
342: $this->info['filename'] = $this->name();
343: }
344: if (!isset($this->info['filesize'])) {
345: $this->info['filesize'] = $this->size();
346: }
347: if (!isset($this->info['mime'])) {
348: $this->info['mime'] = $this->mime();
349: }
350:
351: return $this->info;
352: }
353:
354: /**
355: * Returns the file extension.
356: *
357: * @return string|false The file extension, false if extension cannot be extracted.
358: */
359: public function ext()
360: {
361: if (!$this->info) {
362: $this->info();
363: }
364: if (isset($this->info['extension'])) {
365: return $this->info['extension'];
366: }
367:
368: return false;
369: }
370:
371: /**
372: * Returns the file name without extension.
373: *
374: * @return string|false The file name without extension, false if name cannot be extracted.
375: */
376: public function name()
377: {
378: if (!$this->info) {
379: $this->info();
380: }
381: if (isset($this->info['extension'])) {
382: return basename($this->name, '.' . $this->info['extension']);
383: }
384: if ($this->name) {
385: return $this->name;
386: }
387:
388: return false;
389: }
390:
391: /**
392: * Makes file name safe for saving
393: *
394: * @param string|null $name The name of the file to make safe if different from $this->name
395: * @param string|null $ext The name of the extension to make safe if different from $this->ext
396: * @return string The extension of the file
397: */
398: public function safe($name = null, $ext = null)
399: {
400: if (!$name) {
401: $name = $this->name;
402: }
403: if (!$ext) {
404: $ext = $this->ext();
405: }
406:
407: return preg_replace("/(?:[^\w\.-]+)/", '_', basename($name, $ext));
408: }
409:
410: /**
411: * Get md5 Checksum of file with previous check of Filesize
412: *
413: * @param int|bool $maxsize in MB or true to force
414: * @return string|false md5 Checksum {@link http://php.net/md5_file See md5_file()}, or false in case of an error
415: */
416: public function md5($maxsize = 5)
417: {
418: if ($maxsize === true) {
419: return md5_file($this->path);
420: }
421:
422: $size = $this->size();
423: if ($size && $size < ($maxsize * 1024) * 1024) {
424: return md5_file($this->path);
425: }
426:
427: return false;
428: }
429:
430: /**
431: * Returns the full path of the file.
432: *
433: * @return string Full path to the file
434: */
435: public function pwd()
436: {
437: if ($this->path === null) {
438: $dir = $this->Folder->pwd();
439: if (is_dir($dir)) {
440: $this->path = $this->Folder->slashTerm($dir) . $this->name;
441: }
442: }
443:
444: return $this->path;
445: }
446:
447: /**
448: * Returns true if the file exists.
449: *
450: * @return bool True if it exists, false otherwise
451: */
452: public function exists()
453: {
454: $this->clearStatCache();
455: return (file_exists($this->path) && is_file($this->path));
456: }
457:
458: /**
459: * Returns the "chmod" (permissions) of the file.
460: *
461: * @return string|false Permissions for the file, or false in case of an error
462: */
463: public function perms()
464: {
465: if ($this->exists()) {
466: return substr(sprintf('%o', fileperms($this->path)), -4);
467: }
468:
469: return false;
470: }
471:
472: /**
473: * Returns the file size
474: *
475: * @return int|false Size of the file in bytes, or false in case of an error
476: */
477: public function size()
478: {
479: if ($this->exists()) {
480: return filesize($this->path);
481: }
482:
483: return false;
484: }
485:
486: /**
487: * Returns true if the file is writable.
488: *
489: * @return bool True if it's writable, false otherwise
490: */
491: public function writable()
492: {
493: return is_writable($this->path);
494: }
495:
496: /**
497: * Returns true if the File is executable.
498: *
499: * @return bool True if it's executable, false otherwise
500: */
501: public function executable()
502: {
503: return is_executable($this->path);
504: }
505:
506: /**
507: * Returns true if the file is readable.
508: *
509: * @return bool True if file is readable, false otherwise
510: */
511: public function readable()
512: {
513: return is_readable($this->path);
514: }
515:
516: /**
517: * Returns the file's owner.
518: *
519: * @return int|false The file owner, or false in case of an error
520: */
521: public function owner()
522: {
523: if ($this->exists()) {
524: return fileowner($this->path);
525: }
526:
527: return false;
528: }
529:
530: /**
531: * Returns the file's group.
532: *
533: * @return int|false The file group, or false in case of an error
534: */
535: public function group()
536: {
537: if ($this->exists()) {
538: return filegroup($this->path);
539: }
540:
541: return false;
542: }
543:
544: /**
545: * Returns last access time.
546: *
547: * @return int|false Timestamp of last access time, or false in case of an error
548: */
549: public function lastAccess()
550: {
551: if ($this->exists()) {
552: return fileatime($this->path);
553: }
554:
555: return false;
556: }
557:
558: /**
559: * Returns last modified time.
560: *
561: * @return int|false Timestamp of last modification, or false in case of an error
562: */
563: public function lastChange()
564: {
565: if ($this->exists()) {
566: return filemtime($this->path);
567: }
568:
569: return false;
570: }
571:
572:
573:
574: /**
575: * Returns the numbers of lines in a text.
576: *
577: * @return int|false The numbers of line in file, or false in case of an error
578: */
579: public function lines()
580: {
581: $lines = 0;
582: if( $this->open("rb") ) {
583: while(!feof($this->handle)){
584: fgets($this->handle);
585: $lines++;
586: }
587:
588: $this->close();
589: return $lines;
590: }
591: return false;
592: }
593:
594:
595:
596: /**
597: * Returns the current folder.
598: *
599: * @return \Liberty\Folder Current folder
600: */
601: public function folder()
602: {
603: return $this->Folder;
604: }
605:
606: /**
607: * Copy the File to $dest
608: *
609: * @param string $dest Destination for the copy
610: * @param bool $overwrite Overwrite $dest if exists
611: * @return bool Success
612: */
613: public function copy($dest, $overwrite = true)
614: {
615: if (!$this->exists() || is_file($dest) && !$overwrite) {
616: return false;
617: }
618:
619: return copy($this->path, $dest);
620: }
621:
622: /**
623: * Gets the mime type of the file. Uses the finfo extension if
624: * it's available, otherwise falls back to mime_content_type().
625: *
626: * @return false|string The mimetype of the file, or false if reading fails.
627: */
628: public function mime()
629: {
630: if (!$this->exists()) {
631: return false;
632: }
633: if (class_exists('finfo')) {
634: $finfo = new finfo(FILEINFO_MIME);
635: $type = $finfo->file($this->pwd());
636: if (!$type) {
637: return false;
638: }
639: list($type) = explode(';', $type);
640:
641: return $type;
642: }
643: if (function_exists('mime_content_type')) {
644: return mime_content_type($this->pwd());
645: }
646:
647: return false;
648: }
649:
650: /**
651: * Clear PHP's internal stat cache
652: *
653: * @param bool $all Clear all cache or not. Passing false will clear
654: * the stat cache for the current path only.
655: * @return void
656: */
657: public function clearStatCache($all = false)
658: {
659: if ($all === false) {
660: clearstatcache(true, $this->path);
661: }
662:
663: clearstatcache();
664: }
665:
666: /**
667: * Searches for a given text and replaces the text if found.
668: *
669: * @param string|array $search Text(s) to search for.
670: * @param string|array $replace Text(s) to replace with.
671: * @return bool Success
672: */
673: public function replaceText($search, $replace)
674: {
675: if (!$this->open('r+')) {
676: return false;
677: }
678:
679: if ($this->lock !== null && flock($this->handle, LOCK_EX) === false) {
680: return false;
681: }
682:
683: $replaced = $this->write(str_replace($search, $replace, $this->read()), 'w', true);
684:
685: if ($this->lock !== null) {
686: flock($this->handle, LOCK_UN);
687: }
688: $this->close();
689:
690: return $replaced;
691: }
692: }
693: