1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\File;
38
39use Hoa\Stream;
40
41/**
42 * Class \Hoa\File\Generic.
43 *
44 * Describe a super-file.
45 *
46 * @copyright  Copyright © 2007-2017 Hoa community
47 * @license    New BSD License
48 */
49abstract class Generic
50    extends    Stream
51    implements Stream\IStream\Pathable,
52               Stream\IStream\Statable,
53               Stream\IStream\Touchable
54{
55    /**
56     * Mode.
57     *
58     * @var string
59     */
60    protected $_mode = null;
61
62
63
64    /**
65     * Get filename component of path.
66     *
67     * @return  string
68     */
69    public function getBasename()
70    {
71        return basename($this->getStreamName());
72    }
73
74    /**
75     * Get directory name component of path.
76     *
77     * @return  string
78     */
79    public function getDirname()
80    {
81        return dirname($this->getStreamName());
82    }
83
84    /**
85     * Get size.
86     *
87     * @return  int
88     */
89    public function getSize()
90    {
91        if (false === $this->getStatistic()) {
92            return false;
93        }
94
95        return filesize($this->getStreamName());
96    }
97
98    /**
99     * Get informations about a file.
100     *
101     * @return  array
102     */
103    public function getStatistic()
104    {
105        return fstat($this->getStream());
106    }
107
108    /**
109     * Get last access time of file.
110     *
111     * @return  int
112     */
113    public function getATime()
114    {
115        return fileatime($this->getStreamName());
116    }
117
118    /**
119     * Get inode change time of file.
120     *
121     * @return  int
122     */
123    public function getCTime()
124    {
125        return filectime($this->getStreamName());
126    }
127
128    /**
129     * Get file modification time.
130     *
131     * @return  int
132     */
133    public function getMTime()
134    {
135        return filemtime($this->getStreamName());
136    }
137
138    /**
139     * Get file group.
140     *
141     * @return  int
142     */
143    public function getGroup()
144    {
145        return filegroup($this->getStreamName());
146    }
147
148    /**
149     * Get file owner.
150     *
151     * @return  int
152     */
153    public function getOwner()
154    {
155        return fileowner($this->getStreamName());
156    }
157
158    /**
159     * Get file permissions.
160     *
161     * @return  int
162     */
163    public function getPermissions()
164    {
165        return fileperms($this->getStreamName());
166    }
167
168    /**
169     * Get file permissions as a string.
170     * Result sould be interpreted like this:
171     *     * s: socket;
172     *     * l: symbolic link;
173     *     * -: regular;
174     *     * b: block special;
175     *     * d: directory;
176     *     * c: character special;
177     *     * p: FIFO pipe;
178     *     * u: unknown.
179     *
180     * @return  string
181     */
182    public function getReadablePermissions()
183    {
184        $p = $this->getPermissions();
185
186        if (($p & 0xC000) == 0xC000) {
187            $out = 's';
188        } elseif (($p & 0xA000) == 0xA000) {
189            $out = 'l';
190        } elseif (($p & 0x8000) == 0x8000) {
191            $out = '-';
192        } elseif (($p & 0x6000) == 0x6000) {
193            $out = 'b';
194        } elseif (($p & 0x4000) == 0x4000) {
195            $out = 'd';
196        } elseif (($p & 0x2000) == 0x2000) {
197            $out = 'c';
198        } elseif (($p & 0x1000) == 0x1000) {
199            $out = 'p';
200        } else {
201            $out = 'u';
202        }
203
204        $out .=
205            (($p & 0x0100) ? 'r' : '-') .
206            (($p & 0x0080) ? 'w' : '-') .
207            (($p & 0x0040) ?
208            (($p & 0x0800) ? 's' : 'x') :
209            (($p & 0x0800) ? 'S' : '-')) .
210            (($p & 0x0020) ? 'r' : '-') .
211            (($p & 0x0010) ? 'w' : '-') .
212            (($p & 0x0008) ?
213            (($p & 0x0400) ? 's' : 'x') :
214            (($p & 0x0400) ? 'S' : '-')) .
215            (($p & 0x0004) ? 'r' : '-') .
216            (($p & 0x0002) ? 'w' : '-') .
217            (($p & 0x0001) ?
218            (($p & 0x0200) ? 't' : 'x') :
219            (($p & 0x0200) ? 'T' : '-'));
220
221        return $out;
222    }
223
224    /**
225     * Check if the file is readable.
226     *
227     * @return  bool
228     */
229    public function isReadable()
230    {
231        return is_readable($this->getStreamName());
232    }
233
234    /**
235     * Check if the file is writable.
236     *
237     * @return  bool
238     */
239    public function isWritable()
240    {
241        return is_writable($this->getStreamName());
242    }
243
244    /**
245     * Check if the file is executable.
246     *
247     * @return  bool
248     */
249    public function isExecutable()
250    {
251        return is_executable($this->getStreamName());
252    }
253
254    /**
255     * Clear file status cache.
256     *
257     * @return  void
258     */
259    public function clearStatisticCache()
260    {
261        clearstatcache(true, $this->getStreamName());
262
263        return;
264    }
265
266    /**
267     * Clear all files status cache.
268     *
269     * @return  void
270     */
271    public static function clearAllStatisticCaches()
272    {
273        clearstatcache();
274
275        return;
276    }
277
278    /**
279     * Set access and modification time of file.
280     *
281     * @param   int     $time     Time. If equals to -1, time() should be used.
282     * @param   int     $atime    Access time. If equals to -1, $time should be
283     *                            used.
284     * @return  bool
285     */
286    public function touch($time = -1, $atime = -1)
287    {
288        if ($time == -1) {
289            $time  = time();
290        }
291
292        if ($atime == -1) {
293            $atime = $time;
294        }
295
296        return touch($this->getStreamName(), $time, $atime);
297    }
298
299    /**
300     * Copy file.
301     * Return the destination file path if succeed, false otherwise.
302     *
303     * @param   string  $to       Destination path.
304     * @param   bool    $force    Force to copy if the file $to already exists.
305     *                            Use the \Hoa\Stream\IStream\Touchable::*OVERWRITE
306     *                            constants.
307     * @return  bool
308     */
309    public function copy($to, $force = Stream\IStream\Touchable::DO_NOT_OVERWRITE)
310    {
311        $from = $this->getStreamName();
312
313        if ($force === Stream\IStream\Touchable::DO_NOT_OVERWRITE &&
314            true   === file_exists($to)) {
315            return true;
316        }
317
318        if (null === $this->getStreamContext()) {
319            return @copy($from, $to);
320        }
321
322        return @copy($from, $to, $this->getStreamContext()->getContext());
323    }
324
325    /**
326     * Move a file.
327     *
328     * @param   string  $name     New name.
329     * @param   bool    $force    Force to move if the file $name already
330     *                            exists.
331     *                            Use the \Hoa\Stream\IStream\Touchable::*OVERWRITE
332     *                            constants.
333     * @param   bool    $mkdir    Force to make directory if does not exist.
334     *                            Use the \Hoa\Stream\IStream\Touchable::*DIRECTORY
335     *                            constants.
336     * @return  bool
337     */
338    public function move(
339        $name,
340        $force = Stream\IStream\Touchable::DO_NOT_OVERWRITE,
341        $mkdir = Stream\IStream\Touchable::DO_NOT_MAKE_DIRECTORY
342    ) {
343        $from = $this->getStreamName();
344
345        if ($force === Stream\IStream\Touchable::DO_NOT_OVERWRITE &&
346            true   === file_exists($name)) {
347            return false;
348        }
349
350        if (Stream\IStream\Touchable::MAKE_DIRECTORY === $mkdir) {
351            Directory::create(
352                dirname($name),
353                Directory::MODE_CREATE_RECURSIVE
354            );
355        }
356
357        if (null === $this->getStreamContext()) {
358            return @rename($from, $name);
359        }
360
361        return @rename($from, $name, $this->getStreamContext()->getContext());
362    }
363
364    /**
365     * Delete a file.
366     *
367     * @return  bool
368     */
369    public function delete()
370    {
371        if (null === $this->getStreamContext()) {
372            return @unlink($this->getStreamName());
373        }
374
375        return @unlink(
376            $this->getStreamName(),
377            $this->getStreamContext()->getContext()
378        );
379    }
380
381    /**
382     * Change file group.
383     *
384     * @param   mixed   $group    Group name or number.
385     * @return  bool
386     */
387    public function changeGroup($group)
388    {
389        return chgrp($this->getStreamName(), $group);
390    }
391
392    /**
393     * Change file mode.
394     *
395     * @param   int     $mode    Mode (in octal!).
396     * @return  bool
397     */
398    public function changeMode($mode)
399    {
400        return chmod($this->getStreamName(), $mode);
401    }
402
403    /**
404     * Change file owner.
405     *
406     * @param   mixed   $user    User.
407     * @return  bool
408     */
409    public function changeOwner($user)
410    {
411        return chown($this->getStreamName(), $user);
412    }
413
414    /**
415     * Change the current umask.
416     *
417     * @param   int     $umask    Umask (in octal!). If null, given the current
418     *                            umask value.
419     * @return  int
420     */
421    public static function umask($umask = null)
422    {
423        if (null === $umask) {
424            return umask();
425        }
426
427        return umask($umask);
428    }
429
430    /**
431     * Check if it is a file.
432     *
433     * @return  bool
434     */
435    public function isFile()
436    {
437        return is_file($this->getStreamName());
438    }
439
440    /**
441     * Check if it is a link.
442     *
443     * @return  bool
444     */
445    public function isLink()
446    {
447        return is_link($this->getStreamName());
448    }
449
450    /**
451     * Check if it is a directory.
452     *
453     * @return  bool
454     */
455    public function isDirectory()
456    {
457        return is_dir($this->getStreamName());
458    }
459
460    /**
461     * Check if it is a socket.
462     *
463     * @return  bool
464     */
465    public function isSocket()
466    {
467        return filetype($this->getStreamName()) == 'socket';
468    }
469
470    /**
471     * Check if it is a FIFO pipe.
472     *
473     * @return  bool
474     */
475    public function isFIFOPipe()
476    {
477        return filetype($this->getStreamName()) == 'fifo';
478    }
479
480    /**
481     * Check if it is character special file.
482     *
483     * @return  bool
484     */
485    public function isCharacterSpecial()
486    {
487        return filetype($this->getStreamName()) == 'char';
488    }
489
490    /**
491     * Check if it is block special.
492     *
493     * @return  bool
494     */
495    public function isBlockSpecial()
496    {
497        return filetype($this->getStreamName()) == 'block';
498    }
499
500    /**
501     * Check if it is an unknown type.
502     *
503     * @return  bool
504     */
505    public function isUnknown()
506    {
507        return filetype($this->getStreamName()) == 'unknown';
508    }
509
510    /**
511     * Set the open mode.
512     *
513     * @param   string     $mode    Open mode. Please, see the child::MODE_*
514     *                              constants.
515     * @return  string
516     */
517    protected function setMode($mode)
518    {
519        $old         = $this->_mode;
520        $this->_mode = $mode;
521
522        return $old;
523    }
524
525    /**
526     * Get the open mode.
527     *
528     * @return  string
529     */
530    public function getMode()
531    {
532        return $this->_mode;
533    }
534
535    /**
536     * Get inode.
537     *
538     * @return  int
539     */
540    public function getINode()
541    {
542        return fileinode($this->getStreamName());
543    }
544
545    /**
546     * Check if the system is case sensitive or not.
547     *
548     * @return  bool
549     */
550    public static function isCaseSensitive()
551    {
552        return !(
553            file_exists(mb_strtolower(__FILE__)) &&
554            file_exists(mb_strtoupper(__FILE__))
555        );
556    }
557
558    /**
559     * Get a canonicalized absolute pathname.
560     *
561     * @return  string
562     */
563    public function getRealPath()
564    {
565        if (false === $out = realpath($this->getStreamName())) {
566            return $this->getStreamName();
567        }
568
569        return $out;
570    }
571
572    /**
573     * Get file extension (if exists).
574     *
575     * @return  string
576     */
577    public function getExtension()
578    {
579        return pathinfo(
580            $this->getStreamName(),
581            PATHINFO_EXTENSION
582        );
583    }
584
585    /**
586     * Get filename without extension.
587     *
588     * @return  string
589     */
590    public function getFilename()
591    {
592        $file = basename($this->getStreamName());
593
594        if (defined('PATHINFO_FILENAME')) {
595            return pathinfo($file, PATHINFO_FILENAME);
596        }
597
598        if (strstr($file, '.')) {
599            return substr($file, 0, strrpos($file, '.'));
600        }
601
602        return $file;
603    }
604}
605