ZipInfo.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <?php
  2. namespace PhpZip\Model;
  3. use PhpZip\Exception\ZipException;
  4. use PhpZip\Extra\Fields\NtfsExtraField;
  5. use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
  6. use PhpZip\Util\FilesUtil;
  7. use PhpZip\ZipFile;
  8. /**
  9. * Zip info.
  10. *
  11. * @author Ne-Lexa alexey@nelexa.ru
  12. * @license MIT
  13. */
  14. class ZipInfo
  15. {
  16. // made by constants
  17. const MADE_BY_MS_DOS = 0;
  18. const MADE_BY_AMIGA = 1;
  19. const MADE_BY_OPEN_VMS = 2;
  20. const MADE_BY_UNIX = 3;
  21. const MADE_BY_VM_CMS = 4;
  22. const MADE_BY_ATARI = 5;
  23. const MADE_BY_OS_2 = 6;
  24. const MADE_BY_MACINTOSH = 7;
  25. const MADE_BY_Z_SYSTEM = 8;
  26. const MADE_BY_CP_M = 9;
  27. const MADE_BY_WINDOWS_NTFS = 10;
  28. const MADE_BY_MVS = 11;
  29. const MADE_BY_VSE = 12;
  30. const MADE_BY_ACORN_RISC = 13;
  31. const MADE_BY_VFAT = 14;
  32. const MADE_BY_ALTERNATE_MVS = 15;
  33. const MADE_BY_BEOS = 16;
  34. const MADE_BY_TANDEM = 17;
  35. const MADE_BY_OS_400 = 18;
  36. const MADE_BY_OS_X = 19;
  37. const MADE_BY_UNKNOWN = 20;
  38. const UNX_IFMT = 0170000; // Unix file type mask
  39. const UNX_IFREG = 0100000; // Unix regular file
  40. const UNX_IFSOCK = 0140000; // Unix socket (BSD, not SysV or Amiga)
  41. const UNX_IFLNK = 0120000; // Unix symbolic link (not SysV, Amiga)
  42. const UNX_IFBLK = 0060000; // Unix block special (not Amiga)
  43. const UNX_IFDIR = 0040000; // Unix directory
  44. const UNX_IFCHR = 0020000; // Unix character special (not Amiga)
  45. const UNX_IFIFO = 0010000; // Unix fifo (BCC, not MSC or Amiga)
  46. const UNX_ISUID = 04000; // Unix set user id on execution
  47. const UNX_ISGID = 02000; // Unix set group id on execution
  48. const UNX_ISVTX = 01000; // Unix directory permissions control
  49. const UNX_ENFMT = self::UNX_ISGID; // Unix record locking enforcement flag
  50. const UNX_IRWXU = 00700; // Unix read, write, execute: owner
  51. const UNX_IRUSR = 00400; // Unix read permission: owner
  52. const UNX_IWUSR = 00200; // Unix write permission: owner
  53. const UNX_IXUSR = 00100; // Unix execute permission: owner
  54. const UNX_IRWXG = 00070; // Unix read, write, execute: group
  55. const UNX_IRGRP = 00040; // Unix read permission: group
  56. const UNX_IWGRP = 00020; // Unix write permission: group
  57. const UNX_IXGRP = 00010; // Unix execute permission: group
  58. const UNX_IRWXO = 00007; // Unix read, write, execute: other
  59. const UNX_IROTH = 00004; // Unix read permission: other
  60. const UNX_IWOTH = 00002; // Unix write permission: other
  61. const UNX_IXOTH = 00001; // Unix execute permission: other
  62. private static $platformNames = [
  63. self::MADE_BY_MS_DOS => 'FAT',
  64. self::MADE_BY_AMIGA => 'Amiga',
  65. self::MADE_BY_OPEN_VMS => 'OpenVMS',
  66. self::MADE_BY_UNIX => 'UNIX',
  67. self::MADE_BY_VM_CMS => 'VM/CMS',
  68. self::MADE_BY_ATARI => 'Atari ST',
  69. self::MADE_BY_OS_2 => 'OS/2 H.P.F.S.',
  70. self::MADE_BY_MACINTOSH => 'Macintosh',
  71. self::MADE_BY_Z_SYSTEM => 'Z-System',
  72. self::MADE_BY_CP_M => 'CP/M',
  73. self::MADE_BY_WINDOWS_NTFS => 'Windows NTFS',
  74. self::MADE_BY_MVS => 'MVS (OS/390 - Z/OS)',
  75. self::MADE_BY_VSE => 'VSE',
  76. self::MADE_BY_ACORN_RISC => 'Acorn Risc',
  77. self::MADE_BY_VFAT => 'VFAT',
  78. self::MADE_BY_ALTERNATE_MVS => 'Alternate MVS',
  79. self::MADE_BY_BEOS => 'BeOS',
  80. self::MADE_BY_TANDEM => 'Tandem',
  81. self::MADE_BY_OS_400 => 'OS/400',
  82. self::MADE_BY_OS_X => 'Mac OS X',
  83. ];
  84. private static $compressionMethodNames = [
  85. ZipEntry::UNKNOWN => 'unknown',
  86. ZipFile::METHOD_STORED => 'no compression',
  87. 1 => 'shrink',
  88. 2 => 'reduce level 1',
  89. 3 => 'reduce level 2',
  90. 4 => 'reduce level 3',
  91. 5 => 'reduce level 4',
  92. 6 => 'implode',
  93. 7 => 'reserved for Tokenizing compression algorithm',
  94. ZipFile::METHOD_DEFLATED => 'deflate',
  95. 9 => 'deflate64',
  96. 10 => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
  97. 11 => 'reserved by PKWARE',
  98. 12 => 'bzip2',
  99. 13 => 'reserved by PKWARE',
  100. 14 => 'LZMA (EFS)',
  101. 15 => 'reserved by PKWARE',
  102. 16 => 'reserved by PKWARE',
  103. 17 => 'reserved by PKWARE',
  104. 18 => 'IBM TERSE',
  105. 19 => 'IBM LZ77 z Architecture (PFS)',
  106. 97 => 'WavPack',
  107. 98 => 'PPMd version I, Rev 1',
  108. ZipEntry::METHOD_WINZIP_AES => 'WinZip AES',
  109. ];
  110. /** @var string */
  111. private $name;
  112. /** @var bool */
  113. private $folder;
  114. /** @var int */
  115. private $size;
  116. /** @var int */
  117. private $compressedSize;
  118. /** @var int */
  119. private $mtime;
  120. /** @var int|null */
  121. private $ctime;
  122. /** @var int|null */
  123. private $atime;
  124. /** @var bool */
  125. private $encrypted;
  126. /** @var string|null */
  127. private $comment;
  128. /** @var int */
  129. private $crc;
  130. /** @var string */
  131. private $methodName;
  132. /** @var int */
  133. private $compressionMethod;
  134. /** @var string */
  135. private $platform;
  136. /** @var int */
  137. private $version;
  138. /** @var string */
  139. private $attributes;
  140. /** @var int|null */
  141. private $encryptionMethod;
  142. /** @var int|null */
  143. private $compressionLevel;
  144. /**
  145. * ZipInfo constructor.
  146. *
  147. * @param ZipEntry $entry
  148. *
  149. * @throws ZipException
  150. * @noinspection PhpMissingBreakStatementInspection
  151. */
  152. public function __construct(ZipEntry $entry)
  153. {
  154. $mtime = $entry->getTime();
  155. $atime = null;
  156. $ctime = null;
  157. $field = $entry->getExtraFieldsCollection()->get(NtfsExtraField::getHeaderId());
  158. if ($field instanceof NtfsExtraField) {
  159. /**
  160. * @var NtfsExtraField $field
  161. */
  162. $atime = $field->getAtime();
  163. $ctime = $field->getCtime();
  164. $mtime = $field->getMtime();
  165. }
  166. $this->name = $entry->getName();
  167. $this->folder = $entry->isDirectory();
  168. $this->size = $entry->getSize();
  169. $this->compressedSize = $entry->getCompressedSize();
  170. $this->mtime = $mtime;
  171. $this->ctime = $ctime;
  172. $this->atime = $atime;
  173. $this->encrypted = $entry->isEncrypted();
  174. $this->encryptionMethod = $entry->getEncryptionMethod();
  175. $this->comment = $entry->getComment();
  176. $this->crc = $entry->getCrc();
  177. $this->compressionMethod = self::getMethodId($entry);
  178. $this->methodName = self::getEntryMethodName($entry);
  179. $this->platform = self::getPlatformName($entry);
  180. $this->version = $entry->getVersionNeededToExtract();
  181. $this->compressionLevel = $entry->getCompressionLevel();
  182. $attributes = str_repeat(' ', 12);
  183. $externalAttributes = $entry->getExternalAttributes();
  184. $xattr = (($externalAttributes >> 16) & 0xFFFF);
  185. switch ($entry->getCreatedOS()) {
  186. case self::MADE_BY_MS_DOS:
  187. case self::MADE_BY_WINDOWS_NTFS:
  188. if ($entry->getCreatedOS() !== self::MADE_BY_MS_DOS ||
  189. ($xattr & self::UNX_IRWXU) !==
  190. (self::UNX_IRUSR |
  191. (!($externalAttributes & 1) << 7) |
  192. (($externalAttributes & 0x10) << 2))
  193. ) {
  194. $xattr = $externalAttributes & 0xFF;
  195. $attributes = '.r.-... ';
  196. $attributes[2] = ($xattr & 0x01) ? '-' : 'w';
  197. $attributes[5] = ($xattr & 0x02) ? 'h' : '-';
  198. $attributes[6] = ($xattr & 0x04) ? 's' : '-';
  199. $attributes[4] = ($xattr & 0x20) ? 'a' : '-';
  200. if ($xattr & 0x10) {
  201. $attributes[0] = 'd';
  202. $attributes[3] = 'x';
  203. } else {
  204. $attributes[0] = '-';
  205. }
  206. if ($xattr & 0x08) {
  207. $attributes[0] = 'V';
  208. } else {
  209. $ext = strtolower(pathinfo($entry->getName(), \PATHINFO_EXTENSION));
  210. if (\in_array($ext, ['com', 'exe', 'btm', 'cmd', 'bat'])) {
  211. $attributes[3] = 'x';
  212. }
  213. }
  214. break;
  215. } // else: fall through!
  216. // no break
  217. default: // assume Unix-like
  218. switch ($xattr & self::UNX_IFMT) {
  219. case self::UNX_IFDIR:
  220. $attributes[0] = 'd';
  221. break;
  222. case self::UNX_IFREG:
  223. $attributes[0] = '-';
  224. break;
  225. case self::UNX_IFLNK:
  226. $attributes[0] = 'l';
  227. break;
  228. case self::UNX_IFBLK:
  229. $attributes[0] = 'b';
  230. break;
  231. case self::UNX_IFCHR:
  232. $attributes[0] = 'c';
  233. break;
  234. case self::UNX_IFIFO:
  235. $attributes[0] = 'p';
  236. break;
  237. case self::UNX_IFSOCK:
  238. $attributes[0] = 's';
  239. break;
  240. default:
  241. $attributes[0] = '?';
  242. break;
  243. }
  244. $attributes[1] = ($xattr & self::UNX_IRUSR) ? 'r' : '-';
  245. $attributes[4] = ($xattr & self::UNX_IRGRP) ? 'r' : '-';
  246. $attributes[7] = ($xattr & self::UNX_IROTH) ? 'r' : '-';
  247. $attributes[2] = ($xattr & self::UNX_IWUSR) ? 'w' : '-';
  248. $attributes[5] = ($xattr & self::UNX_IWGRP) ? 'w' : '-';
  249. $attributes[8] = ($xattr & self::UNX_IWOTH) ? 'w' : '-';
  250. if ($xattr & self::UNX_IXUSR) {
  251. $attributes[3] = ($xattr & self::UNX_ISUID) ? 's' : 'x';
  252. } else {
  253. $attributes[3] = ($xattr & self::UNX_ISUID) ? 'S' : '-';
  254. } // S==undefined
  255. if ($xattr & self::UNX_IXGRP) {
  256. $attributes[6] = ($xattr & self::UNX_ISGID) ? 's' : 'x';
  257. } // == UNX_ENFMT
  258. else {
  259. $attributes[6] = ($xattr & self::UNX_ISGID) ? 'S' : '-';
  260. } // SunOS 4.1.x
  261. if ($xattr & self::UNX_IXOTH) {
  262. $attributes[9] = ($xattr & self::UNX_ISVTX) ? 't' : 'x';
  263. } // "sticky bit"
  264. else {
  265. $attributes[9] = ($xattr & self::UNX_ISVTX) ? 'T' : '-';
  266. } // T==undefined
  267. }
  268. $this->attributes = trim($attributes);
  269. }
  270. /**
  271. * @param ZipEntry $entry
  272. *
  273. * @throws ZipException
  274. *
  275. * @return int
  276. */
  277. private static function getMethodId(ZipEntry $entry)
  278. {
  279. $method = $entry->getMethod();
  280. if ($entry->isEncrypted() && $entry->getMethod() === ZipEntry::METHOD_WINZIP_AES) {
  281. $field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
  282. if ($field !== null) {
  283. /** @var WinZipAesEntryExtraField $field */
  284. $method = $field->getMethod();
  285. }
  286. }
  287. return $method;
  288. }
  289. /**
  290. * @param ZipEntry $entry
  291. *
  292. * @throws ZipException
  293. *
  294. * @return string
  295. */
  296. private static function getEntryMethodName(ZipEntry $entry)
  297. {
  298. $return = '';
  299. $compressionMethod = $entry->getMethod();
  300. if ($entry->isEncrypted()) {
  301. if ($entry->getMethod() === ZipEntry::METHOD_WINZIP_AES) {
  302. $return .= ucfirst(self::$compressionMethodNames[$entry->getMethod()]);
  303. /** @var WinZipAesEntryExtraField|null $field */
  304. $field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
  305. if ($field !== null) {
  306. $return .= '-' . $field->getKeyStrength();
  307. $compressionMethod = $field->getMethod();
  308. }
  309. } else {
  310. $return .= 'ZipCrypto';
  311. }
  312. $return .= ' ';
  313. }
  314. if (isset(self::$compressionMethodNames[$compressionMethod])) {
  315. $return .= ucfirst(self::$compressionMethodNames[$compressionMethod]);
  316. } else {
  317. $return .= 'unknown';
  318. }
  319. return $return;
  320. }
  321. /**
  322. * @param ZipEntry $entry
  323. *
  324. * @return string
  325. */
  326. public static function getPlatformName(ZipEntry $entry)
  327. {
  328. if (isset(self::$platformNames[$entry->getCreatedOS()])) {
  329. return self::$platformNames[$entry->getCreatedOS()];
  330. }
  331. return 'unknown';
  332. }
  333. /**
  334. * @return string
  335. */
  336. public function getName()
  337. {
  338. return $this->name;
  339. }
  340. /**
  341. * @return string
  342. *
  343. * @deprecated use \PhpZip\Model\ZipInfo::getName()
  344. */
  345. public function getPath()
  346. {
  347. return $this->getName();
  348. }
  349. /**
  350. * @return bool
  351. */
  352. public function isFolder()
  353. {
  354. return $this->folder;
  355. }
  356. /**
  357. * @return int
  358. */
  359. public function getSize()
  360. {
  361. return $this->size;
  362. }
  363. /**
  364. * @return int
  365. */
  366. public function getCompressedSize()
  367. {
  368. return $this->compressedSize;
  369. }
  370. /**
  371. * @return int
  372. */
  373. public function getMtime()
  374. {
  375. return $this->mtime;
  376. }
  377. /**
  378. * @return int|null
  379. */
  380. public function getCtime()
  381. {
  382. return $this->ctime;
  383. }
  384. /**
  385. * @return int|null
  386. */
  387. public function getAtime()
  388. {
  389. return $this->atime;
  390. }
  391. /**
  392. * @return string
  393. */
  394. public function getAttributes()
  395. {
  396. return $this->attributes;
  397. }
  398. /**
  399. * @return bool
  400. */
  401. public function isEncrypted()
  402. {
  403. return $this->encrypted;
  404. }
  405. /**
  406. * @return string|null
  407. */
  408. public function getComment()
  409. {
  410. return $this->comment;
  411. }
  412. /**
  413. * @return int
  414. */
  415. public function getCrc()
  416. {
  417. return $this->crc;
  418. }
  419. /**
  420. * @return string
  421. *
  422. * @deprecated use \PhpZip\Model\ZipInfo::getMethodName()
  423. */
  424. public function getMethod()
  425. {
  426. return $this->getMethodName();
  427. }
  428. /**
  429. * @return string
  430. */
  431. public function getMethodName()
  432. {
  433. return $this->methodName;
  434. }
  435. /**
  436. * @return string
  437. */
  438. public function getPlatform()
  439. {
  440. return $this->platform;
  441. }
  442. /**
  443. * @return int
  444. */
  445. public function getVersion()
  446. {
  447. return $this->version;
  448. }
  449. /**
  450. * @return int|null
  451. */
  452. public function getEncryptionMethod()
  453. {
  454. return $this->encryptionMethod;
  455. }
  456. /**
  457. * @return int|null
  458. */
  459. public function getCompressionLevel()
  460. {
  461. return $this->compressionLevel;
  462. }
  463. /**
  464. * @return int
  465. */
  466. public function getCompressionMethod()
  467. {
  468. return $this->compressionMethod;
  469. }
  470. /**
  471. * @return array
  472. */
  473. public function toArray()
  474. {
  475. return [
  476. 'name' => $this->getName(),
  477. 'path' => $this->getName(), // deprecated
  478. 'folder' => $this->isFolder(),
  479. 'size' => $this->getSize(),
  480. 'compressed_size' => $this->getCompressedSize(),
  481. 'modified' => $this->getMtime(),
  482. 'created' => $this->getCtime(),
  483. 'accessed' => $this->getAtime(),
  484. 'attributes' => $this->getAttributes(),
  485. 'encrypted' => $this->isEncrypted(),
  486. 'encryption_method' => $this->getEncryptionMethod(),
  487. 'comment' => $this->getComment(),
  488. 'crc' => $this->getCrc(),
  489. 'method' => $this->getMethodName(), // deprecated
  490. 'method_name' => $this->getMethodName(),
  491. 'compression_method' => $this->getCompressionMethod(),
  492. 'platform' => $this->getPlatform(),
  493. 'version' => $this->getVersion(),
  494. ];
  495. }
  496. /**
  497. * @return string
  498. */
  499. public function __toString()
  500. {
  501. return __CLASS__ . ' {'
  502. . 'Name="' . $this->getName() . '", '
  503. . ($this->isFolder() ? 'Folder, ' : '')
  504. . 'Size="' . FilesUtil::humanSize($this->getSize()) . '"'
  505. . ', Compressed size="' . FilesUtil::humanSize($this->getCompressedSize()) . '"'
  506. . ', Modified time="' . date(\DATE_W3C, $this->getMtime()) . '", '
  507. . ($this->getCtime() !== null ? 'Created time="' . date(\DATE_W3C, $this->getCtime()) . '", ' : '')
  508. . ($this->getAtime() !== null ? 'Accessed time="' . date(\DATE_W3C, $this->getAtime()) . '", ' : '')
  509. . ($this->isEncrypted() ? 'Encrypted, ' : '')
  510. . (!empty($this->comment) ? 'Comment="' . $this->getComment() . '", ' : '')
  511. . (!empty($this->crc) ? 'Crc=0x' . dechex($this->getCrc()) . ', ' : '')
  512. . 'Method name="' . $this->getMethodName() . '", '
  513. . 'Attributes="' . $this->getAttributes() . '", '
  514. . 'Platform="' . $this->getPlatform() . '", '
  515. . 'Version=' . $this->getVersion()
  516. . '}';
  517. }
  518. }