ZipInfo.php 18 KB

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