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

Namespaces

  • Alo
    • Cache
    • CLI
    • Controller
    • Db
    • Exception
    • FileSystem
    • Session
    • 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     } else {
 10 
 11         /**
 12          * Object-oriented file handler
 13          *
 14          * @author Arturas Molcanovas <a.molcanovas@gmail.com>
 15          */
 16         class File extends AbstractFileSystem {
 17 
 18             /**
 19              * Open for reading only; place the file pointer at the beginning of the file.
 20              *
 21              * @var string
 22              */
 23             const M_READ_EXISTING_BEGIN = 'r';
 24 
 25             /**
 26              * Open for reading and writing; place the file pointer at the beginning of the file.
 27              *
 28              * @var string
 29              */
 30             const M_RW_EXISTING_BEGIN = 'r+';
 31 
 32             /**
 33              * Open for writing only; place the file pointer at the beginning of the file
 34              * and truncate the file to zero length. If the file does not exist, attempt
 35              * to create it.
 36              *
 37              * @var string
 38              */
 39             const M_WRITE_TRUNCATE_BEGIN = 'w';
 40 
 41             /**
 42              * Open for reading and writing; place the file pointer at the beginning of
 43              * the file and truncate the file to zero length. If the file does not exist,
 44              * attempt to create it.
 45              *
 46              * @var string
 47              */
 48             const M_RW_TRUNCATE_BEGIN = 'w+';
 49 
 50             /**
 51              * Open for writing only; place the file pointer at the end of the file. If
 52              * the file does not exist, attempt to create it.
 53              *
 54              * @var string
 55              */
 56             const M_WRITE_END = 'a';
 57 
 58             /**
 59              * Open for reading and writing; place the file pointer at the end of the file.
 60              * If the file does not exist, attempt to create it.
 61              *
 62              * @var string
 63              */
 64             const M_RW_END = 'a+';
 65 
 66             /**
 67              * Create and open for writing only; place the file pointer at the beginning
 68              * of the file. If the file already exists, the fopen() call will fail by
 69              * returning FALSE and generating an error of level E_WARNING. If the file
 70              * does not exist, attempt to create it. This is equivalent to specifying
 71              * O_EXCL|O_CREAT flags for the underlying open(2) system call.
 72              *
 73              * @var string
 74              */
 75             const M_WRITE_NONEXIST_BEGIN = 'x';
 76 
 77             /**
 78              * Create and open for reading and writing; otherwise it has the same behavior as
 79              * M_WRITE_NONEXIST_BEGIN
 80              *
 81              * @var string
 82              * @see self::M_WRITE_NONEXIST_BEGIN
 83              */
 84             const M_RW_NONEXIST_BEGIN = 'x+';
 85 
 86             /**
 87              * Open the file for writing only. If the file does not exist, it is created.
 88              * If it exists, it is neither truncated (as opposed to M_WRITE_TRUNCATE_BEGIN), nor the call to
 89              * this function fails (as is the case with M_WRITE_NONEXIST_BEGIN). The file pointer is positioned
 90              * on the beginning of the file. This may be useful if it's desired to get an
 91              * advisory lock (see flock()) before attempting to modify the file, as using
 92              * M_WRITE_NONEXIST_BEGIN could truncate the file before the lock was
 93              * obtained (if truncation is desired, ftruncate() can be used after the lock
 94              * is requested).
 95              *
 96              * @var string
 97              * @see self::M_WRITE_NONEXIST_BEGIN
 98              * @see self::M_WRITE_TRUNCATE_BEGIN
 99              */
100             const M_WRITE_BEGIN = 'c';
101 
102             /**
103              * Open the file for reading and writing; otherwise it has the same behavior as
104              * M_WRITE_BEGIN.
105              *
106              * @var string
107              * @see self::M_WRITE_BEGIN;
108              */
109             const M_RW_BEGIN = 'c+';
110 
111             /**
112              * Whether GZIP is installed
113              *
114              * @var boolean
115              */
116             protected static $gz;
117 
118             /**
119              * The file content
120              *
121              * @var string
122              */
123             protected $content;
124 
125             /**
126              * The file name
127              *
128              * @var string
129              */
130             protected $name;
131 
132             /**
133              * The file directory
134              *
135              * @var string
136              */
137             protected $dir;
138 
139             /**
140              * The full file path. Updates with every setName & setDir
141              *
142              * @var string
143              * @see self::setName()
144              * @see self::setDir()
145              */
146             protected $filepath;
147 
148             /**
149              * Instantiates the class
150              *
151              * @author Art <a.molcanovas@gmail.com>
152              */
153             function __construct() {
154                 parent::__construct();
155 
156                 $this->dir = DIR_TMP;
157                 self::$gz  = function_exists('gzencode');
158             }
159 
160             /**
161              * Instantiates the class
162              *
163              * @author Art <a.molcanovas@gmail.com>
164              *
165              * @return File
166              */
167             static function file() {
168                 return new File();
169             }
170 
171             /**
172              * Converts a filesize for display
173              *
174              * @author Art <a.molcanovas@gmail.com>
175              *
176              * @param int $size The file size in bytes
177              *
178              * @return string The file size in its largest form, e.g. 1024 bytes become 1KB;
179              */
180             static function convertSize($size) {
181                 if (is_numeric($size)) {
182                     $size = (int)$size;
183 
184                     if ($size < 1024) {
185                         return $size . 'B';
186                     } elseif ($size < 1048576) {
187                         return round($size / 1024, 2) . 'KB';
188                     } elseif ($size < 1099511627776) {
189                         return round($size / 1048576, 2) . 'MB';
190                     } elseif ($size < 1125899906842624) {
191                         return round($size / 1099511627776, 2) . 'GB';
192                     } else {
193                         return round($size / 1125899906842624, 2) . 'TB';
194                     }
195                 }
196 
197                 return $size;
198             }
199 
200             /**
201              * Appends the file contents on the disc
202              *
203              * @author Art <a.molcanovas@gmail.com>
204              * @throws FE When fopen fails
205              * @return boolean
206              */
207             function append() {
208                 \Log::debug('Appended the file');
209 
210                 return $this->doWrite(self::M_WRITE_END);
211             }
212 
213             /**
214              * Performs a write operation
215              *
216              * @author Art <a.molcanovas@gmail.com>
217              *
218              * @param string $mode The write mode - see class constants
219              *
220              * @return File
221              * @throws FE When fopen fails
222              */
223             protected function doWrite($mode) {
224                 $this->checkParams();
225 
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 $onlyThatMember   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, $onlyThatMember = false) {
268                 return $this->name ? self::getExtensionStatically($this->name, $depth, $onlyThatMember) : 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 $onlyThatMember   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 getExtensionStatically($filename, $depth = 1, $onlyThatMember = false) {
285                 $exploded = explode('.', strtolower($filename));
286                 if (!is_numeric($depth) || $depth < 1) {
287                     $depth = 1;
288                 }
289 
290                 return $onlyThatMember && $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 \debugLite($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         }
587     }
588 
AloFramework documentation API documentation generated byApiGen 2.8.0