2
0

ZipTest.php 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  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. $outputZipFile = ZipOutputFile::openFromFile($this->outputFilename);
  231. $outputZipFile->rename($oldName, $newName);
  232. $outputZipFile->saveAsFile($this->outputFilename);
  233. self::assertCorrectZipArchive($this->outputFilename);
  234. $zipFile = ZipFile::openFromFile($this->outputFilename);
  235. self::assertFalse($zipFile->hasEntry($oldName));
  236. self::assertTrue($zipFile->hasEntry($newName));
  237. $zipFile->close();
  238. }
  239. /**
  240. * Delete entry from name.
  241. */
  242. public function testDeleteFromName()
  243. {
  244. $inputDir = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
  245. $deleteEntryName = 'composer.json';
  246. $outputZipFile = new ZipOutputFile();
  247. $outputZipFile->addDir($inputDir, false);
  248. $outputZipFile->saveAsFile($this->outputFilename);
  249. $outputZipFile->close();
  250. self::assertCorrectZipArchive($this->outputFilename);
  251. $zipFile = ZipFile::openFromFile($this->outputFilename);
  252. $outputZipFile = $zipFile->edit();
  253. $outputZipFile->deleteFromName($deleteEntryName);
  254. $outputZipFile->saveAsFile($this->outputFilename);
  255. $zipFile->close();
  256. self::assertCorrectZipArchive($this->outputFilename);
  257. $zipFile = ZipFile::openFromFile($this->outputFilename);
  258. self::assertFalse($zipFile->hasEntry($deleteEntryName));
  259. $zipFile->close();
  260. }
  261. /**
  262. * Delete zip entries from glob pattern
  263. */
  264. public function testDeleteFromGlob()
  265. {
  266. $inputDir = dirname(dirname(__DIR__));
  267. $outputZipFile = new ZipOutputFile();
  268. $outputZipFile->addFilesFromGlob($inputDir, '**.{php,xml,json}', true);
  269. $outputZipFile->saveAsFile($this->outputFilename);
  270. $outputZipFile->close();
  271. self::assertCorrectZipArchive($this->outputFilename);
  272. $zipFile = ZipFile::openFromFile($this->outputFilename);
  273. $outputZipFile = new ZipOutputFile($zipFile);
  274. $outputZipFile->deleteFromGlob('**.{xml,json}');
  275. $outputZipFile->saveAsFile($this->outputFilename);
  276. $zipFile->close();
  277. self::assertCorrectZipArchive($this->outputFilename);
  278. $zipFile = ZipFile::openFromFile($this->outputFilename);
  279. self::assertFalse($zipFile->hasEntry('composer.json'));
  280. self::assertFalse($zipFile->hasEntry('bootstrap.xml'));
  281. $zipFile->close();
  282. }
  283. /**
  284. * Delete entries from regex pattern
  285. */
  286. public function testDeleteFromRegex()
  287. {
  288. $inputDir = dirname(dirname(__DIR__));
  289. $outputZipFile = new ZipOutputFile();
  290. $outputZipFile->addFilesFromRegex($inputDir, '~\.(xml|php|json)$~i', true, 'Path');
  291. $outputZipFile->saveAsFile($this->outputFilename);
  292. $outputZipFile->close();
  293. self::assertCorrectZipArchive($this->outputFilename);
  294. $zipFile = ZipFile::openFromFile($this->outputFilename);
  295. $outputZipFile = new ZipOutputFile($zipFile);
  296. $outputZipFile->deleteFromRegex('~\.(json)$~i');
  297. $outputZipFile->saveAsFile($this->outputFilename);
  298. $zipFile->close();
  299. self::assertCorrectZipArchive($this->outputFilename);
  300. $zipFile = ZipFile::openFromFile($this->outputFilename);
  301. self::assertFalse($zipFile->hasEntry('Path/composer.json'));
  302. self::assertTrue($zipFile->hasEntry('Path/bootstrap.xml'));
  303. $zipFile->close();
  304. }
  305. /**
  306. * Delete all entries
  307. */
  308. public function testDeleteAll()
  309. {
  310. $outputZipFile = new ZipOutputFile();
  311. $outputZipFile->addDir(__DIR__);
  312. $outputZipFile->saveAsFile($this->outputFilename);
  313. $outputZipFile->close();
  314. self::assertCorrectZipArchive($this->outputFilename);
  315. $zipFile = ZipFile::openFromFile($this->outputFilename);
  316. self::assertTrue($zipFile->count() > 0);
  317. $outputZipFile = new ZipOutputFile($zipFile);
  318. $outputZipFile->deleteAll();
  319. $outputZipFile->saveAsFile($this->outputFilename);
  320. $zipFile->close();
  321. self::assertCorrectEmptyZip($this->outputFilename);
  322. $zipFile = ZipFile::openFromFile($this->outputFilename);
  323. self::assertEquals($zipFile->count(), 0);
  324. $zipFile->close();
  325. }
  326. /**
  327. * Test zip archive comment.
  328. */
  329. public function testArchiveComment()
  330. {
  331. $comment = "This zip file comment" . PHP_EOL
  332. . "Αυτό το σχόλιο αρχείο zip" . PHP_EOL
  333. . "Это комментарий zip архива" . PHP_EOL
  334. . "這個ZIP文件註釋" . PHP_EOL
  335. . "ეს zip ფაილის კომენტარი" . PHP_EOL
  336. . "このzipファイルにコメント" . PHP_EOL
  337. . "ความคิดเห็นนี้ไฟล์ซิป";
  338. $outputZipFile = new ZipOutputFile();
  339. $outputZipFile->setComment($comment);
  340. $outputZipFile->addFromFile(__FILE__);
  341. $outputZipFile->saveAsFile($this->outputFilename);
  342. $outputZipFile->close();
  343. self::assertCorrectZipArchive($this->outputFilename);
  344. $zipFile = ZipFile::openFromFile($this->outputFilename);
  345. self::assertEquals($zipFile->getComment(), $comment);
  346. // remove comment
  347. $outputZipFile = ZipOutputFile::openFromZipFile($zipFile);
  348. $outputZipFile->setComment(null);
  349. $outputZipFile->saveAsFile($this->outputFilename);
  350. $outputZipFile->close();
  351. $zipFile->close();
  352. self::assertCorrectZipArchive($this->outputFilename);
  353. // check empty comment
  354. $zipFile = ZipFile::openFromFile($this->outputFilename);
  355. self::assertEquals($zipFile->getComment(), "");
  356. $zipFile->close();
  357. }
  358. /**
  359. * Test very long archive comment.
  360. *
  361. * @expectedException \PhpZip\Exception\IllegalArgumentException
  362. */
  363. public function testVeryLongArchiveComment()
  364. {
  365. $comment = "Very long comment" . PHP_EOL .
  366. "Очень длинный комментарий" . PHP_EOL;
  367. $comment = str_repeat($comment, ceil(0xffff / strlen($comment)) + strlen($comment) + 1);
  368. $outputZipFile = new ZipOutputFile();
  369. $outputZipFile->setComment($comment);
  370. }
  371. /**
  372. * Test zip entry comment.
  373. */
  374. public function testEntryComment()
  375. {
  376. $entries = [
  377. '文件1.txt' => [
  378. 'data' => CryptoUtil::randomBytes(255),
  379. 'comment' => "這是註釋的條目。",
  380. ],
  381. 'file2.txt' => [
  382. 'data' => CryptoUtil::randomBytes(255),
  383. 'comment' => null
  384. ],
  385. 'file3.txt' => [
  386. 'data' => CryptoUtil::randomBytes(255),
  387. 'comment' => CryptoUtil::randomBytes(255),
  388. ],
  389. 'file4.txt' => [
  390. 'data' => CryptoUtil::randomBytes(255),
  391. 'comment' => "Комментарий файла"
  392. ],
  393. 'file5.txt' => [
  394. 'data' => CryptoUtil::randomBytes(255),
  395. 'comment' => "ไฟล์แสดงความคิดเห็น"
  396. ],
  397. 'file6 emoji 🙍🏼.txt' => [
  398. 'data' => CryptoUtil::randomBytes(255),
  399. 'comment' => "Emoji comment file - 😀 ⛈ ❤️ 🤴🏽"
  400. ],
  401. ];
  402. $outputZipFile = new ZipOutputFile();
  403. foreach ($entries as $entryName => $item) {
  404. $outputZipFile->addFromString($entryName, $item['data']);
  405. $outputZipFile->setEntryComment($entryName, $item['comment']);
  406. }
  407. $outputZipFile->saveAsFile($this->outputFilename);
  408. $outputZipFile->close();
  409. self::assertCorrectZipArchive($this->outputFilename);
  410. $zipFile = ZipFile::openFromFile($this->outputFilename);
  411. foreach ($zipFile->getListFiles() as $entryName) {
  412. $entriesItem = $entries[$entryName];
  413. self::assertNotEmpty($entriesItem);
  414. self::assertEquals($zipFile->getEntryContent($entryName), $entriesItem['data']);
  415. self::assertEquals($zipFile->getEntryComment($entryName), (string)$entriesItem['comment']);
  416. }
  417. $zipFile->close();
  418. }
  419. /**
  420. * Test zip entry very long comment.
  421. *
  422. * @expectedException \PhpZip\Exception\ZipException
  423. */
  424. public function testVeryLongEntryComment()
  425. {
  426. $comment = "Very long comment" . PHP_EOL .
  427. "Очень длинный комментарий" . PHP_EOL;
  428. $comment = str_repeat($comment, ceil(0xffff / strlen($comment)) + strlen($comment) + 1);
  429. $outputZipFile = new ZipOutputFile();
  430. $outputZipFile->addFromFile(__FILE__, 'test');
  431. $outputZipFile->setEntryComment('test', $comment);
  432. }
  433. /**
  434. * Test set illegal compression method.
  435. *
  436. * @expectedException \PhpZip\Exception\IllegalArgumentException
  437. */
  438. public function testIllegalCompressionMethod()
  439. {
  440. $outputZipFile = new ZipOutputFile();
  441. $outputZipFile->addFromFile(__FILE__, null, ZipEntry::WINZIP_AES);
  442. }
  443. /**
  444. * Test all available support compression methods.
  445. */
  446. public function testCompressionMethod()
  447. {
  448. $entries = [
  449. '1' => [
  450. 'data' => CryptoUtil::randomBytes(255),
  451. 'method' => ZipEntry::METHOD_STORED,
  452. ],
  453. '2' => [
  454. 'data' => CryptoUtil::randomBytes(255),
  455. 'method' => ZipEntry::METHOD_DEFLATED,
  456. ],
  457. ];
  458. if (extension_loaded("bz2")) {
  459. $entries['3'] = [
  460. 'data' => CryptoUtil::randomBytes(255),
  461. 'method' => ZipEntry::METHOD_BZIP2,
  462. ];
  463. }
  464. $outputZipFile = new ZipOutputFile();
  465. foreach ($entries as $entryName => $item) {
  466. $outputZipFile->addFromString($entryName, $item['data'], $item['method']);
  467. }
  468. $outputZipFile->saveAsFile($this->outputFilename);
  469. $outputZipFile->close();
  470. self::assertCorrectZipArchive($this->outputFilename);
  471. $zipFile = ZipFile::openFromFile($this->outputFilename);
  472. $outputZipFile = ZipOutputFile::openFromZipFile($zipFile);
  473. $outputZipFile->setLevel(ZipOutputFile::LEVEL_BEST_COMPRESSION);
  474. foreach ($zipFile->getRawEntries() as $entry) {
  475. self::assertEquals($zipFile->getEntryContent($entry->getName()), $entries[$entry->getName()]['data']);
  476. self::assertEquals($entry->getMethod(), $entries[$entry->getName()]['method']);
  477. switch ($entry->getMethod()) {
  478. case ZipEntry::METHOD_STORED:
  479. $entries[$entry->getName()]['method'] = ZipEntry::METHOD_DEFLATED;
  480. $outputZipFile->setCompressionMethod($entry->getName(), ZipEntry::METHOD_DEFLATED);
  481. break;
  482. case ZipEntry::METHOD_DEFLATED:
  483. $entries[$entry->getName()]['method'] = ZipEntry::METHOD_STORED;
  484. $outputZipFile->setCompressionMethod($entry->getName(), ZipEntry::METHOD_STORED);
  485. break;
  486. }
  487. }
  488. $outputZipFile->saveAsFile($this->outputFilename);
  489. $outputZipFile->close();
  490. $zipFile->close();
  491. self::assertCorrectZipArchive($this->outputFilename);
  492. $zipFile = ZipFile::openFromFile($this->outputFilename);
  493. foreach ($zipFile->getRawEntries() as $entry) {
  494. $actualEntry = $entries[$entry->getName()];
  495. self::assertEquals($zipFile->getEntryContent($entry->getName()), $actualEntry['data']);
  496. self::assertEquals($entry->getMethod(), $actualEntry['method']);
  497. }
  498. $zipFile->close();
  499. }
  500. /**
  501. * Test extract all files.
  502. */
  503. public function testExtract()
  504. {
  505. $entries = [
  506. 'test1.txt' => CryptoUtil::randomBytes(255),
  507. 'test2.txt' => CryptoUtil::randomBytes(255),
  508. 'test/test 2/test3.txt' => CryptoUtil::randomBytes(255),
  509. 'test empty/dir' => null,
  510. ];
  511. $outputFolderInput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  512. if (!is_dir($outputFolderInput)) {
  513. mkdir($outputFolderInput, 0755, true);
  514. }
  515. $outputFolderOutput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  516. if (!is_dir($outputFolderOutput)) {
  517. mkdir($outputFolderOutput, 0755, true);
  518. }
  519. $outputZipFile = new ZipOutputFile();
  520. foreach ($entries as $entryName => $value) {
  521. if ($value === null) {
  522. $outputZipFile->addEmptyDir($entryName);
  523. } else {
  524. $outputZipFile->addFromString($entryName, $value);
  525. }
  526. }
  527. $outputZipFile->saveAsFile($this->outputFilename);
  528. $outputZipFile->close();
  529. $zipFile = ZipFile::openFromFile($this->outputFilename);
  530. $zipFile->extractTo($outputFolderInput);
  531. $outputZipFile = new ZipOutputFile($zipFile);
  532. $outputZipFile->extractTo($outputFolderOutput);
  533. foreach ($entries as $entryName => $value) {
  534. $fullInputFilename = $outputFolderInput . DIRECTORY_SEPARATOR . $entryName;
  535. $fullOutputFilename = $outputFolderOutput . DIRECTORY_SEPARATOR . $entryName;
  536. if ($value === null) {
  537. self::assertTrue(is_dir($fullInputFilename));
  538. self::assertTrue(is_dir($fullOutputFilename));
  539. self::assertTrue(FilesUtil::isEmptyDir($fullInputFilename));
  540. self::assertTrue(FilesUtil::isEmptyDir($fullOutputFilename));
  541. } else {
  542. self::assertTrue(is_file($fullInputFilename));
  543. self::assertTrue(is_file($fullOutputFilename));
  544. $contentInput = file_get_contents($fullInputFilename);
  545. $contentOutput = file_get_contents($fullOutputFilename);
  546. self::assertEquals($contentInput, $value);
  547. self::assertEquals($contentOutput, $value);
  548. self::assertEquals($contentInput, $contentOutput);
  549. }
  550. }
  551. $outputZipFile->close();
  552. $zipFile->close();
  553. FilesUtil::removeDir($outputFolderInput);
  554. FilesUtil::removeDir($outputFolderOutput);
  555. }
  556. /**
  557. * Test extract some files
  558. */
  559. public function testExtractSomeFiles()
  560. {
  561. $entries = [
  562. 'test1.txt' => CryptoUtil::randomBytes(255),
  563. 'test2.txt' => CryptoUtil::randomBytes(255),
  564. 'test3.txt' => CryptoUtil::randomBytes(255),
  565. 'test4.txt' => CryptoUtil::randomBytes(255),
  566. 'test5.txt' => CryptoUtil::randomBytes(255),
  567. 'test/test/test.txt' => CryptoUtil::randomBytes(255),
  568. 'test/test/test 2.txt' => CryptoUtil::randomBytes(255),
  569. 'test empty/dir/' => null,
  570. 'test empty/dir2/' => null,
  571. ];
  572. $extractEntries = ['test1.txt', 'test3.txt', 'test5.txt', 'test/test/test 2.txt', 'test empty/dir2/'];
  573. $outputFolderInput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  574. if (!is_dir($outputFolderInput)) {
  575. mkdir($outputFolderInput, 0755, true);
  576. }
  577. $outputFolderOutput = sys_get_temp_dir() . '/zipExtract' . uniqid();
  578. if (!is_dir($outputFolderOutput)) {
  579. mkdir($outputFolderOutput, 0755, true);
  580. }
  581. $outputZipFile = new ZipOutputFile();
  582. foreach ($entries as $entryName => $value) {
  583. if ($value === null) {
  584. $outputZipFile->addEmptyDir($entryName);
  585. } else {
  586. $outputZipFile->addFromString($entryName, $value);
  587. }
  588. }
  589. $outputZipFile->saveAsFile($this->outputFilename);
  590. $outputZipFile->close();
  591. $zipFile = ZipFile::openFromFile($this->outputFilename);
  592. $zipFile->extractTo($outputFolderInput, $extractEntries);
  593. $outputZipFile = new ZipOutputFile($zipFile);
  594. $outputZipFile->extractTo($outputFolderOutput, $extractEntries);
  595. foreach ($entries as $entryName => $value) {
  596. $fullInputFilename = $outputFolderInput . DIRECTORY_SEPARATOR . $entryName;
  597. $fullOutputFilename = $outputFolderOutput . DIRECTORY_SEPARATOR . $entryName;
  598. if (in_array($entryName, $extractEntries)) {
  599. if ($value === null) {
  600. self::assertTrue(is_dir($fullInputFilename));
  601. self::assertTrue(is_dir($fullOutputFilename));
  602. self::assertTrue(FilesUtil::isEmptyDir($fullInputFilename));
  603. self::assertTrue(FilesUtil::isEmptyDir($fullOutputFilename));
  604. } else {
  605. self::assertTrue(is_file($fullInputFilename));
  606. self::assertTrue(is_file($fullOutputFilename));
  607. $contentInput = file_get_contents($fullInputFilename);
  608. $contentOutput = file_get_contents($fullOutputFilename);
  609. self::assertEquals($contentInput, $value);
  610. self::assertEquals($contentOutput, $value);
  611. self::assertEquals($contentInput, $contentOutput);
  612. }
  613. } else {
  614. if ($value === null) {
  615. self::assertFalse(is_dir($fullInputFilename));
  616. self::assertFalse(is_dir($fullOutputFilename));
  617. } else {
  618. self::assertFalse(is_file($fullInputFilename));
  619. self::assertFalse(is_file($fullOutputFilename));
  620. }
  621. }
  622. }
  623. $outputZipFile->close();
  624. $zipFile->close();
  625. FilesUtil::removeDir($outputFolderInput);
  626. FilesUtil::removeDir($outputFolderOutput);
  627. }
  628. /**
  629. * Test archive password.
  630. */
  631. public function testSetPassword()
  632. {
  633. $password = base64_encode(CryptoUtil::randomBytes(100));
  634. $badPassword = "sdgt43r23wefe";
  635. $outputZip = ZipOutputFile::create();
  636. $outputZip->addDir(__DIR__);
  637. $outputZip->setPassword($password, ZipEntry::ENCRYPTION_METHOD_TRADITIONAL);
  638. $outputZip->saveAsFile($this->outputFilename);
  639. $outputZip->close();
  640. self::assertCorrectZipArchive($this->outputFilename, $password);
  641. $zipFile = ZipFile::openFromFile($this->outputFilename);
  642. // set bad password Traditional Encryption
  643. $zipFile->setPassword($badPassword);
  644. foreach ($zipFile->getListFiles() as $entryName) {
  645. try {
  646. $zipFile->getEntryContent($entryName);
  647. self::fail("Expected Exception has not been raised.");
  648. } catch (ZipAuthenticationException $ae) {
  649. self::assertNotNull($ae);
  650. }
  651. }
  652. // set correct password
  653. $zipFile->setPassword($password);
  654. foreach ($zipFile->getAllInfo() as $info) {
  655. self::assertTrue($info->isEncrypted());
  656. self::assertContains('ZipCrypto', $info->getMethod());
  657. $decryptContent = $zipFile->getEntryContent($info->getPath());
  658. self::assertNotEmpty($decryptContent);
  659. self::assertContains('<?php', $decryptContent);
  660. }
  661. // change encryption method
  662. $outputZip = ZipOutputFile::openFromZipFile($zipFile);
  663. $outputZip->setPassword($password, ZipEntry::ENCRYPTION_METHOD_WINZIP_AES);
  664. $outputZip->saveAsFile($this->outputFilename);
  665. $outputZip->close();
  666. $zipFile->close();
  667. self::assertCorrectZipArchive($this->outputFilename, $password);
  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' => extension_loaded("bz2") ? ZipEntry::METHOD_BZIP2 : ZipEntry::METHOD_STORED,
  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. /**
  865. * Test zip alignment.
  866. */
  867. public function testZipAlign()
  868. {
  869. $zipOutputFile = ZipOutputFile::create();
  870. for ($i = 0; $i < 100; $i++) {
  871. $zipOutputFile->addFromString(
  872. 'entry' . $i . '.txt',
  873. CryptoUtil::randomBytes(mt_rand(100, 4096)),
  874. ZipEntry::METHOD_STORED
  875. );
  876. }
  877. $zipOutputFile->saveAsFile($this->outputFilename);
  878. $zipOutputFile->close();
  879. self::assertCorrectZipArchive($this->outputFilename);
  880. $result = self::doZipAlignVerify($this->outputFilename);
  881. if($result === null) return; // zip align not installed
  882. // check not zip align
  883. self::assertFalse($result);
  884. $zipFile = ZipFile::openFromFile($this->outputFilename);
  885. $zipOutputFile = ZipOutputFile::openFromZipFile($zipFile);
  886. $zipOutputFile->setZipAlign(4);
  887. $zipOutputFile->saveAsFile($this->outputFilename);
  888. $zipOutputFile->close();
  889. $zipFile->close();
  890. self::assertCorrectZipArchive($this->outputFilename);
  891. $result = self::doZipAlignVerify($this->outputFilename);
  892. self::assertNotNull($result);
  893. // check zip align
  894. self::assertTrue($result);
  895. }
  896. /**
  897. * Test support ZIP64 ext (slow test - normal).
  898. * Create > 65535 files in archive and open and extract to /dev/null.
  899. */
  900. public function testCreateAndOpenZip64Ext()
  901. {
  902. $countFiles = 0xffff + 1;
  903. $outputZipFile = ZipOutputFile::create();
  904. for ($i = 0; $i < $countFiles; $i++) {
  905. $outputZipFile->addFromString($i . '.txt', $i, ZipEntry::METHOD_STORED);
  906. }
  907. $outputZipFile->saveAsFile($this->outputFilename);
  908. $outputZipFile->close();
  909. self::assertCorrectZipArchive($this->outputFilename);
  910. $zipFile = ZipFile::openFromFile($this->outputFilename);
  911. self::assertEquals($zipFile->count(), $countFiles);
  912. foreach ($zipFile as $entry => $content) {
  913. strlen($content);
  914. }
  915. $zipFile->close();
  916. }
  917. }