xref: /dokuwiki/vendor/splitbrain/php-archive/src/FileInfo.php (revision 67b479b256ae7436b507860d32ee7e0b29b1657a)
1<?php
2
3namespace splitbrain\PHPArchive;
4
5/**
6 * Class FileInfo
7 *
8 * stores meta data about a file in an Archive
9 *
10 * @author  Andreas Gohr <andi@splitbrain.org>
11 * @package splitbrain\PHPArchive
12 * @license MIT
13 */
14class FileInfo
15{
16
17    protected $isdir = false;
18    protected $path = '';
19    protected $size = 0;
20    protected $csize = 0;
21    protected $mtime = 0;
22    protected $mode = 0664;
23    protected $owner = '';
24    protected $group = '';
25    protected $uid = 0;
26    protected $gid = 0;
27    protected $comment = '';
28
29    /**
30     * initialize dynamic defaults
31     *
32     * @param string $path The path of the file, can also be set later through setPath()
33     */
34    public function __construct($path = '')
35    {
36        $this->mtime = time();
37        $this->setPath($path);
38    }
39
40    /**
41     * Factory to build FileInfo from existing file or directory
42     *
43     * @param string $path path to a file on the local file system
44     * @param string $as   optional path to use inside the archive
45     * @throws FileInfoException
46     * @return FileInfo
47     */
48    public static function fromPath($path, $as = '')
49    {
50        clearstatcache(false, $path);
51
52        if (!file_exists($path)) {
53            throw new FileInfoException("$path does not exist");
54        }
55
56        $stat = stat($path);
57        $file = new FileInfo();
58
59        $file->setPath($path);
60        $file->setIsdir(is_dir($path));
61        $file->setMode(fileperms($path));
62        $file->setOwner(fileowner($path));
63        $file->setGroup(filegroup($path));
64        $file->setUid($stat['uid']);
65        $file->setGid($stat['gid']);
66        $file->setMtime($stat['mtime']);
67
68        if ($as) {
69            $file->setPath($as);
70        }
71
72        return $file;
73    }
74
75    /**
76     * @return int
77     */
78    public function getSize()
79    {
80        return $this->size;
81    }
82
83    /**
84     * @param int $size
85     */
86    public function setSize($size)
87    {
88        $this->size = $size;
89    }
90
91    /**
92     * @return int
93     */
94    public function getCompressedSize()
95    {
96        return $this->csize;
97    }
98
99    /**
100     * @param int $csize
101     */
102    public function setCompressedSize($csize)
103    {
104        $this->csize = $csize;
105    }
106
107    /**
108     * @return int
109     */
110    public function getMtime()
111    {
112        return $this->mtime;
113    }
114
115    /**
116     * @param int $mtime
117     */
118    public function setMtime($mtime)
119    {
120        $this->mtime = $mtime;
121    }
122
123    /**
124     * @return int
125     */
126    public function getGid()
127    {
128        return $this->gid;
129    }
130
131    /**
132     * @param int $gid
133     */
134    public function setGid($gid)
135    {
136        $this->gid = $gid;
137    }
138
139    /**
140     * @return int
141     */
142    public function getUid()
143    {
144        return $this->uid;
145    }
146
147    /**
148     * @param int $uid
149     */
150    public function setUid($uid)
151    {
152        $this->uid = $uid;
153    }
154
155    /**
156     * @return string
157     */
158    public function getComment()
159    {
160        return $this->comment;
161    }
162
163    /**
164     * @param string $comment
165     */
166    public function setComment($comment)
167    {
168        $this->comment = $comment;
169    }
170
171    /**
172     * @return string
173     */
174    public function getGroup()
175    {
176        return $this->group;
177    }
178
179    /**
180     * @param string $group
181     */
182    public function setGroup($group)
183    {
184        $this->group = $group;
185    }
186
187    /**
188     * @return boolean
189     */
190    public function getIsdir()
191    {
192        return $this->isdir;
193    }
194
195    /**
196     * @param boolean $isdir
197     */
198    public function setIsdir($isdir)
199    {
200        // default mode for directories
201        if ($isdir && $this->mode === 0664) {
202            $this->mode = 0775;
203        }
204        $this->isdir = $isdir;
205    }
206
207    /**
208     * @return int
209     */
210    public function getMode()
211    {
212        return $this->mode;
213    }
214
215    /**
216     * @param int $mode
217     */
218    public function setMode($mode)
219    {
220        $this->mode = $mode;
221    }
222
223    /**
224     * @return string
225     */
226    public function getOwner()
227    {
228        return $this->owner;
229    }
230
231    /**
232     * @param string $owner
233     */
234    public function setOwner($owner)
235    {
236        $this->owner = $owner;
237    }
238
239    /**
240     * @return string
241     */
242    public function getPath()
243    {
244        return $this->path;
245    }
246
247    /**
248     * @param string $path
249     */
250    public function setPath($path)
251    {
252        $this->path = $this->cleanPath($path);
253    }
254
255    /**
256     * Cleans up a path and removes relative parts, also strips leading slashes
257     *
258     * @param string $path
259     * @return string
260     */
261    protected function cleanPath($path)
262    {
263        $path    = str_replace('\\', '/', $path);
264        $path    = explode('/', $path);
265        $newpath = array();
266        foreach ($path as $p) {
267            if ($p === '' || $p === '.') {
268                continue;
269            }
270            if ($p === '..') {
271                array_pop($newpath);
272                continue;
273            }
274            array_push($newpath, $p);
275        }
276        return trim(implode('/', $newpath), '/');
277    }
278
279    /**
280     * Strip given prefix or number of path segments from the filename
281     *
282     * The $strip parameter allows you to strip a certain number of path components from the filenames
283     * found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when
284     * an integer is passed as $strip.
285     * Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix,
286     * the prefix will be stripped. It is recommended to give prefixes with a trailing slash.
287     *
288     * @param  int|string $strip
289     * @return FileInfo
290     */
291    public function strip($strip)
292    {
293        $filename = $this->getPath();
294        $striplen = strlen($strip);
295        if (is_int($strip)) {
296            // if $strip is an integer we strip this many path components
297            $parts = explode('/', $filename);
298            if (!$this->getIsdir()) {
299                $base = array_pop($parts); // keep filename itself
300            } else {
301                $base = '';
302            }
303            $filename = join('/', array_slice($parts, $strip));
304            if ($base) {
305                $filename .= "/$base";
306            }
307        } else {
308            // if strip is a string, we strip a prefix here
309            if (substr($filename, 0, $striplen) == $strip) {
310                $filename = substr($filename, $striplen);
311            }
312        }
313
314        $this->setPath($filename);
315    }
316
317    /**
318     * Does the file match the given include and exclude expressions?
319     *
320     * Exclude rules take precedence over include rules
321     *
322     * @param string $include Regular expression of files to include
323     * @param string $exclude Regular expression of files to exclude
324     * @return bool
325     */
326    public function match($include = '', $exclude = '')
327    {
328        $extract = true;
329        if ($include && !preg_match($include, $this->getPath())) {
330            $extract = false;
331        }
332        if ($exclude && preg_match($exclude, $this->getPath())) {
333            $extract = false;
334        }
335
336        return $extract;
337    }
338}
339
340class FileInfoException extends \Exception
341{
342}