AloFramework documentation
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
  • Download

Namespaces

  • Alo
    • Cache
    • CLI
    • Controller
    • Db
    • Exception
    • FileSystem
    • Session
    • Statics
    • Test
    • Validators
    • Windows
  • Controller
  • None
  • PHP

Classes

  • AbstractFileSystem
  • File
  1 <?php
  2 
  3    namespace Alo\FileSystem;
  4 
  5    use Alo\Exception\FileSystemException as FE;
  6 
  7    if(!defined('GEN_START')) {
  8       http_response_code(404);
  9       die();
 10    }
 11 
 12    /**
 13     * Object-oriented file handler
 14     *
 15     * @author Arturas Molcanovas <a.molcanovas@gmail.com>
 16     */
 17    class File extends AbstractFileSystem {
 18 
 19       /**
 20        * Open for reading only; place the file pointer at the beginning of the file.
 21        *
 22        * @var string
 23        */
 24       const M_READ_EXISTING_BEGIN = 'r';
 25 
 26       /**
 27        * Open for reading and writing; place the file pointer at the beginning of the file.
 28        *
 29        * @var string
 30        */
 31       const M_RW_EXISTING_BEGIN = 'r+';
 32 
 33       /**
 34        * Open for writing only; place the file pointer at the beginning of the file
 35        * and truncate the file to zero length. If the file does not exist, attempt
 36        * to create it.
 37        *
 38        * @var string
 39        */
 40       const M_WRITE_TRUNCATE_BEGIN = 'w';
 41 
 42       /**
 43        * Open for reading and writing; place the file pointer at the beginning of
 44        * the file and truncate the file to zero length. If the file does not exist,
 45        * attempt to create it.
 46        *
 47        * @var string
 48        */
 49       const M_RW_TRUNCATE_BEGIN = 'w+';
 50 
 51       /**
 52        * Open for writing only; place the file pointer at the end of the file. If
 53        * the file does not exist, attempt to create it.
 54        *
 55        * @var string
 56        */
 57       const M_WRITE_END = 'a';
 58 
 59       /**
 60        * Open for reading and writing; place the file pointer at the end of the file.
 61        * If the file does not exist, attempt to create it.
 62        *
 63        * @var string
 64        */
 65       const M_RW_END = 'a+';
 66 
 67       /**
 68        * Create and open for writing only; place the file pointer at the beginning
 69        * of the file. If the file already exists, the fopen() call will fail by
 70        * returning FALSE and generating an error of level E_WARNING. If the file
 71        * does not exist, attempt to create it. This is equivalent to specifying
 72        * O_EXCL|O_CREAT flags for the underlying open(2) system call.
 73        *
 74        * @var string
 75        */
 76       const M_WRITE_NONEXIST_BEGIN = 'x';
 77 
 78       /**
 79        * Create and open for reading and writing; otherwise it has the same behavior as
 80        * M_WRITE_NONEXIST_BEGIN
 81        *
 82        * @var string
 83        * @see self::M_WRITE_NONEXIST_BEGIN
 84        */
 85       const M_RW_NONEXIST_BEGIN = 'x+';
 86 
 87       /**
 88        * Open the file for writing only. If the file does not exist, it is created.
 89        * If it exists, it is neither truncated (as opposed to M_WRITE_TRUNCATE_BEGIN), nor the call to
 90        * this function fails (as is the case with M_WRITE_NONEXIST_BEGIN). The file pointer is positioned
 91        * on the beginning of the file. This may be useful if it's desired to get an
 92        * advisory lock (see flock()) before attempting to modify the file, as using
 93        * M_WRITE_NONEXIST_BEGIN could truncate the file before the lock was
 94        * obtained (if truncation is desired, ftruncate() can be used after the lock
 95        * is requested).
 96        *
 97        * @var string
 98        * @see self::M_WRITE_NONEXIST_BEGIN
 99        * @see self::M_WRITE_TRUNCATE_BEGIN
100        */
101       const M_WRITE_BEGIN = 'c';
102 
103       /**
104        * Open the file for reading and writing; otherwise it has the same behavior as
105        * M_WRITE_BEGIN.
106        *
107        * @var string
108        * @see self::M_WRITE_BEGIN;
109        */
110       const M_RW_BEGIN = 'c+';
111 
112       /**
113        * Whether GZIP is installed
114        *
115        * @var boolean
116        */
117       protected static $gz;
118 
119       /**
120        * The file content
121        *
122        * @var string
123        */
124       protected $content;
125 
126       /**
127        * The file name
128        *
129        * @var string
130        */
131       protected $name;
132 
133       /**
134        * The file directory
135        *
136        * @var string
137        */
138       protected $dir;
139 
140       /**
141        * The full file path. Updates with every setName & setDir
142        *
143        * @var string
144        * @see self::setName()
145        * @see self::setDir()
146        */
147       protected $filepath;
148 
149       /**
150        * Instantiates the class
151        *
152        * @author Art <a.molcanovas@gmail.com>
153        */
154       function __construct() {
155          parent::__construct();
156 
157          $this->dir = DIR_TMP;
158          self::$gz  = function_exists('gzencode');
159       }
160 
161       /**
162        * Instantiates the class
163        *
164        * @author Art <a.molcanovas@gmail.com>
165        *
166        * @return File
167        */
168       static function File() {
169          return new File();
170       }
171 
172       /**
173        * Converts a filesize for display
174        *
175        * @author Art <a.molcanovas@gmail.com>
176        *
177        * @param int $size The file size in bytes
178        *
179        * @return string The file size in its largest form, e.g. 1024 bytes become 1KB;
180        */
181       static function convert_size($size) {
182          if(is_numeric($size)) {
183             $size = (int)$size;
184 
185             if($size < 1024) {
186                return $size . 'B';
187             } elseif($size < 1048576) {
188                return round($size / 1024, 2) . 'KB';
189             } elseif($size < 1099511627776) {
190                return round($size / 1048576, 2) . 'MB';
191             } elseif($size < 1125899906842624) {
192                return round($size / 1099511627776, 2) . 'GB';
193             } else {
194                return round($size / 1125899906842624, 2) . 'TB';
195             }
196          }
197 
198          return $size;
199       }
200 
201       /**
202        * Appends the file contents on the disc
203        *
204        * @author Art <a.molcanovas@gmail.com>
205        * @throws FE When fopen fails
206        * @return boolean
207        */
208       function append() {
209          \Log::debug('Appended the file');
210 
211          return $this->doWrite(self::M_WRITE_END);
212       }
213 
214       /**
215        * Performs a write operation
216        *
217        * @author Art <a.molcanovas@gmail.com>
218        *
219        * @param string $mode The write mode - see class constants
220        *
221        * @return File
222        * @throws FE When fopen fails
223        */
224       protected function doWrite($mode) {
225          $this->checkParams();
226          if(!$fp = @fopen($this->filepath, $mode)) {
227             throw new FE('Failed to fopen file ' . $this->filepath, FE::E_FOPEN_FAIL);
228          } else {
229             flock($fp, LOCK_EX);
230             fwrite($fp, $this->content);
231             flock($fp, LOCK_UN);
232             fclose($fp);
233             \Log::debug('Wrote ' . $this->filepath . ' contents');
234 
235             return $this;
236          }
237       }
238 
239       /**
240        * Checks if the dir and name are set
241        *
242        * @author Art <a.molcanovas@gmail.com>
243        * @return File
244        * @throws FE When the file path is not set
245        */
246       protected function checkParams() {
247          if(!$this->dir || !$this->name) {
248             throw new FE('File path not set', FE::E_PATH_NOT_SET);
249          }
250 
251          return $this;
252       }
253 
254       /**
255        * Gets the file extension based on the currently set filename
256        *
257        * @author Art <a.molcanovas@gmail.com>
258        *
259        * @param int     $depth            The depth to search for, e.g. if the file name is
260        *                                  foo.tar.gz, depth=1 would return "gz" while depth=2 would return .tar.gz
261        * @param boolean $only_that_member Only effective if $depth > 1. If FALSE
262        *                                  and the extension is tar.gz, will return "tar.gz", if TRUE, will return "tar".
263        *
264        * @return string
265        * @uses   self::get_extension()
266        */
267       function getExtension($depth = 1, $only_that_member = false) {
268          return $this->name ? self::get_extension($this->name, $depth, $only_that_member) : null;
269       }
270 
271       /**
272        * Gets the file extension based on name
273        *
274        * @author Art <a.molcanovas@gmail.com>
275        *
276        * @param string  $filename         The file name
277        * @param int     $depth            The depth to search for, e.g. if the file name is
278        *                                  foo.tar.gz, depth=1 would return "gz" while depth=2 would return .tar.gz
279        * @param boolean $only_that_member Only effective if $depth > 1. If FALSE
280        *                                  and the extension is tar.gz, will return "tar.gz", if TRUE, will return "tar".
281        *
282        * @return string
283        */
284       static function get_extension($filename, $depth = 1, $only_that_member = false) {
285          $exploded = explode('.', strtolower($filename));
286          if(!is_numeric($depth) || $depth < 1) {
287             $depth = 1;
288          }
289 
290          return $only_that_member && $depth > 1 ? get($exploded[count($exploded) - $depth]) :
291             implode('.', array_slice($exploded, $depth * -1));
292       }
293 
294       /**
295        * Alias for self::unlink()
296        *
297        * @author Art <a.molcanovas@gmail.com>
298        * @uses   self::unlink()
299        * @return boolean
300        */
301       function delete() {
302          return $this->unlink();
303       }
304 
305       /**
306        * Deletes the file
307        *
308        * @author Art <a.molcanovas@gmail.com>
309        * @throws FE When the file path is not set
310        * @return boolean
311        */
312       function unlink() {
313          $this->checkParams();
314          if(unlink($this->filepath)) {
315             \Log::debug('Deleted ' . $this->filepath);
316 
317             return true;
318          } else {
319             \Log::error('Failed to delete ' . $this->filepath);
320 
321             return false;
322          }
323       }
324 
325       /**
326        * Gzip-encodes the fetched content
327        *
328        * @author Art <a.molcanovas@gmail.com>
329        *
330        * @param int $level Compression strength (0-9)
331        *
332        * @throws FE When the file doesn't exist
333        * @return boolean
334        */
335       function gzipContent($level = 9) {
336          if(self::$gz) {
337             if(!$this->content) {
338                if($this->filepath && $this->fileExists()) {
339                   \Log::debug('File contents not present for gzip: reading them');
340                   $this->read();
341                } else {
342                   \Log::error('Failed to gzip file contents: file not found');
343                }
344             }
345 
346             if($this->content) {
347                $this->content = gzencode($this->content, $level);
348                \Log::debug('Gzipped file contents');
349 
350                return true;
351             } else {
352                \Log::error('Failed to gzip file contents: content not present');
353             }
354          } else {
355             \Log::error('Failed to gzip file contents: extension not loaded');
356          }
357 
358          return false;
359       }
360 
361       /**
362        * Checks whether the file exists at the set path
363        *
364        * @author Art <a.molcanovas@gmail.com>
365        * @throws FE When the file path is not set
366        * @return boolean
367        */
368       function fileExists() {
369          $this->checkParams();
370 
371          return file_exists($this->filepath);
372       }
373 
374       /**
375        * Reads the file contents into $this->content
376        *
377        * @author Art <a.molcanovas@gmail.com>
378        * @return boolean
379        * @throws FE When the file doesn't exist
380        */
381       function read() {
382          $this->checkParams();
383          if(file_exists($this->filepath)) {
384             $this->content = file_get_contents($this->filepath, true);
385             \Log::debug('Read ' . $this->filepath . ' contents');
386 
387             return true;
388          } else {
389             throw new FE($this->filepath . ' doesn\'t exist', FE::E_FILE_NOT_EXISTS);
390          }
391       }
392 
393       /**
394        * Gzip-decodes the fetched content
395        *
396        * @author Art <a.molcanovas@gmail.com>
397        * @throws FE When the file doesn't exist
398        * @return boolean
399        */
400       function ungzipContent() {
401          if(self::$gz) {
402             if(!$this->content) {
403                if($this->filepath && $this->fileExists()) {
404                   \Log::debug('File contents not present for ungzip: reading them');
405                   $this->read();
406                } else {
407                   \Log::error('Failed to ungzip file contents: file not found');
408                }
409             }
410 
411             if($this->content) {
412                $this->content = gzdecode($this->content);
413                \Log::debug('Ungzipped file contents');
414 
415                return true;
416             } else {
417                \Log::error('Failed to ungzip file contents: content not present');
418             }
419          } else {
420             \Log::error('Failed to ungzip file contents: extension not loaded');
421          }
422 
423          return false;
424       }
425 
426       /**
427        * Overwrites the file contents on the disc
428        *
429        * @author Art <a.molcanovas@gmail.com>
430        * @throws FE When the file path is not set
431        * @return File
432        */
433       function write() {
434          \Log::debug('Overwriting file contents');
435 
436          return $this->doWrite(self::M_WRITE_TRUNCATE_BEGIN);
437       }
438 
439       /**
440        * Returns a string representation of the object data
441        *
442        * @author Art <a.molcanovas@gmail.com>
443        * @return string
444        */
445       function __toString() {
446          return \lite_debug($this);
447       }
448 
449       /**
450        * Returns the file's path in the system
451        *
452        * @author Art <a.molcanovas@gmail.com>
453        * @return string
454        */
455       function getFilePath() {
456          return $this->filepath;
457       }
458 
459       /**
460        * If no argument is passed, gets the file name, otherwise sets it
461        *
462        * @author Art <a.molcanovas@gmail.com>
463        *
464        * @param string $name The name
465        *
466        * @return File|string
467        * @throws FE When the name is invalid
468        */
469       function name($name = '') {
470          if($name === '') {
471             return $this->name;
472          } elseif(is_scalar($name)) {
473             $this->replace($name);
474             $this->name = trim($name, DIRECTORY_SEPARATOR);
475             $this->updatePath();
476             \Log::debug('File name set to ' . $this->name);
477 
478          } else {
479             throw new FE('File name invalid', FE::E_NAME_INVALID);
480          }
481 
482          return $this;
483       }
484 
485       /**
486        * Updates the file path when the directory or file name are changed
487        *
488        * @author Art <a.molcanovas@gmail.com>
489        * @return File
490        */
491       protected function updatePath() {
492          $this->filepath = $this->dir . DIRECTORY_SEPARATOR . $this->name;
493 
494          return $this;
495       }
496 
497       /**
498        * If no argument is passed, gets the directory name, otherwise sets it
499        *
500        * @author Art <a.molcanovas@gmail.com>
501        *
502        * @param string $dir The directory
503        *
504        * @return File|string
505        * @throws FE When the name is invalid
506        */
507       function dir($dir = '') {
508          if($dir === '') {
509             return $this->dir;
510          } elseif(is_scalar($dir)) {
511             $this->replace($dir);
512             $this->dir = rtrim($dir, DIRECTORY_SEPARATOR);
513             $this->updatePath();
514             \Log::debug('Directory name set to ' . $dir);
515 
516          } else {
517             throw new FE('Directory name invalid', FE::E_NAME_INVALID);
518          }
519 
520          return $this;
521       }
522 
523       /**
524        * Scans the directory for files
525        *
526        * @author Art <a.molcanovas@gmail.com>
527        * @return array
528        */
529       function scandir() {
530          return $this->dir ? scandir($this->dir) : [];
531       }
532 
533       /**
534        * If no argument is passed, gets the currently set content, otherwise sets it
535        *
536        * @author Art <a.molcanovas@gmail.com>
537        *
538        * @param string $content Content to set
539        *
540        * @throws FE When content is not scalar
541        * @return File|string
542        */
543       function content($content = '~none~') {
544          if($content === '~none~') {
545             return $this->content;
546          } elseif(is_scalar($content)) {
547             \Log::debug('Overwrote file contents');
548             $this->content = $content;
549 
550          } else {
551             throw new FE('Content is not scalar!', FE::E_CONTENT_INVALID);
552          }
553 
554          return $this;
555       }
556 
557       /**
558        * Clears the file content
559        *
560        * @author Art <a.molcanovas@gmail.com>
561        * @return File
562        */
563       function clearContent() {
564          \Log::debug('Cleared file contents');
565          $this->content = null;
566 
567          return $this;
568       }
569 
570       /**
571        * Appends the file content
572        *
573        * @author Art <a.molcanovas@gmail.com>
574        *
575        * @param string $c The content
576        *
577        * @return File
578        */
579       function addContent($c) {
580          \Log::debug('Appended file contents');
581          $this->content .= $c;
582 
583          return $this;
584       }
585 
586    }
AloFramework documentation API documentation generated by ApiGen 2.8.0