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

Namespaces

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

Classes

  • Cron
  • cURL
  • Email
  • File
  • Handler
  • Profiler
  • SFTP
  1 <?php
  2 
  3    namespace Alo;
  4 
  5    use Alo\Exception\FileException 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 {
 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        * The file content
114        *
115        * @var string
116        */
117       protected $content;
118 
119       /**
120        * The file name
121        *
122        * @var string
123        */
124       protected $name;
125 
126       /**
127        * The file directory
128        *
129        * @var string
130        */
131       protected $dir;
132 
133       /**
134        * Replacements for placeholders
135        *
136        * @var array
137        */
138       protected $replace;
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        * Whether GZIP is installed
151        *
152        * @var boolean
153        */
154       protected static $gz;
155 
156       /**
157        * Instantiates the class
158        *
159        * @author Art <a.molcanovas@gmail.com>
160        */
161       function __construct() {
162          $time = time();
163          $this->replace = [
164             'search'  => [
165                '{timestamp}',
166                '{datetime}',
167                '{date}',
168                '{time}',
169                '{year}',
170                '{month}',
171                '{day}',
172                '{weekday}'
173             ],
174             'replace' => [
175                $time,
176                date('Y-m-d H.i.s', $time),
177                date('Y-m-d', $time),
178                date('H.i.s', $time),
179                date('Y', $time),
180                date('m', $time),
181                date('d', $time),
182                date('l', $time)
183             ]
184          ];
185 
186          $this->dir = DIR_TMP;
187          self::$gz = function_exists('gzencode');
188       }
189 
190       /**
191        * Converts a filesize for display
192        *
193        * @author Art <a.molcanovas@gmail.com>
194        * @param int $size The file size in bytes
195        * @return string The file size in its largest form, e.g. 1024 bytes become 1KB;
196        */
197       static function convert_size($size) {
198          if (is_numeric($size)) {
199             $size = (int)$size;
200 
201             if ($size < 1024) {
202                return $size . 'B';
203             } elseif ($size < 1048576) {
204                return round($size / 1024, 2) . 'KB';
205             } elseif ($size < 1099511627776) {
206                return round($size / 1048576, 2) . 'MB';
207             } elseif ($size < 1125899906842624) {
208                return round($size / 1099511627776, 2) . 'GB';
209             } else {
210                return round($size / 1125899906842624, 2) . 'TB';
211             }
212          }
213 
214          return $size;
215       }
216 
217       /**
218        * Appends the file contents on the disc
219        *
220        * @author Art <a.molcanovas@gmail.com>
221        * @return boolean
222        */
223       function append() {
224          \Log::debug('Appended the file');
225 
226          return $this->doWrite(self::M_WRITE_END);
227       }
228 
229       /**
230        * Perform placeholder replacement operations
231        *
232        * @author Art <a.molcanovas@gmail.com>
233        * @param string $subject The string to perform operations in
234        */
235       protected function replace(&$subject) {
236          $subject = str_ireplace($this->replace['search'], $this->replace['replace'], $subject);
237       }
238 
239       /**
240        * Gets the file extension based on name
241        *
242        * @author Art <a.molcanovas@gmail.com>
243        * @param string  $filename         The file name
244        * @param int     $depth            The depth to search for, e.g. if the file name is
245        *                                  foo.tar.gz, depth=1 would return "gz" while depth=2 would return .tar.gz
246        * @param boolean $only_that_member Only effective if $depth > 1. If FALSE
247        *                                  and the extension is tar.gz, will return "tar.gz", if TRUE, will return "tar".
248        * @return string
249        */
250       static function get_extension($filename, $depth = 1, $only_that_member = false) {
251          $exploded = explode('.', strtolower($filename));
252          if (!is_numeric($depth) || $depth < 1) {
253             $depth = 1;
254          }
255 
256          return $only_that_member && $depth > 1 ? get($exploded[count($exploded) - $depth]) : implode('.', array_slice($exploded, $depth * -1));
257       }
258 
259       /**
260        * Gets the file extension based on the currently set filename
261        *
262        * @author Art <a.molcanovas@gmail.com>
263        * @param int     $depth            The depth to search for, e.g. if the file name is
264        *                                  foo.tar.gz, depth=1 would return "gz" while depth=2 would return .tar.gz
265        * @param boolean $only_that_member Only effective if $depth > 1. If FALSE
266        *                                  and the extension is tar.gz, will return "tar.gz", if TRUE, will return "tar".
267        * @return string
268        * @uses   self::get_extension()
269        */
270       function getExtension($depth = 1, $only_that_member = false) {
271          return $this->name ? self::get_extension($this->name, $depth, $only_that_member) : null;
272       }
273 
274       /**
275        * Alias for self::unlink()
276        *
277        * @author Art <a.molcanovas@gmail.com>
278        * @uses   self::unlink()
279        * @return boolean
280        */
281       function delete() {
282          return $this->unlink();
283       }
284 
285       /**
286        * Deletes the file
287        *
288        * @author Art <a.molcanovas@gmail.com>
289        * @return boolean
290        */
291       function unlink() {
292          $this->checkParams();
293          if (unlink($this->filepath)) {
294             \Log::debug('Deleted ' . $this->filepath);
295 
296             return true;
297          } else {
298             \Log::error('Failed to delete ' . $this->filepath);
299 
300             return false;
301          }
302       }
303 
304       /**
305        * Checks whether the file exists at the set path
306        *
307        * @author Art <a.molcanovas@gmail.com>
308        * @return boolean
309        */
310       function fileExists() {
311          $this->checkParams();
312 
313          return file_exists($this->filepath);
314       }
315 
316       /**
317        * Gzip-encodes the fetched content
318        *
319        * @author Art <a.molcanovas@gmail.com>
320        * @param int $level Compression strength (0-9)
321        * @return boolean
322        */
323       function gzipContent($level = 9) {
324          if (self::$gz) {
325             if (!$this->content) {
326                if ($this->filepath && $this->fileExists()) {
327                   \Log::debug('File contents not present for gzip: reading them');
328                   $this->read();
329                } else {
330                   \Log::error('Failed to gzip file contents: file not found');
331                }
332             }
333 
334             if ($this->content) {
335                $this->content = gzencode($this->content, $level);
336                \Log::debug('Gzipped file contents');
337 
338                return true;
339             } else {
340                \Log::error('Failed to gzip file contents: content not present');
341             }
342          } else {
343             \Log::error('Failed to gzip file contents: extension not loaded');
344          }
345 
346          return false;
347       }
348 
349       /**
350        * Gzip-decodes the fetched content
351        *
352        * @author Art <a.molcanovas@gmail.com>
353        * @return boolean
354        */
355       function ungzipContent() {
356          if (self::$gz) {
357             if (!$this->content) {
358                if ($this->filepath && $this->fileExists()) {
359                   \Log::debug('File contents not present for ungzip: reading them');
360                   $this->read();
361                } else {
362                   \Log::error('Failed to ungzip file contents: file not found');
363                }
364             }
365 
366             if ($this->content) {
367                $this->content = gzdecode($this->content);
368                \Log::debug('Ungzipped file contents');
369 
370                return true;
371             } else {
372                \Log::error('Failed to ungzip file contents: content not present');
373             }
374          } else {
375             \Log::error('Failed to ungzip file contents: extension not loaded');
376          }
377 
378          return false;
379       }
380 
381       /**
382        * Overwrites the file contents on the disc
383        *
384        * @author Art <a.molcanovas@gmail.com>
385        * @return File
386        */
387       function write() {
388          \Log::debug('Overwriting file contents');
389 
390          return $this->doWrite(self::M_WRITE_TRUNCATE_BEGIN);
391       }
392 
393       /**
394        * Performs a write operation
395        *
396        * @author Art <a.molcanovas@gmail.com>
397        * @param string $mode The write mode - see class constants
398        * @return File
399        * @throws FE When fopen fails
400        */
401       protected function doWrite($mode) {
402          $this->checkParams();
403          if (!$fp = @fopen($this->filepath, $mode)) {
404             throw new FE('Failed to fopen file ' . $this->filepath, FE::E_FOPEN_FAIL);
405          } else {
406             flock($fp, LOCK_EX);
407             fwrite($fp, $this->content);
408             flock($fp, LOCK_UN);
409             fclose($fp);
410             \Log::debug('Wrote ' . $this->filepath . ' contents');
411 
412             return $this;
413          }
414       }
415 
416       /**
417        * Checks if the dir and name are set
418        *
419        * @author Art <a.molcanovas@gmail.com>
420        * @return File
421        * @throws FE When the file path is not set
422        */
423       protected function checkParams() {
424          if (!$this->dir || !$this->name) {
425             throw new FE('File path not set', FE::E_PATH_NOT_SET);
426          }
427 
428          return $this;
429       }
430 
431       /**
432        * Reads the file contents into $this->content
433        *
434        * @author Art <a.molcanovas@gmail.com>
435        * @return boolean
436        * @throws FE When the file doesn't exist
437        */
438       function read() {
439          $this->checkParams();
440          if (file_exists($this->filepath)) {
441             $this->content = file_get_contents($this->filepath, true);
442             \Log::debug('Read ' . $this->filepath . ' contents');
443 
444             return true;
445          } else {
446             throw new FE($this->filepath . ' doesn\'t exist', FE::E_FILE_NOT_EXISTS);
447          }
448       }
449 
450       /**
451        * Returns a string representation of the object data
452        *
453        * @author Art <a.molcanovas@gmail.com>
454        * @return string
455        */
456       function __toString() {
457          return \lite_debug($this);
458       }
459 
460       /**
461        * Updates the file path when the directory or file name are changed
462        *
463        * @author Art <a.molcanovas@gmail.com>
464        * @return File
465        */
466       protected function updatePath() {
467          $this->filepath = $this->dir . DIRECTORY_SEPARATOR . $this->name;
468 
469          return $this;
470       }
471 
472       /**
473        * Returns the file's path in the system
474        *
475        * @author Art <a.molcanovas@gmail.com>
476        * @return string
477        */
478       function getFilePath() {
479          return $this->filepath;
480       }
481 
482       /**
483        * If no argument is passed, gets the file name, otherwise sets it
484        *
485        * @author Art <a.molcanovas@gmail.com>
486        * @param string $name The name
487        * @return File|string
488        * @throws FE When the name is invalid
489        */
490       function name($name = '') {
491          if ($name === '') {
492             return $this->name;
493          } elseif (is_scalar($name)) {
494             $this->replace($name);
495             $this->name = trim($name, DIRECTORY_SEPARATOR);
496             $this->updatePath();
497             \Log::debug('File name set to ' . $this->name);
498 
499          } else {
500             throw new FE('File name invalid', FE::E_NAME_INVALID);
501          }
502 
503          return $this;
504       }
505 
506       /**
507        * If no argument is passed, gets the directory name, otherwise sets it
508        *
509        * @author Art <a.molcanovas@gmail.com>
510        * @param string $dir The directory
511        * @return File|string
512        * @throws FE When the name is invalid
513        */
514       function dir($dir = '') {
515          if ($dir === '') {
516             return $this->dir;
517          } elseif (is_scalar($dir)) {
518             $this->replace($dir);
519             $this->dir = rtrim($dir, DIRECTORY_SEPARATOR);
520             $this->updatePath();
521             \Log::debug('Directory name set to ' . $dir);
522 
523          } else {
524             throw new FE('Directory name invalid', FE::E_NAME_INVALID);
525          }
526 
527          return $this;
528       }
529 
530       /**
531        * Scans the directory for files
532        *
533        * @author Art <a.molcanovas@gmail.com>
534        * @return array
535        */
536       function scandir() {
537          return $this->dir ? scandir($this->dir) : [];
538       }
539 
540       /**
541        * If no argument is passed, gets the currently set content, otherwise sets it
542        *
543        * @author Art <a.molcanovas@gmail.com>
544        * @param string $content Content to set
545        * @throws FE When content is not scalar
546        * @return File|string
547        */
548       function content($content = '~none~') {
549          if ($content === '~none~') {
550             return $this->content;
551          } elseif (is_scalar($content)) {
552             \Log::debug('Overwrote file contents');
553             $this->content = $content;
554 
555          } else {
556             throw new FE('Content is not scalar!', FE::E_CONTENT_INVALID);
557          }
558 
559          return $this;
560       }
561 
562       /**
563        * Clears the file content
564        *
565        * @author Art <a.molcanovas@gmail.com>
566        * @return File
567        */
568       function clearContent() {
569          \Log::debug('Cleared file contents');
570          $this->content = null;
571 
572          return $this;
573       }
574 
575       /**
576        * Appends the file content
577        *
578        * @author Art <a.molcanovas@gmail.com>
579        * @param string $c The content
580        * @return File
581        */
582       function addContent($c) {
583          \Log::debug('Appended file contents');
584          $this->content .= $c;
585 
586          return $this;
587       }
588 
589    }
AloFramework documentation API documentation generated by ApiGen 2.8.0