2
0

ZipTest.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. <?php
  2. namespace PhpZip;
  3. use PhpZip\Exception\ZipAuthenticationException;
  4. use PhpZip\Model\ZipEntry;
  5. use PhpZip\Util\CryptoUtil;
  6. use PhpZip\Util\FilesUtil;
  7. /**
  8. * ZipFile and ZipOutputFile test
  9. */
  10. class ZipTest extends ZipTestCase
  11. {
  12. /**
  13. * @var string
  14. */
  15. private $outputFilename;
  16. /**
  17. * Before test
  18. */
  19. protected function setUp()
  20. {
  21. parent::setUp();
  22. $this->outputFilename = sys_get_temp_dir() . '/' . uniqid() . '.zip';
  23. }
  24. /**
  25. * After test
  26. */
  27. protected function tearDown()
  28. {
  29. parent::tearDown();
  30. if ($this->outputFilename !== null && file_exists($this->outputFilename)) {
  31. unlink($this->outputFilename);
  32. }
  33. }
  34. /**
  35. * Create empty archive
  36. *
  37. * @see ZipOutputFile::create()
  38. */
  39. public function testCreateEmptyArchive()
  40. {
  41. $zipFile = ZipOutputFile::create();
  42. $zipFile->saveAsFile($this->outputFilename);
  43. $zipFile->close();
  44. $zipFile = ZipFile::openFromFile($this->outputFilename);
  45. self::assertEquals(count($zipFile), 0);
  46. $zipFile->close();
  47. self::assertCorrectEmptyZip($this->outputFilename);
  48. }
  49. /**
  50. * Create archive and add files.
  51. *
  52. * @see ZipOutputFile::addFromString()
  53. * @see ZipOutputFile::addFromFile()
  54. * @see ZipOutputFile::addFromStream()
  55. * @see ZipFile::getEntryContent()
  56. */
  57. public function testCreateArchiveAndAddFiles()
  58. {
  59. $outputFromString = file_get_contents(__FILE__);
  60. $outputFromFile = file_get_contents(dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'bootstrap.xml');
  61. $outputFromStream = file_get_contents(dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'composer.json');
  62. $filenameFromString = basename(__FILE__);
  63. $filenameFromFile = 'data/test file.txt';
  64. $filenameFromStream = 'data/ডিরেক্টরি/αρχείο.json';
  65. $emptyDirName = 'empty dir/пустой каталог/空目錄/ไดเรกทอรีที่ว่างเปล่า/';
  66. $tempFile = tempnam(sys_get_temp_dir(), 'txt');
  67. file_put_contents($tempFile, $outputFromFile);
  68. $tempStream = tmpfile();
  69. fwrite($tempStream, $outputFromStream);
  70. $outputZipFile = ZipOutputFile::create();
  71. $outputZipFile->addFromString($filenameFromString, $outputFromString);
  72. $outputZipFile->addFromFile($tempFile, $filenameFromFile);
  73. $outputZipFile->addFromStream($tempStream, $filenameFromStream);
  74. $outputZipFile->addEmptyDir($emptyDirName);
  75. $outputZipFile->saveAsFile($this->outputFilename);
  76. $outputZipFile->close();
  77. unlink($tempFile);
  78. self::assertCorrectZipArchive($this->outputFilename);
  79. $zipFile = ZipFile::openFromFile($this->outputFilename);
  80. self::assertEquals(count($zipFile), 4);
  81. self::assertEquals($zipFile->getEntryContent($filenameFromString), $outputFromString);
  82. self::assertEquals($zipFile->getEntryContent($filenameFromFile), $outputFromFile);
  83. self::assertEquals($zipFile->getEntryContent($filenameFromStream), $outputFromStream);
  84. self::assertTrue($zipFile->hasEntry($emptyDirName));
  85. self::assertTrue($zipFile->isDirectory($emptyDirName));
  86. $listFiles = $zipFile->getListFiles();
  87. self::assertEquals($listFiles[0], $filenameFromString);
  88. self::assertEquals($listFiles[1], $filenameFromFile);
  89. self::assertEquals($listFiles[2], $filenameFromStream);
  90. self::assertEquals($listFiles[3], $emptyDirName);
  91. $zipFile->close();
  92. }
  93. /**
  94. * Create archive and add directory recursively.
  95. */
  96. public function testAddDirRecursively()
  97. {
  98. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . "src";
  99. $outputZipFile = new ZipOutputFile();
  100. $outputZipFile->addDir($inputDir);
  101. $outputZipFile->saveAsFile($this->outputFilename);
  102. $outputZipFile->close();
  103. self::assertCorrectZipArchive($this->outputFilename);
  104. }
  105. /**
  106. * Create archive and add directory not recursively.
  107. */
  108. public function testAddDirNotRecursively()
  109. {
  110. $inputDir = dirname(dirname(__DIR__));
  111. $recursive = false;
  112. $outputZipFile = new ZipOutputFile();
  113. $outputZipFile->addDir($inputDir, $recursive);
  114. $outputZipFile->saveAsFile($this->outputFilename);
  115. $outputZipFile->close();
  116. self::assertCorrectZipArchive($this->outputFilename);
  117. }
  118. /**
  119. * Create archive and add directory and put files to path.
  120. */
  121. public function testAddDirAndMoveToPath()
  122. {
  123. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . "src";
  124. $recursive = true;
  125. $outputZipFile = new ZipOutputFile();
  126. $moveToPath = 'Library/src';
  127. $outputZipFile->addDir($inputDir, $recursive, $moveToPath);
  128. $outputZipFile->saveAsFile($this->outputFilename);
  129. $outputZipFile->close();
  130. self::assertCorrectZipArchive($this->outputFilename);
  131. }
  132. /**
  133. * Create archive and add directory with ignore files list.
  134. */
  135. public function testAddDirAndIgnoreFiles()
  136. {
  137. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  138. $recursive = false;
  139. $outputZipFile = new ZipOutputFile();
  140. $ignoreFiles = ['tests/', '.git/', 'composer.lock', 'vendor/', ".idea/"];
  141. $moveToPath = 'PhpZip Library';
  142. $outputZipFile->addDir($inputDir, $recursive, $moveToPath, $ignoreFiles);
  143. $outputZipFile->saveAsFile($this->outputFilename);
  144. $outputZipFile->close();
  145. self::assertCorrectZipArchive($this->outputFilename);
  146. }
  147. /**
  148. * Create archive and add directory recursively with ignore files list.
  149. */
  150. public function testAddDirAndIgnoreFilesRecursively()
  151. {
  152. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  153. $recursive = true;
  154. $outputZipFile = new ZipOutputFile();
  155. $ignoreFiles = ['tests/', '.git/', 'composer.lock', 'vendor/', ".idea/copyright/"];
  156. $moveToPath = 'PhpZip Library';
  157. $outputZipFile->addDir($inputDir, $recursive, $moveToPath, $ignoreFiles);
  158. $outputZipFile->saveAsFile($this->outputFilename);
  159. $outputZipFile->close();
  160. self::assertCorrectZipArchive($this->outputFilename);
  161. }
  162. /**
  163. * Create archive and add files from glob pattern
  164. */
  165. public function testAddFilesFromGlob()
  166. {
  167. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  168. $moveToPath = null;
  169. $recursive = false;
  170. $outputZipFile = new ZipOutputFile();
  171. $outputZipFile->addFilesFromGlob($inputDir, '**.{php,xml}', $moveToPath, $recursive);
  172. $outputZipFile->saveAsFile($this->outputFilename);
  173. $outputZipFile->close();
  174. self::assertCorrectZipArchive($this->outputFilename);
  175. }
  176. /**
  177. * Create archive and add recursively files from glob pattern
  178. */
  179. public function testAddFilesFromGlobRecursive()
  180. {
  181. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  182. $moveToPath = "PhpZip Library";
  183. $recursive = true;
  184. $outputZipFile = new ZipOutputFile();
  185. $outputZipFile->addFilesFromGlob($inputDir, '**.{php,xml}', $recursive, $moveToPath);
  186. $outputZipFile->saveAsFile($this->outputFilename);
  187. $outputZipFile->close();
  188. self::assertCorrectZipArchive($this->outputFilename);
  189. }
  190. /**
  191. * Create archive and add files from regex pattern
  192. */
  193. public function testAddFilesFromRegex()
  194. {
  195. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  196. $moveToPath = "Test";
  197. $recursive = false;
  198. $outputZipFile = new ZipOutputFile();
  199. $outputZipFile->addFilesFromRegex($inputDir, '~\.(xml|php)$~i', $recursive, $moveToPath);
  200. $outputZipFile->saveAsFile($this->outputFilename);
  201. $outputZipFile->close();
  202. self::assertCorrectZipArchive($this->outputFilename);
  203. }
  204. /**
  205. * Create archive and add files recursively from regex pattern
  206. */
  207. public function testAddFilesFromRegexRecursive()
  208. {
  209. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  210. $moveToPath = "Test";
  211. $recursive = true;
  212. $outputZipFile = new ZipOutputFile();
  213. $outputZipFile->addFilesFromRegex($inputDir, '~\.(xml|php)$~i', $recursive, $moveToPath);
  214. $outputZipFile->saveAsFile($this->outputFilename);
  215. $outputZipFile->close();
  216. self::assertCorrectZipArchive($this->outputFilename);
  217. }
  218. /**
  219. * Rename zip entry name.
  220. */
  221. public function testRename()
  222. {
  223. $oldName = basename(__FILE__);
  224. $newName = 'tests/' . $oldName;
  225. $outputZipFile = new ZipOutputFile();
  226. $outputZipFile->addDir(__DIR__);
  227. $outputZipFile->saveAsFile($this->outputFilename);
  228. $outputZipFile->close();
  229. self::assertCorrectZipArchive($this->outputFilename);
  230. $zipFile = ZipFile::openFromFile($this->outputFilename);
  231. $outputZipFile = new ZipOutputFile($zipFile);
  232. $outputZipFile->rename($oldName, $newName);
  233. $outputZipFile->saveAsFile($this->outputFilename);
  234. $zipFile->close();
  235. self::assertCorrectZipArchive($this->outputFilename);
  236. $zipFile = ZipFile::openFromFile($this->outputFilename);
  237. self::assertFalse($zipFile->hasEntry($oldName));
  238. self::assertTrue($zipFile->hasEntry($newName));
  239. $zipFile->close();
  240. }
  241. /**
  242. * Delete entry from name.
  243. */
  244. public function testDeleteFromName()
  245. {
  246. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  247. $deleteEntryName = 'composer.json';
  248. $outputZipFile = new ZipOutputFile();
  249. $outputZipFile->addDir($inputDir, false);
  250. $outputZipFile->saveAsFile($this->outputFilename);
  251. $outputZipFile->close();
  252. self::assertCorrectZipArchive($this->outputFilename);
  253. $zipFile = ZipFile::openFromFile($this->outputFilename);
  254. $outputZipFile = new ZipOutputFile($zipFile);
  255. $outputZipFile->deleteFromName($deleteEntryName);
  256. $outputZipFile->saveAsFile($this->outputFilename);
  257. $zipFile->close();
  258. self::assertCorrectZipArchive($this->outputFilename);
  259. $zipFile = ZipFile::openFromFile($this->outputFilename);
  260. self::assertFalse($zipFile->hasEntry($deleteEntryName));
  261. $zipFile->close();
  262. }
  263. /**
  264. * Delete zip entries from glob pattern
  265. */
  266. public function testDeleteFromGlob()
  267. {
  268. $inputDir = dirname(dirname(__DIR__));
  269. $outputZipFile = new ZipOutputFile();
  270. $outputZipFile->addFilesFromGlob($inputDir, '**.{php,xml,json}', true);
  271. $outputZipFile->saveAsFile($this->outputFilename);
  272. $outputZipFile->close();
  273. self::assertCorrectZipArchive($this->outputFilename);
  274. $zipFile = ZipFile::openFromFile($this->outputFilename);
  275. $outputZipFile = new ZipOutputFile($zipFile);
  276. $outputZipFile->deleteFromGlob('**.{xml,json}');
  277. $outputZipFile->saveAsFile($this->outputFilename);
  278. $zipFile->close();
  279. self::assertCorrectZipArchive($this->outputFilename);
  280. $zipFile = ZipFile::openFromFile($this->outputFilename);
  281. self::assertFalse($zipFile->hasEntry('composer.json'));
  282. self::assertFalse($zipFile->hasEntry('bootstrap.xml'));
  283. $zipFile->close();
  284. }
  285. /**
  286. * Delete entries from regex pattern
  287. */
  288. public function testDeleteFromRegex()
  289. {
  290. $inputDir = dirname(dirname(__DIR__));
  291. $outputZipFile = new ZipOutputFile();
  292. $outputZipFile->addFilesFromRegex($inputDir, '~\.(xml|php|json)$~i', true, 'Path');
  293. $outputZipFile->saveAsFile($this->outputFilename);
  294. $outputZipFile->close();
  295. self::assertCorrectZipArchive($this->outputFilename);
  296. $zipFile = ZipFile::openFromFile($this->outputFilename);
  297. $outputZipFile = new ZipOutputFile($zipFile);
  298. $outputZipFile->deleteFromRegex('~\.(json)$~i');
  299. $outputZipFile->saveAsFile($this->outputFilename);
  300. $zipFile->close();
  301. self::assertCorrectZipArchive($this->outputFilename);
  302. $zipFile = ZipFile::openFromFile($this->outputFilename);
  303. self::assertFalse($zipFile->hasEntry('Path/composer.json'));
  304. self::assertTrue($zipFile->hasEntry('Path/bootstrap.xml'));
  305. $zipFile->close();
  306. }
  307. /**
  308. * Delete all entries
  309. */
  310. public function testDeleteAll()
  311. {
  312. $outputZipFile = new ZipOutputFile();
  313. $outputZipFile->addDir(__DIR__);
  314. $outputZipFile->saveAsFile($this->outputFilename);
  315. $outputZipFile->close();
  316. self::assertCorrectZipArchive($this->outputFilename);
  317. $zipFile = ZipFile::openFromFile($this->outputFilename);
  318. self::assertTrue($zipFile->count() > 0);
  319. $outputZipFile = new ZipOutputFile($zipFile);
  320. $outputZipFile->deleteAll();
  321. $outputZipFile->saveAsFile($this->outputFilename);
  322. $zipFile->close();
  323. self::assertCorrectEmptyZip($this->outputFilename);
  324. $zipFile = ZipFile::openFromFile($this->outputFilename);
  325. self::assertEquals($zipFile->count(), 0);
  326. $zipFile->close();
  327. }
  328. /**
  329. * Test zip archive comment.
  330. */
  331. public function testArchiveComment()
  332. {
  333. $comment = "This zip file comment" . PHP_EOL
  334. . "Αυτό το σχόλιο αρχείο zip" . PHP_EOL
  335. . "Это комментарий zip архива" . PHP_EOL
  336. . "這個ZIP文件註釋" . PHP_EOL
  337. . "ეს zip ფაილის კომენტარი" . PHP_EOL
  338. . "このzipファイルにコメント" . PHP_EOL
  339. . "ความคิดเห็นนี้ไฟล์ซิป";
  340. $outputZipFile = new ZipOutputFile();
  341. $outputZipFile->setComment($comment);
  342. $outputZipFile->addFromFile(__FILE__);
  343. $outputZipFile->saveAsFile($this->outputFilename);
  344. $outputZipFile->close();
  345. self::assertCorrectZipArchive($this->outputFilename);
  346. $zipFile = ZipFile::openFromFile($this->outputFilename);
  347. self::assertEquals($zipFile->getComment(), $comment);
  348. // remove comment
  349. $outputZipFile = ZipOutputFile::openFromZipFile($zipFile);
  350. $outputZipFile->setComment(null);
  351. $outputZipFile->saveAsFile($this->outputFilename);
  352. $outputZipFile->close();
  353. $zipFile->close();
  354. self::assertCorrectZipArchive($this->outputFilename);
  355. // check empty comment
  356. $zipFile = ZipFile::openFromFile($this->outputFilename);
  357. self::assertEquals($zipFile->getComment(), "");
  358. $zipFile->close();
  359. }
  360. /**
  361. * Test very long archive comment.
  362. *
  363. * @expectedException \PhpZip\Exception\IllegalArgumentException
  364. */
  365. public function testVeryLongArchiveComment()
  366. {
  367. $comment = "Very long comment" . PHP_EOL .
  368. "Очень длинный комментарий" . PHP_EOL;
  369. $comment = str_repeat($comment, ceil(0xffff / strlen($comment)) + strlen($comment) + 1);
  370. $outputZipFile = new ZipOutputFile();
  371. $outputZipFile->setComment($comment);
  372. }
  373. /**
  374. * Test zip entry comment.
  375. */
  376. public function testEntryComment()
  377. {
  378. $entries = [
  379. '文件1.txt' => [
  380. 'data' => CryptoUtil::randomBytes(255),
  381. 'comment' => "這是註釋的條目。",
  382. ],
  383. 'file2.txt' => [
  384. 'data' => CryptoUtil::randomBytes(255),
  385. 'comment' => null
  386. ],
  387. 'file3.txt' => [
  388. 'data' => CryptoUtil::randomBytes(255),
  389. 'comment' => CryptoUtil::randomBytes(255),
  390. ],
  391. 'file4.txt' => [
  392. 'data' => CryptoUtil::randomBytes(255),
  393. 'comment' => "Комментарий файла"
  394. ],
  395. 'file5.txt' => [
  396. 'data' => CryptoUtil::randomBytes(255),
  397. 'comment' => "ไฟล์แสดงความคิดเห็น"
  398. ],
  399. 'file6 emoji 🙍🏼.txt' => [
  400. 'data' => CryptoUtil::randomBytes(255),
  401. 'comment' => "Emoji comment file - 😀 ⛈ ❤️ 🤴🏽"
  402. ],
  403. ];
  404. $outputZipFile = new ZipOutputFile();
  405. foreach ($entries as $entryName => $item) {
  406. $outputZipFile->addFromString($entryName, $item['data']);
  407. $outputZipFile->setEntryComment($entryName, $item['comment']);
  408. }
  409. $outputZipFile->saveAsFile($this->outputFilename);
  410. $outputZipFile->close();
  411. self::assertCorrectZipArchive($this->outputFilename);
  412. $zipFile = ZipFile::openFromFile($this->outputFilename);
  413. foreach ($zipFile->getListFiles() as $entryName) {
  414. $entriesItem = $entries[$entryName];
  415. self::assertNotEmpty($entriesItem);
  416. self::assertEquals($zipFile->getEntryContent($entryName), $entriesItem['data']);
  417. self::assertEquals($zipFile->getEntryComment($entryName), (string)$entriesItem['comment']);
  418. }
  419. $zipFile->close();
  420. }
  421. /**
  422. * Test zip entry very long comment.
  423. *
  424. * @expectedException \PhpZip\Exception\ZipException
  425. */
  426. public function testVeryLongEntryComment()
  427. {
  428. $comment = "Very long comment" . PHP_EOL .
  429. "Очень длинный комментарий" . PHP_EOL;
  430. $comment = str_repeat($comment, ceil(0xffff / strlen($comment)) + strlen($comment) + 1);
  431. $outputZipFile = new ZipOutputFile();
  432. $outputZipFile->addFromFile(__FILE__, 'test');
  433. $outputZipFile->setEntryComment('test', $comment);
  434. }
  435. /**
  436. * Test set illegal compression method.
  437. *
  438. * @expectedException \PhpZip\Exception\IllegalArgumentException
  439. */
  440. public function testIllegalCompressionMethod()
  441. {
  442. $outputZipFile = new ZipOutputFile();
  443. $outputZipFile->addFromFile(__FILE__, null, ZipEntry::WINZIP_AES);
  444. }
  445. /**
  446. * Test all available support compression methods.
  447. */
  448. public function testCompressionMethod()
  449. {
  450. $entries = [
  451. '1' => [
  452. 'data' => CryptoUtil::randomBytes(255),
  453. 'method' => ZipEntry::METHOD_STORED,
  454. ],
  455. '2' => [
  456. 'data' => CryptoUtil::randomBytes(255),
  457. 'method' => ZipEntry::METHOD_DEFLATED,
  458. ],
  459. ];
  460. if (extension_loaded("bz2")) {
  461. $entries['3'] = [
  462. 'data' => CryptoUtil::randomBytes(255),
  463. 'method' => ZipEntry::METHOD_BZIP2,
  464. ];
  465. }
  466. $outputZipFile = new ZipOutputFile();
  467. foreach ($entries as $entryName => $item) {
  468. $outputZipFile->addFromString($entryName, $item['data'], $item['method']);
  469. }
  470. $outputZipFile->saveAsFile($this->outputFilename);
  471. $outputZipFile->close();
  472. self::assertCorrectZipArchive($this->outputFilename);
  473. $zipFile = ZipFile::openFromFile($this->outputFilename);
  474. $outputZipFile = ZipOutputFile::openFromZipFile($zipFile);
  475. $outputZipFile->setLevel(ZipOutputFile::LEVEL_BEST_COMPRESSION);
  476. foreach ($zipFile->getRawEntries() as $entry) {
  477. self::assertEquals($zipFile->getEntryContent($entry->getName()), $entries[$entry->getName()]['data']);
  478. self::assertEquals($entry->getMethod(), $entries[$entry->getName()]['method']);
  479. switch ($entry->getMethod()) {
  480. case ZipEntry::METHOD_STORED:
  481. $entries[$entry->getName()]['method'] = ZipEntry::METHOD_DEFLATED;
  482. $outputZipFile->setCompressionMethod($entry->getName(), ZipEntry::METHOD_DEFLATED);
  483. break;
  484. case ZipEntry::METHOD_DEFLATED:
  485. $entries[$entry->getName()]['method'] = ZipEntry::METHOD_STORED;
  486. $outputZipFile->setCompressionMethod($entry->getName(), ZipEntry::METHOD_STORED);
  487. break;
  488. }
  489. }
  490. $outputZipFile->saveAsFile($this->outputFilename);
  491. $outputZipFile->close();
  492. $zipFile->close();
  493. self::assertCorrectZipArchive($this->outputFilename);
  494. $zipFile = ZipFile::openFromFile($this->outputFilename);
  495. foreach ($zipFile->getRawEntries() as $entry) {
  496. $actualEntry = $entries[$entry->getName()];
  497. self::assertEquals($zipFile->getEntryContent($entry->getName()), $actualEntry['data']);
  498. self::assertEquals($entry->getMethod(), $actualEntry['method']);
  499. }
  500. $zipFile->close();
  501. }
  502. /**
  503. * Test extract all files.
  504. */
  505. public function testExtract()
  506. {
  507. $entries = [
  508. 'test1.txt' => CryptoUtil::randomBytes(255),
  509. 'test2.txt' => CryptoUtil::randomBytes(255),
  510. 'test/test 2/test3.txt' => CryptoUtil::randomBytes(255),
  511. 'test empty/dir' => null,
  512. ];
  513. $outputFolderInput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  514. if (!is_dir($outputFolderInput)) {
  515. mkdir($outputFolderInput, 0755, true);
  516. }
  517. $outputFolderOutput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  518. if (!is_dir($outputFolderOutput)) {
  519. mkdir($outputFolderOutput, 0755, true);
  520. }
  521. $outputZipFile = new ZipOutputFile();
  522. foreach ($entries as $entryName => $value) {
  523. if ($value === null) {
  524. $outputZipFile->addEmptyDir($entryName);
  525. } else {
  526. $outputZipFile->addFromString($entryName, $value);
  527. }
  528. }
  529. $outputZipFile->saveAsFile($this->outputFilename);
  530. $outputZipFile->close();
  531. $zipFile = ZipFile::openFromFile($this->outputFilename);
  532. $zipFile->extractTo($outputFolderInput);
  533. $outputZipFile = new ZipOutputFile($zipFile);
  534. $outputZipFile->extractTo($outputFolderOutput);
  535. foreach ($entries as $entryName => $value) {
  536. $fullInputFilename = $outputFolderInput . DIRECTORY_SEPARATOR . $entryName;
  537. $fullOutputFilename = $outputFolderOutput . DIRECTORY_SEPARATOR . $entryName;
  538. if ($value === null) {
  539. self::assertTrue(is_dir($fullInputFilename));
  540. self::assertTrue(is_dir($fullOutputFilename));
  541. self::assertTrue(FilesUtil::isEmptyDir($fullInputFilename));
  542. self::assertTrue(FilesUtil::isEmptyDir($fullOutputFilename));
  543. } else {
  544. self::assertTrue(is_file($fullInputFilename));
  545. self::assertTrue(is_file($fullOutputFilename));
  546. $contentInput = file_get_contents($fullInputFilename);
  547. $contentOutput = file_get_contents($fullOutputFilename);
  548. self::assertEquals($contentInput, $value);
  549. self::assertEquals($contentOutput, $value);
  550. self::assertEquals($contentInput, $contentOutput);
  551. }
  552. }
  553. $outputZipFile->close();
  554. $zipFile->close();
  555. FilesUtil::removeDir($outputFolderInput);
  556. FilesUtil::removeDir($outputFolderOutput);
  557. }
  558. /**
  559. * Test extract some files
  560. */
  561. public function testExtractSomeFiles()
  562. {
  563. $entries = [
  564. 'test1.txt' => CryptoUtil::randomBytes(255),
  565. 'test2.txt' => CryptoUtil::randomBytes(255),
  566. 'test3.txt' => CryptoUtil::randomBytes(255),
  567. 'test4.txt' => CryptoUtil::randomBytes(255),
  568. 'test5.txt' => CryptoUtil::randomBytes(255),
  569. 'test/test/test.txt' => CryptoUtil::randomBytes(255),
  570. 'test/test/test 2.txt' => CryptoUtil::randomBytes(255),
  571. 'test empty/dir/' => null,
  572. 'test empty/dir2/' => null,
  573. ];
  574. $extractEntries = ['test1.txt', 'test3.txt', 'test5.txt', 'test/test/test 2.txt', 'test empty/dir2/'];
  575. $outputFolderInput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  576. if (!is_dir($outputFolderInput)) {
  577. mkdir($outputFolderInput, 0755, true);
  578. }
  579. $outputFolderOutput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  580. if (!is_dir($outputFolderOutput)) {
  581. mkdir($outputFolderOutput, 0755, true);
  582. }
  583. $outputZipFile = new ZipOutputFile();
  584. foreach ($entries as $entryName => $value) {
  585. if ($value === null) {
  586. $outputZipFile->addEmptyDir($entryName);
  587. } else {
  588. $outputZipFile->addFromString($entryName, $value);
  589. }
  590. }
  591. $outputZipFile->saveAsFile($this->outputFilename);
  592. $outputZipFile->close();
  593. $zipFile = ZipFile::openFromFile($this->outputFilename);
  594. $zipFile->extractTo($outputFolderInput, $extractEntries);
  595. $outputZipFile = new ZipOutputFile($zipFile);
  596. $outputZipFile->extractTo($outputFolderOutput, $extractEntries);
  597. foreach ($entries as $entryName => $value) {
  598. $fullInputFilename = $outputFolderInput . DIRECTORY_SEPARATOR . $entryName;
  599. $fullOutputFilename = $outputFolderOutput . DIRECTORY_SEPARATOR . $entryName;
  600. if (in_array($entryName, $extractEntries)) {
  601. if ($value === null) {
  602. self::assertTrue(is_dir($fullInputFilename));
  603. self::assertTrue(is_dir($fullOutputFilename));
  604. self::assertTrue(FilesUtil::isEmptyDir($fullInputFilename));
  605. self::assertTrue(FilesUtil::isEmptyDir($fullOutputFilename));
  606. } else {
  607. self::assertTrue(is_file($fullInputFilename));
  608. self::assertTrue(is_file($fullOutputFilename));
  609. $contentInput = file_get_contents($fullInputFilename);
  610. $contentOutput = file_get_contents($fullOutputFilename);
  611. self::assertEquals($contentInput, $value);
  612. self::assertEquals($contentOutput, $value);
  613. self::assertEquals($contentInput, $contentOutput);
  614. }
  615. } else {
  616. if ($value === null) {
  617. self::assertFalse(is_dir($fullInputFilename));
  618. self::assertFalse(is_dir($fullOutputFilename));
  619. } else {
  620. self::assertFalse(is_file($fullInputFilename));
  621. self::assertFalse(is_file($fullOutputFilename));
  622. }
  623. }
  624. }
  625. $outputZipFile->close();
  626. $zipFile->close();
  627. FilesUtil::removeDir($outputFolderInput);
  628. FilesUtil::removeDir($outputFolderOutput);
  629. }
  630. /**
  631. * Test archive password.
  632. */
  633. public function testSetPassword()
  634. {
  635. $password = CryptoUtil::randomBytes(100);
  636. $badPassword = "sdgt43r23wefe";
  637. $outputZip = ZipOutputFile::create();
  638. $outputZip->addDir(__DIR__);
  639. $outputZip->setPassword($password, ZipEntry::ENCRYPTION_METHOD_TRADITIONAL);
  640. $outputZip->saveAsFile($this->outputFilename);
  641. $outputZip->close();
  642. $zipFile = ZipFile::openFromFile($this->outputFilename);
  643. // set bad password Traditional Encryption
  644. $zipFile->setPassword($badPassword);
  645. foreach ($zipFile->getListFiles() as $entryName) {
  646. try {
  647. $zipFile->getEntryContent($entryName);
  648. self::fail("Expected Exception has not been raised.");
  649. } catch (ZipAuthenticationException $ae) {
  650. self::assertNotNull($ae);
  651. }
  652. }
  653. // set correct password
  654. $zipFile->setPassword($password);
  655. foreach ($zipFile->getAllInfo() as $info) {
  656. self::assertTrue($info->isEncrypted());
  657. self::assertContains('ZipCrypto', $info->getMethod());
  658. $decryptContent = $zipFile->getEntryContent($info->getPath());
  659. self::assertNotEmpty($decryptContent);
  660. self::assertContains('<?php', $decryptContent);
  661. }
  662. // change encryption method
  663. $outputZip = ZipOutputFile::openFromZipFile($zipFile);
  664. $outputZip->setPassword($password, ZipEntry::ENCRYPTION_METHOD_WINZIP_AES);
  665. $outputZip->saveAsFile($this->outputFilename);
  666. $outputZip->close();
  667. $zipFile->close();
  668. // check from WinZip AES encryption
  669. $zipFile = ZipFile::openFromFile($this->outputFilename);
  670. // set bad password WinZip AES
  671. $zipFile->setPassword($badPassword);
  672. foreach ($zipFile->getListFiles() as $entryName) {
  673. try {
  674. $zipFile->getEntryContent($entryName);
  675. self::fail("Expected Exception has not been raised.");
  676. } catch (ZipAuthenticationException $ae) {
  677. self::assertNotNull($ae);
  678. }
  679. }
  680. // set correct password WinZip AES
  681. $zipFile->setPassword($password);
  682. foreach ($zipFile->getAllInfo() as $info) {
  683. self::assertTrue($info->isEncrypted());
  684. self::assertContains('WinZip', $info->getMethod());
  685. $decryptContent = $zipFile->getEntryContent($info->getPath());
  686. self::assertNotEmpty($decryptContent);
  687. self::assertContains('<?php', $decryptContent);
  688. }
  689. // clear password
  690. $outputZip = ZipOutputFile::openFromZipFile($zipFile);
  691. $outputZip->removePasswordAllEntries();
  692. $outputZip->saveAsFile($this->outputFilename);
  693. $outputZip->close();
  694. $zipFile->close();
  695. self::assertCorrectZipArchive($this->outputFilename);
  696. // check remove password
  697. $zipFile = ZipFile::openFromFile($this->outputFilename);
  698. foreach ($zipFile->getAllInfo() as $info) {
  699. self::assertFalse($info->isEncrypted());
  700. }
  701. $zipFile->close();
  702. }
  703. /**
  704. * Test set password to some entries.
  705. */
  706. public function testSetPasswordToSomeEntries()
  707. {
  708. $entries = [
  709. 'Traditional PKWARE Encryption Test.dat' => [
  710. 'data' => CryptoUtil::randomBytes(255),
  711. 'password' => CryptoUtil::randomBytes(255),
  712. 'encryption_method' => ZipEntry::ENCRYPTION_METHOD_TRADITIONAL,
  713. 'compression_method' => ZipEntry::METHOD_DEFLATED,
  714. ],
  715. 'WinZip AES Encryption Test.dat' => [
  716. 'data' => CryptoUtil::randomBytes(255),
  717. 'password' => CryptoUtil::randomBytes(255),
  718. 'encryption_method' => ZipEntry::ENCRYPTION_METHOD_WINZIP_AES,
  719. 'compression_method' => ZipEntry::METHOD_BZIP2,
  720. ],
  721. 'Not password.dat' => [
  722. 'data' => CryptoUtil::randomBytes(255),
  723. 'password' => null,
  724. 'encryption_method' => ZipEntry::ENCRYPTION_METHOD_TRADITIONAL,
  725. 'compression_method' => ZipEntry::METHOD_STORED,
  726. ],
  727. ];
  728. $outputZip = ZipOutputFile::create();
  729. foreach ($entries as $entryName => $item) {
  730. $outputZip->addFromString($entryName, $item['data'], $item['compression_method']);
  731. if ($item['password'] !== null) {
  732. $outputZip->setEntryPassword($entryName, $item['password'], $item['encryption_method']);
  733. }
  734. }
  735. $outputZip->saveAsFile($this->outputFilename);
  736. $outputZip->close();
  737. $outputDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'zipextract' . uniqid();
  738. if (!is_dir($outputDir)) {
  739. self::assertTrue(mkdir($outputDir, 0755, true));
  740. }
  741. $zipFile = ZipFile::openFromFile($this->outputFilename);
  742. foreach ($entries as $entryName => $item) {
  743. if ($item['password'] !== null) {
  744. $zipFile->setEntryPassword($entryName, $item['password']);
  745. }
  746. }
  747. $zipFile->extractTo($outputDir);
  748. $zipFile->close();
  749. self::assertFalse(FilesUtil::isEmptyDir($outputDir));
  750. foreach ($entries as $entryName => $item) {
  751. self::assertEquals(file_get_contents($outputDir . DIRECTORY_SEPARATOR . $entryName), $item['data']);
  752. }
  753. FilesUtil::removeDir($outputDir);
  754. }
  755. /**
  756. * Test `ZipFile` implemented \ArrayAccess, \Countable and |iterator.
  757. */
  758. public function testZipFileArrayAccessAndCountableAndIterator()
  759. {
  760. $files = [];
  761. $numFiles = mt_rand(20, 100);
  762. for ($i = 0; $i < $numFiles; $i++) {
  763. $files['file' . $i . '.txt'] = CryptoUtil::randomBytes(255);
  764. }
  765. $methods = [ZipEntry::METHOD_STORED, ZipEntry::METHOD_DEFLATED];
  766. if (extension_loaded("bz2")) {
  767. $methods[] = ZipEntry::METHOD_BZIP2;
  768. }
  769. $zipOutputFile = ZipOutputFile::create();
  770. $zipOutputFile->setLevel(ZipOutputFile::LEVEL_BEST_SPEED);
  771. foreach ($files as $entryName => $content) {
  772. $zipOutputFile->addFromString($entryName, $content, $methods[array_rand($methods)]);
  773. }
  774. $zipOutputFile->saveAsFile($this->outputFilename);
  775. $zipOutputFile->close();
  776. self::assertCorrectZipArchive($this->outputFilename);
  777. $zipFile = ZipFile::openFromFile($this->outputFilename);
  778. // Test \Countable
  779. self::assertEquals($zipFile->count(), $numFiles);
  780. self::assertEquals(count($zipFile), $numFiles);
  781. // Test \ArrayAccess
  782. reset($files);
  783. foreach ($zipFile as $entryName => $content) {
  784. self::assertEquals($entryName, key($files));
  785. self::assertEquals($content, current($files));
  786. next($files);
  787. }
  788. // Test \Iterator
  789. reset($files);
  790. $iterator = new \ArrayIterator($zipFile);
  791. $iterator->rewind();
  792. while ($iterator->valid()) {
  793. $key = $iterator->key();
  794. $value = $iterator->current();
  795. self::assertEquals($key, key($files));
  796. self::assertEquals($value, current($files));
  797. next($files);
  798. $iterator->next();
  799. }
  800. $zipFile->close();
  801. }
  802. /**
  803. * Test `ZipOutputFile` implemented \ArrayAccess, \Countable and |iterator.
  804. */
  805. public function testZipOutputFileArrayAccessAndCountableAndIterator()
  806. {
  807. $files = [];
  808. $numFiles = mt_rand(20, 100);
  809. for ($i = 0; $i < $numFiles; $i++) {
  810. $files['file' . $i . '.txt'] = CryptoUtil::randomBytes(255);
  811. }
  812. $methods = [ZipEntry::METHOD_STORED, ZipEntry::METHOD_DEFLATED];
  813. if (extension_loaded("bz2")) {
  814. $methods[] = ZipEntry::METHOD_BZIP2;
  815. }
  816. $zipOutputFile = ZipOutputFile::create();
  817. $zipOutputFile->setLevel(ZipOutputFile::LEVEL_BEST_SPEED);
  818. foreach ($files as $entryName => $content) {
  819. $zipOutputFile->addFromString($entryName, $content, $methods[array_rand($methods)]);
  820. }
  821. $zipOutputFile->saveAsFile($this->outputFilename);
  822. $zipOutputFile->close();
  823. self::assertCorrectZipArchive($this->outputFilename);
  824. $zipFile = ZipFile::openFromFile($this->outputFilename);
  825. $zipOutputFile = ZipOutputFile::openFromZipFile($zipFile);
  826. // Test \Countable
  827. self::assertEquals($zipOutputFile->count(), $numFiles);
  828. self::assertEquals(count($zipOutputFile), $numFiles);
  829. // Test \ArrayAccess
  830. reset($files);
  831. foreach ($zipOutputFile as $entryName => $content) {
  832. self::assertEquals($entryName, key($files));
  833. self::assertEquals($content, current($files));
  834. next($files);
  835. }
  836. // Test \Iterator
  837. reset($files);
  838. $iterator = new \ArrayIterator($zipOutputFile);
  839. $iterator->rewind();
  840. while ($iterator->valid()) {
  841. $key = $iterator->key();
  842. $value = $iterator->current();
  843. self::assertEquals($key, key($files));
  844. self::assertEquals($value, current($files));
  845. next($files);
  846. $iterator->next();
  847. }
  848. // Test set and unset
  849. $zipOutputFile['new entry name'] = 'content';
  850. unset($zipOutputFile['file0.txt'], $zipOutputFile['file1.txt'], $zipOutputFile['file2.txt']);
  851. $zipOutputFile->saveAsFile($this->outputFilename);
  852. $zipOutputFile->close();
  853. $zipFile->close();
  854. $zipFile = ZipFile::openFromFile($this->outputFilename);
  855. self::assertEquals($numFiles + 1 - 3, sizeof($zipFile));
  856. self::assertTrue(isset($zipFile['new entry name']));
  857. self::assertEquals($zipFile['new entry name'], 'content');
  858. self::assertFalse(isset($zipFile['file0.txt']));
  859. self::assertFalse(isset($zipFile['file1.txt']));
  860. self::assertFalse(isset($zipFile['file2.txt']));
  861. self::assertTrue(isset($zipFile['file3.txt']));
  862. $zipFile->close();
  863. }
  864. public function testZipAlign()
  865. {
  866. $zipOutputFile = ZipOutputFile::create();
  867. for ($i = 0; $i < 100; $i++) {
  868. $zipOutputFile->addFromString(
  869. 'entry' . $i . '.txt',
  870. CryptoUtil::randomBytes(mt_rand(100, 4096)),
  871. ZipEntry::METHOD_STORED
  872. );
  873. }
  874. $zipOutputFile->saveAsFile($this->outputFilename);
  875. $zipOutputFile->close();
  876. self::assertCorrectZipArchive($this->outputFilename);
  877. $result = self::doZipAlignVerify($this->outputFilename);
  878. if($result === null) return; // zip align not installed
  879. // check not zip align
  880. self::assertFalse($result, false);
  881. $zipFile = ZipFile::openFromFile($this->outputFilename);
  882. $zipOutputFile = ZipOutputFile::openFromZipFile($zipFile);
  883. $zipOutputFile->setZipAlign(4);
  884. $zipOutputFile->saveAsFile($this->outputFilename);
  885. $zipOutputFile->close();
  886. $zipFile->close();
  887. self::assertCorrectZipArchive($this->outputFilename);
  888. $result = self::doZipAlignVerify($this->outputFilename);
  889. self::assertNotNull($result);
  890. // check zip align
  891. self::assertTrue($result);
  892. }
  893. /**
  894. * Test support ZIP64 ext (slow test - normal).
  895. */
  896. public function testCreateAndOpenZip64Ext()
  897. {
  898. $countFiles = 0xffff + 1;
  899. $outputZipFile = ZipOutputFile::create();
  900. for ($i = 0; $i < $countFiles; $i++) {
  901. $outputZipFile->addFromString($i . '.txt', $i, ZipEntry::METHOD_STORED);
  902. }
  903. $outputZipFile->saveAsFile($this->outputFilename);
  904. $outputZipFile->close();
  905. self::assertCorrectZipArchive($this->outputFilename);
  906. $zipFile = ZipFile::openFromFile($this->outputFilename);
  907. self::assertEquals($zipFile->count(), $countFiles);
  908. foreach ($zipFile->getListFiles() as $entry) {
  909. $zipFile->getEntryContent($entry);
  910. }
  911. $zipFile->close();
  912. }
  913. }