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}