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