ZipFileTest.php 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452
  1. <?php
  2. namespace PhpZip\Tests;
  3. use GuzzleHttp\Psr7\Response;
  4. use PhpZip\Constants\ZipCompressionLevel;
  5. use PhpZip\Constants\ZipCompressionMethod;
  6. use PhpZip\Constants\ZipPlatform;
  7. use PhpZip\Exception\InvalidArgumentException;
  8. use PhpZip\Exception\ZipEntryNotFoundException;
  9. use PhpZip\Exception\ZipException;
  10. use PhpZip\Exception\ZipUnsupportMethodException;
  11. use PhpZip\Model\Data\ZipFileData;
  12. use PhpZip\Model\ZipEntry;
  13. use PhpZip\Model\ZipInfo;
  14. use PhpZip\Util\FilesUtil;
  15. use PhpZip\ZipFile;
  16. use Psr\Http\Message\ResponseInterface;
  17. /**
  18. * ZipFile test.
  19. *
  20. * @internal
  21. *
  22. * @small
  23. */
  24. class ZipFileTest extends ZipTestCase
  25. {
  26. /**
  27. * @throws ZipException
  28. */
  29. public function testOpenFileCantExists()
  30. {
  31. $this->setExpectedException(ZipException::class, 'does not exist');
  32. $zipFile = new ZipFile();
  33. $zipFile->openFile(uniqid('', true));
  34. }
  35. /**
  36. * @throws ZipException
  37. */
  38. public function testOpenFileCantOpen()
  39. {
  40. $this->setExpectedException(ZipException::class, 'can\'t open');
  41. if (static::skipTestForRootUser()) {
  42. return;
  43. }
  44. static::assertNotFalse(file_put_contents($this->outputFilename, 'content'));
  45. static::assertTrue(chmod($this->outputFilename, 0222));
  46. $zipFile = new ZipFile();
  47. $zipFile->openFile($this->outputFilename);
  48. }
  49. /**
  50. * @throws ZipException
  51. */
  52. public function testOpenFileEmptyFile()
  53. {
  54. $this->setExpectedException(ZipException::class, 'Corrupt zip file');
  55. static::assertNotFalse(touch($this->outputFilename));
  56. $zipFile = new ZipFile();
  57. $zipFile->openFile($this->outputFilename);
  58. }
  59. /**
  60. * @throws ZipException
  61. * @throws \Exception
  62. */
  63. public function testOpenFileInvalidZip()
  64. {
  65. $this->setExpectedException(
  66. ZipException::class,
  67. 'Invalid zip file. The end of the central directory could not be found.'
  68. );
  69. static::assertNotFalse(file_put_contents($this->outputFilename, random_bytes(255)));
  70. $zipFile = new ZipFile();
  71. $zipFile->openFile($this->outputFilename);
  72. }
  73. /**
  74. * @throws ZipException
  75. */
  76. public function testOpenFromStringNullString()
  77. {
  78. $this->setExpectedException(InvalidArgumentException::class, 'Empty string passed');
  79. $zipFile = new ZipFile();
  80. $zipFile->openFromString(null);
  81. }
  82. /**
  83. * @throws ZipException
  84. */
  85. public function testOpenFromStringEmptyString()
  86. {
  87. $this->setExpectedException(InvalidArgumentException::class, 'Empty string passed');
  88. $zipFile = new ZipFile();
  89. $zipFile->openFromString('');
  90. }
  91. /**
  92. * @throws ZipException
  93. * @throws \Exception
  94. */
  95. public function testOpenFromStringInvalidZip()
  96. {
  97. $this->setExpectedException(
  98. ZipException::class,
  99. 'Invalid zip file. The end of the central directory could not be found.'
  100. );
  101. $zipFile = new ZipFile();
  102. $zipFile->openFromString(random_bytes(255));
  103. }
  104. /**
  105. * @throws ZipException
  106. */
  107. public function testOpenFromString()
  108. {
  109. $zipFile = new ZipFile();
  110. $zipFile->addFromString('file', 'content');
  111. $zipFile['file2'] = 'content 2';
  112. $zipContents = $zipFile->outputAsString();
  113. $zipFile->close();
  114. $zipFile->openFromString($zipContents);
  115. static::assertSame($zipFile->count(), 2);
  116. static::assertTrue(isset($zipFile['file']));
  117. static::assertTrue(isset($zipFile['file2']));
  118. static::assertSame($zipFile['file'], 'content');
  119. static::assertSame($zipFile['file2'], 'content 2');
  120. $zipFile->close();
  121. }
  122. /**
  123. * @throws ZipException
  124. */
  125. public function testOpenFromStreamNullStream()
  126. {
  127. $this->setExpectedException(InvalidArgumentException::class, 'Stream must be a resource');
  128. $zipFile = new ZipFile();
  129. $zipFile->openFromStream(null);
  130. }
  131. /**
  132. * @throws ZipException
  133. */
  134. public function testOpenFromStreamInvalidResourceType()
  135. {
  136. $this->setExpectedException(InvalidArgumentException::class, 'Stream must be a resource');
  137. $zipFile = new ZipFile();
  138. /** @noinspection PhpParamsInspection */
  139. $zipFile->openFromStream('stream resource');
  140. }
  141. /**
  142. * @throws ZipException
  143. */
  144. public function testOpenFromStreamInvalidResourceType2()
  145. {
  146. $this->setExpectedException(InvalidArgumentException::class, 'Invalid resource type');
  147. $zipFile = new ZipFile();
  148. if (!\extension_loaded('gd')) {
  149. static::markTestSkipped('not extension gd');
  150. return;
  151. }
  152. /** @noinspection PhpComposerExtensionStubsInspection */
  153. $zipFile->openFromStream(imagecreate(1, 1));
  154. }
  155. /**
  156. * @throws ZipException
  157. */
  158. public function testOpenFromStreamInvalidResourceType3()
  159. {
  160. $this->setExpectedException(InvalidArgumentException::class, 'Directory stream not supported');
  161. $zipFile = new ZipFile();
  162. $zipFile->openFromStream(opendir(__DIR__));
  163. }
  164. /**
  165. * @throws ZipException
  166. * @noinspection PhpUsageOfSilenceOperatorInspection
  167. * @noinspection NestedPositiveIfStatementsInspection
  168. */
  169. public function testOpenFromStreamNoSeekable()
  170. {
  171. $this->setExpectedException(InvalidArgumentException::class, 'The stream wrapper type "http" is not supported');
  172. if (!$fp = @fopen('http://localhost', 'rb')) {
  173. if (!$fp = @fopen('http://example.org', 'rb')) {
  174. static::markTestSkipped('not connected to localhost or remote host');
  175. return;
  176. }
  177. }
  178. $zipFile = new ZipFile();
  179. $zipFile->openFromStream($fp);
  180. }
  181. /**
  182. * @throws ZipException
  183. */
  184. public function testOpenFromStreamEmptyContents()
  185. {
  186. $this->setExpectedException(ZipException::class, 'Corrupt zip file');
  187. $fp = fopen($this->outputFilename, 'w+b');
  188. $zipFile = new ZipFile();
  189. $zipFile->openFromStream($fp);
  190. }
  191. /**
  192. * @throws ZipException
  193. * @throws \Exception
  194. */
  195. public function testOpenFromStreamInvalidZip()
  196. {
  197. $this->setExpectedException(
  198. ZipException::class,
  199. 'Invalid zip file. The end of the central directory could not be found.'
  200. );
  201. $fp = fopen($this->outputFilename, 'w+b');
  202. fwrite($fp, random_bytes(255));
  203. $zipFile = new ZipFile();
  204. $zipFile->openFromStream($fp);
  205. }
  206. /**
  207. * @throws ZipException
  208. */
  209. public function testOpenFromStream()
  210. {
  211. $zipFile = new ZipFile();
  212. $zipFile
  213. ->addFromString('file', 'content')
  214. ->saveAsFile($this->outputFilename)
  215. ->close()
  216. ;
  217. $handle = fopen($this->outputFilename, 'rb');
  218. $zipFile->openFromStream($handle);
  219. static::assertSame($zipFile->count(), 1);
  220. static::assertTrue(isset($zipFile['file']));
  221. static::assertSame($zipFile['file'], 'content');
  222. $zipFile->close();
  223. }
  224. /**
  225. * Test create, open and extract empty archive.
  226. *
  227. * @throws ZipException
  228. */
  229. public function testEmptyArchive()
  230. {
  231. $zipFile = new ZipFile();
  232. $zipFile
  233. ->saveAsFile($this->outputFilename)
  234. ->close()
  235. ;
  236. static::assertCorrectEmptyZip($this->outputFilename);
  237. static::assertTrue(mkdir($this->outputDirname, 0755, true));
  238. $zipFile->openFile($this->outputFilename);
  239. static::assertSame($zipFile->count(), 0);
  240. $zipFile
  241. ->extractTo($this->outputDirname)
  242. ->close()
  243. ;
  244. static::assertTrue(FilesUtil::isEmptyDir($this->outputDirname));
  245. }
  246. /**
  247. * No modified archive.
  248. *
  249. * @throws ZipException
  250. *
  251. * @see ZipOutputFile::create()
  252. */
  253. public function testNoModifiedArchive()
  254. {
  255. static::assertTrue(mkdir($this->outputDirname, 0755, true));
  256. $fileActual = $this->outputDirname . \DIRECTORY_SEPARATOR . 'file_actual.zip';
  257. $fileExpected = $this->outputDirname . \DIRECTORY_SEPARATOR . 'file_expected.zip';
  258. $zipFile = new ZipFile();
  259. $zipFile->addDirRecursive(__DIR__ . '/../src');
  260. $sourceCount = $zipFile->count();
  261. static::assertTrue($sourceCount > 0);
  262. $zipFile
  263. ->saveAsFile($fileActual)
  264. ->close()
  265. ;
  266. static::assertCorrectZipArchive($fileActual);
  267. $zipFile
  268. ->openFile($fileActual)
  269. ->saveAsFile($fileExpected)
  270. ;
  271. static::assertCorrectZipArchive($fileExpected);
  272. $zipFileExpected = new ZipFile();
  273. $zipFileExpected->openFile($fileExpected);
  274. static::assertSame($zipFile->count(), $sourceCount);
  275. static::assertSame($zipFileExpected->count(), $zipFile->count());
  276. static::assertSame($zipFileExpected->getListFiles(), $zipFile->getListFiles());
  277. foreach ($zipFile as $entryName => $content) {
  278. static::assertSame($zipFileExpected[$entryName], $content);
  279. }
  280. $zipFileExpected->close();
  281. $zipFile->close();
  282. }
  283. /**
  284. * Create archive and add files.
  285. *
  286. * @throws ZipException
  287. *
  288. * @see ZipOutputFile::addFromFile()
  289. * @see ZipOutputFile::addFromStream()
  290. * @see ZipFile::getEntryContents()
  291. * @see ZipOutputFile::addFromString()
  292. */
  293. public function testCreateArchiveAndAddFiles()
  294. {
  295. $outputFromString = file_get_contents(__FILE__);
  296. $outputFromString2 = file_get_contents(\dirname(__DIR__) . \DIRECTORY_SEPARATOR . 'README.md');
  297. $outputFromFile = file_get_contents(\dirname(__DIR__) . \DIRECTORY_SEPARATOR . 'phpunit.xml');
  298. $outputFromStream = file_get_contents(\dirname(__DIR__) . \DIRECTORY_SEPARATOR . 'composer.json');
  299. $filenameFromString = basename(__FILE__);
  300. $filenameFromString2 = 'test_file.txt';
  301. $filenameFromFile = 'data/test file.txt';
  302. $filenameFromStream = 'data/ডিরেক্টরি/αρχείο.json';
  303. $emptyDirName = 'empty dir/пустой каталог/空目錄/ไดเรกทอรีที่ว่างเปล่า/';
  304. $emptyDirName2 = 'empty dir/пустой каталог/';
  305. $emptyDirName3 = 'empty dir/пустой каталог/ещё один пустой каталог/';
  306. $tempFile = tempnam(sys_get_temp_dir(), 'txt');
  307. file_put_contents($tempFile, $outputFromFile);
  308. $tempStream = tmpfile();
  309. fwrite($tempStream, $outputFromStream);
  310. $zipFile = new ZipFile();
  311. $zipFile
  312. ->addFromString($filenameFromString, $outputFromString)
  313. ->addFile($tempFile, $filenameFromFile)
  314. ->addFromStream($tempStream, $filenameFromStream)
  315. ->addEmptyDir($emptyDirName)
  316. ;
  317. $zipFile[$filenameFromString2] = $outputFromString2;
  318. $zipFile[$emptyDirName2] = null;
  319. $zipFile[$emptyDirName3] = 'this content ignoring';
  320. static::assertSame(\count($zipFile), 7);
  321. $zipFile
  322. ->saveAsFile($this->outputFilename)
  323. ->close()
  324. ;
  325. unlink($tempFile);
  326. static::assertCorrectZipArchive($this->outputFilename);
  327. $zipFile->openFile($this->outputFilename);
  328. static::assertSame(\count($zipFile), 7);
  329. static::assertSame($zipFile[$filenameFromString], $outputFromString);
  330. static::assertSame($zipFile[$filenameFromFile], $outputFromFile);
  331. static::assertSame($zipFile[$filenameFromStream], $outputFromStream);
  332. static::assertSame($zipFile[$filenameFromString2], $outputFromString2);
  333. static::assertTrue(isset($zipFile[$emptyDirName]));
  334. static::assertTrue(isset($zipFile[$emptyDirName2]));
  335. static::assertTrue(isset($zipFile[$emptyDirName3]));
  336. static::assertTrue($zipFile->isDirectory($emptyDirName));
  337. static::assertTrue($zipFile->isDirectory($emptyDirName2));
  338. static::assertTrue($zipFile->isDirectory($emptyDirName3));
  339. $listFiles = $zipFile->getListFiles();
  340. static::assertSame($listFiles[0], $filenameFromString);
  341. static::assertSame($listFiles[1], $filenameFromFile);
  342. static::assertSame($listFiles[2], $filenameFromStream);
  343. static::assertSame($listFiles[3], $emptyDirName);
  344. static::assertSame($listFiles[4], $filenameFromString2);
  345. static::assertSame($listFiles[5], $emptyDirName2);
  346. static::assertSame($listFiles[6], $emptyDirName3);
  347. $zipFile->close();
  348. }
  349. /**
  350. * @throws ZipException
  351. */
  352. public function testEmptyContent()
  353. {
  354. $zipFile = new ZipFile();
  355. $zipFile['file'] = '';
  356. $zipFile->saveAsFile($this->outputFilename);
  357. $zipFile->close();
  358. $zipFile->openFile($this->outputFilename);
  359. static::assertSame($zipFile['file'], '');
  360. $zipFile->close();
  361. }
  362. /**
  363. * Test compression method from image file.
  364. *
  365. * @throws ZipException
  366. */
  367. public function testCompressionMethodFromImageMimeType()
  368. {
  369. if (!\function_exists('mime_content_type')) {
  370. static::markTestSkipped('Function mime_content_type not exists');
  371. return;
  372. }
  373. $outputFilename = $this->outputFilename;
  374. $this->outputFilename .= '.gif';
  375. static::assertNotFalse(
  376. file_put_contents(
  377. $this->outputFilename,
  378. base64_decode('R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw==')
  379. )
  380. );
  381. $basename = basename($this->outputFilename);
  382. $zipFile = new ZipFile();
  383. $zipFile->addFile($this->outputFilename, $basename);
  384. $zipFile->saveAsFile($outputFilename);
  385. unlink($this->outputFilename);
  386. $this->outputFilename = $outputFilename;
  387. $zipFile->openFile($this->outputFilename);
  388. $info = $zipFile->getEntryInfo($basename);
  389. static::assertSame($info->getMethodName(), 'Stored');
  390. $zipFile->close();
  391. }
  392. /**
  393. * Rename zip entry name.
  394. *
  395. * @throws ZipException
  396. */
  397. public function testRename()
  398. {
  399. $oldName = basename(__FILE__);
  400. $newName = 'tests/' . $oldName;
  401. $zipFile = new ZipFile();
  402. $zipFile->addDir(__DIR__);
  403. $zipFile->saveAsFile($this->outputFilename);
  404. $zipFile->close();
  405. static::assertCorrectZipArchive($this->outputFilename);
  406. $zipFile->openFile($this->outputFilename);
  407. $zipFile->rename($oldName, $newName);
  408. $zipFile->addFromString('file1.txt', 'content');
  409. $zipFile->addFromString('file2.txt', 'content');
  410. $zipFile->addFromString('file3.txt', 'content');
  411. $zipFile->rename('file1.txt', 'file_long_name.txt');
  412. $zipFile->rename('file2.txt', 'file4.txt');
  413. $zipFile->rename('file3.txt', 'fi.txt');
  414. $zipFile->saveAsFile($this->outputFilename);
  415. $zipFile->close();
  416. static::assertCorrectZipArchive($this->outputFilename);
  417. $zipFile->openFile($this->outputFilename);
  418. static::assertFalse(isset($zipFile[$oldName]));
  419. static::assertTrue(isset($zipFile[$newName]));
  420. static::assertFalse(isset($zipFile['file1.txt']));
  421. static::assertFalse(isset($zipFile['file2.txt']));
  422. static::assertFalse(isset($zipFile['file3.txt']));
  423. static::assertTrue(isset($zipFile['file_long_name.txt']));
  424. static::assertTrue(isset($zipFile['file4.txt']));
  425. static::assertTrue(isset($zipFile['fi.txt']));
  426. $zipFile->close();
  427. }
  428. /**
  429. * @throws ZipException
  430. */
  431. public function testRenameEntryNull()
  432. {
  433. $this->setExpectedException(InvalidArgumentException::class, 'name is null');
  434. $zipFile = new ZipFile();
  435. $zipFile->rename(null, 'new-file');
  436. }
  437. /**
  438. * @throws ZipException
  439. */
  440. public function testRenameEntryNull2()
  441. {
  442. $this->setExpectedException(InvalidArgumentException::class, 'name is null');
  443. $zipFile = new ZipFile();
  444. $zipFile->rename('old-file', null);
  445. }
  446. /**
  447. * @throws ZipException
  448. */
  449. public function testRenameEntryToExistsNewEntry()
  450. {
  451. $this->setExpectedException(InvalidArgumentException::class, 'is exists');
  452. $zipFile = new ZipFile();
  453. $zipFile['file'] = 'content';
  454. $zipFile['file2'] = 'content 2';
  455. $zipFile->saveAsFile($this->outputFilename);
  456. $zipFile->close();
  457. $zipFile = new ZipFile();
  458. $zipFile->openFile($this->outputFilename);
  459. $zipFile->rename('file2', 'file');
  460. }
  461. /**
  462. * @throws ZipException
  463. */
  464. public function testRenameEntryNotFound()
  465. {
  466. $this->setExpectedException(ZipEntryNotFoundException::class);
  467. $zipFile = new ZipFile();
  468. $zipFile['file'] = 'content';
  469. $zipFile['file2'] = 'content 2';
  470. $zipFile->saveAsFile($this->outputFilename);
  471. $zipFile->close();
  472. $zipFile = new ZipFile();
  473. $zipFile->openFile($this->outputFilename);
  474. $zipFile->rename('file2.bak', 'file3');
  475. }
  476. /**
  477. * Delete entry from name.
  478. *
  479. * @throws ZipException
  480. */
  481. public function testDeleteFromName()
  482. {
  483. $inputDir = \dirname(__DIR__) . \DIRECTORY_SEPARATOR;
  484. $deleteEntryName = 'composer.json';
  485. $zipFile = new ZipFile();
  486. $zipFile->addDir($inputDir);
  487. $zipFile->saveAsFile($this->outputFilename);
  488. $zipFile->close();
  489. static::assertCorrectZipArchive($this->outputFilename);
  490. $zipFile->openFile($this->outputFilename);
  491. $zipFile->deleteFromName($deleteEntryName);
  492. $zipFile->saveAsFile($this->outputFilename);
  493. $zipFile->close();
  494. static::assertCorrectZipArchive($this->outputFilename);
  495. $zipFile->openFile($this->outputFilename);
  496. static::assertFalse(isset($zipFile[$deleteEntryName]));
  497. $zipFile->close();
  498. }
  499. /**
  500. * @throws ZipEntryNotFoundException
  501. * @throws ZipException
  502. */
  503. public function testDeleteNewEntry()
  504. {
  505. $zipFile = new ZipFile();
  506. $zipFile['entry1'] = '';
  507. $zipFile['entry2'] = '';
  508. $zipFile->deleteFromName('entry2');
  509. $zipFile->saveAsFile($this->outputFilename);
  510. $zipFile->close();
  511. $zipFile->openFile($this->outputFilename);
  512. static::assertSame(\count($zipFile), 1);
  513. static::assertTrue(isset($zipFile['entry1']));
  514. static::assertFalse(isset($zipFile['entry2']));
  515. $zipFile->close();
  516. }
  517. /**
  518. * @throws ZipEntryNotFoundException
  519. */
  520. public function testDeleteFromNameNotFoundEntry()
  521. {
  522. $this->setExpectedException(ZipEntryNotFoundException::class);
  523. $zipFile = new ZipFile();
  524. $zipFile->deleteFromName('entry');
  525. }
  526. public function testCatchNotFoundEntry()
  527. {
  528. $entryName = 'entry';
  529. $zipFile = new ZipFile();
  530. try {
  531. $zipFile->getEntry($entryName);
  532. } catch (ZipEntryNotFoundException $e) {
  533. static::assertSame($e->getEntryName(), $entryName);
  534. }
  535. }
  536. /**
  537. * Delete zip entries from glob pattern.
  538. *
  539. * @throws ZipException
  540. */
  541. public function testDeleteFromGlob()
  542. {
  543. $inputDir = \dirname(__DIR__);
  544. $zipFile = new ZipFile();
  545. $zipFile->addFilesFromGlobRecursive($inputDir, '**.{xml,json,md}', '/');
  546. static::assertTrue(isset($zipFile['composer.json']));
  547. static::assertTrue(isset($zipFile['phpunit.xml']));
  548. $zipFile->saveAsFile($this->outputFilename);
  549. $zipFile->close();
  550. static::assertCorrectZipArchive($this->outputFilename);
  551. $zipFile->openFile($this->outputFilename);
  552. static::assertTrue(isset($zipFile['composer.json']));
  553. static::assertTrue(isset($zipFile['phpunit.xml']));
  554. $zipFile->deleteFromGlob('**.{xml,json}');
  555. static::assertFalse(isset($zipFile['composer.json']));
  556. static::assertFalse(isset($zipFile['phpunit.xml']));
  557. $zipFile->saveAsFile($this->outputFilename);
  558. $zipFile->close();
  559. static::assertCorrectZipArchive($this->outputFilename);
  560. $zipFile->openFile($this->outputFilename);
  561. static::assertTrue($zipFile->count() > 0);
  562. foreach ($zipFile->getListFiles() as $name) {
  563. static::assertStringEndsWith('.md', $name);
  564. }
  565. $zipFile->close();
  566. }
  567. public function testDeleteFromGlobFailNull()
  568. {
  569. $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
  570. $zipFile = new ZipFile();
  571. $zipFile->deleteFromGlob(null);
  572. }
  573. public function testDeleteFromGlobFailEmpty()
  574. {
  575. $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
  576. $zipFile = new ZipFile();
  577. $zipFile->deleteFromGlob('');
  578. }
  579. /**
  580. * Delete entries from regex pattern.
  581. *
  582. * @throws ZipException
  583. */
  584. public function testDeleteFromRegex()
  585. {
  586. $inputDir = \dirname(__DIR__);
  587. $zipFile = new ZipFile();
  588. $zipFile->addFilesFromRegexRecursive($inputDir, '~\.(xml|json)$~i', 'Path');
  589. $zipFile->saveAsFile($this->outputFilename);
  590. $zipFile->close();
  591. static::assertCorrectZipArchive($this->outputFilename);
  592. $zipFile->openFile($this->outputFilename);
  593. $zipFile->deleteFromRegex('~\.(json)$~i');
  594. $zipFile->addFromString('test.txt', 'content');
  595. $zipFile->deleteFromRegex('~\.txt$~');
  596. $zipFile->saveAsFile($this->outputFilename);
  597. $zipFile->close();
  598. static::assertCorrectZipArchive($this->outputFilename);
  599. $zipFile->openFile($this->outputFilename);
  600. static::assertFalse(isset($zipFile['Path/composer.json']));
  601. static::assertFalse(isset($zipFile['Path/test.txt']));
  602. static::assertTrue(isset($zipFile['Path/phpunit.xml']));
  603. $zipFile->close();
  604. }
  605. public function testDeleteFromRegexFailNull()
  606. {
  607. $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
  608. $zipFile = new ZipFile();
  609. $zipFile->deleteFromRegex(null);
  610. }
  611. public function testDeleteFromRegexFailEmpty()
  612. {
  613. $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
  614. $zipFile = new ZipFile();
  615. $zipFile->deleteFromRegex('');
  616. }
  617. /**
  618. * Delete all entries.
  619. *
  620. * @throws ZipException
  621. */
  622. public function testDeleteAll()
  623. {
  624. $zipFile = new ZipFile();
  625. $zipFile->addDirRecursive(\dirname(__DIR__) . \DIRECTORY_SEPARATOR . 'src');
  626. static::assertTrue($zipFile->count() > 0);
  627. $zipFile->saveAsFile($this->outputFilename);
  628. $zipFile->close();
  629. static::assertCorrectZipArchive($this->outputFilename);
  630. $zipFile->openFile($this->outputFilename);
  631. static::assertTrue($zipFile->count() > 0);
  632. $zipFile->deleteAll();
  633. $zipFile->saveAsFile($this->outputFilename);
  634. $zipFile->close();
  635. static::assertCorrectEmptyZip($this->outputFilename);
  636. $zipFile->openFile($this->outputFilename);
  637. static::assertSame($zipFile->count(), 0);
  638. $zipFile->close();
  639. }
  640. /**
  641. * Test zip archive comment.
  642. *
  643. * @throws ZipException
  644. */
  645. public function testArchiveComment()
  646. {
  647. $comment = 'This zip file comment' . \PHP_EOL
  648. . 'Αυτό το σχόλιο αρχείο zip' . \PHP_EOL
  649. . 'Это комментарий zip архива' . \PHP_EOL
  650. . '這個ZIP文件註釋' . \PHP_EOL
  651. . 'ეს zip ფაილის კომენტარი' . \PHP_EOL
  652. . 'このzipファイルにコメント' . \PHP_EOL
  653. . 'ความคิดเห็นนี้ไฟล์ซิป';
  654. $zipFile = new ZipFile();
  655. $zipFile->setArchiveComment($comment);
  656. $zipFile->addFile(__FILE__);
  657. $zipFile->saveAsFile($this->outputFilename);
  658. $zipFile->close();
  659. static::assertCorrectZipArchive($this->outputFilename);
  660. $zipFile->openFile($this->outputFilename);
  661. static::assertSame($zipFile->getArchiveComment(), $comment);
  662. $zipFile->setArchiveComment(null); // remove archive comment
  663. $zipFile->saveAsFile($this->outputFilename);
  664. $zipFile->close();
  665. static::assertCorrectZipArchive($this->outputFilename);
  666. // check empty comment
  667. $zipFile->openFile($this->outputFilename);
  668. static::assertNull($zipFile->getArchiveComment());
  669. $zipFile->close();
  670. }
  671. /**
  672. * Test very long archive comment.
  673. */
  674. public function testVeryLongArchiveComment()
  675. {
  676. $this->setExpectedException(InvalidArgumentException::class);
  677. $comment = 'Very long comment' . \PHP_EOL .
  678. 'Очень длинный комментарий' . \PHP_EOL;
  679. $comment = str_repeat($comment, ceil(0xffff / \strlen($comment)) + \strlen($comment) + 1);
  680. $zipFile = new ZipFile();
  681. $zipFile->setArchiveComment($comment);
  682. }
  683. /**
  684. * Test zip entry comment.
  685. *
  686. * @throws ZipException
  687. * @throws \Exception
  688. */
  689. public function testEntryComment()
  690. {
  691. $entries = [
  692. '文件1.txt' => [
  693. 'data' => random_bytes(255),
  694. 'comment' => '這是註釋的條目。',
  695. ],
  696. 'file2.txt' => [
  697. 'data' => random_bytes(255),
  698. 'comment' => null,
  699. ],
  700. 'file3.txt' => [
  701. 'data' => random_bytes(255),
  702. 'comment' => random_bytes(255),
  703. ],
  704. 'file4.txt' => [
  705. 'data' => random_bytes(255),
  706. 'comment' => 'Комментарий файла',
  707. ],
  708. 'file5.txt' => [
  709. 'data' => random_bytes(255),
  710. 'comment' => 'ไฟล์แสดงความคิดเห็น',
  711. ],
  712. 'file6 emoji 🙍🏼.txt' => [
  713. 'data' => random_bytes(255),
  714. 'comment' => 'Emoji comment file - 😀 ⛈ ❤️ 🤴🏽',
  715. ],
  716. ];
  717. // create archive with entry comments
  718. $zipFile = new ZipFile();
  719. foreach ($entries as $entryName => $item) {
  720. $zipFile->addFromString($entryName, $item['data']);
  721. $zipFile->setEntryComment($entryName, $item['comment']);
  722. }
  723. $zipFile->saveAsFile($this->outputFilename);
  724. $zipFile->close();
  725. static::assertCorrectZipArchive($this->outputFilename);
  726. // check and modify comments
  727. $zipFile->openFile($this->outputFilename);
  728. foreach ($zipFile->getListFiles() as $entryName) {
  729. $entriesItem = $entries[$entryName];
  730. static::assertNotEmpty($entriesItem);
  731. static::assertSame($zipFile[$entryName], $entriesItem['data']);
  732. static::assertSame($zipFile->getEntryComment($entryName), (string) $entriesItem['comment']);
  733. }
  734. // modify comment
  735. $entries['file5.txt']['comment'] = mt_rand(1, 100000000);
  736. $zipFile->setEntryComment('file5.txt', $entries['file5.txt']['comment']);
  737. $zipFile->saveAsFile($this->outputFilename);
  738. $zipFile->close();
  739. static::assertCorrectZipArchive($this->outputFilename);
  740. // check modify comments
  741. $zipFile->openFile($this->outputFilename);
  742. foreach ($entries as $entryName => $entriesItem) {
  743. static::assertTrue(isset($zipFile[$entryName]));
  744. static::assertSame($zipFile->getEntryComment($entryName), (string) $entriesItem['comment']);
  745. static::assertSame($zipFile[$entryName], $entriesItem['data']);
  746. }
  747. $zipFile->close();
  748. }
  749. /**
  750. * Test zip entry very long comment.
  751. *
  752. * @throws ZipException
  753. */
  754. public function testVeryLongEntryComment()
  755. {
  756. $this->setExpectedException(InvalidArgumentException::class, 'Comment too long');
  757. $comment = 'Very long comment' . \PHP_EOL .
  758. 'Очень длинный комментарий' . \PHP_EOL;
  759. $comment = str_repeat($comment, ceil(0xffff / \strlen($comment)) + \strlen($comment) + 1);
  760. $zipFile = new ZipFile();
  761. $zipFile->addFile(__FILE__, 'test');
  762. $zipFile->setEntryComment('test', $comment);
  763. }
  764. /**
  765. * @throws ZipException
  766. */
  767. public function testSetEntryCommentNotFoundEntry()
  768. {
  769. $this->setExpectedException(ZipEntryNotFoundException::class);
  770. $zipFile = new ZipFile();
  771. $zipFile->setEntryComment('test', 'comment');
  772. }
  773. /**
  774. * Test all available support compression methods.
  775. *
  776. * @throws ZipException
  777. * @throws \Exception
  778. */
  779. public function testCompressionMethod()
  780. {
  781. $entries = [
  782. '1' => [
  783. 'data' => random_bytes(255),
  784. 'method' => ZipCompressionMethod::STORED,
  785. 'expected' => 'Stored',
  786. ],
  787. '2' => [
  788. 'data' => random_bytes(255),
  789. 'method' => ZipCompressionMethod::DEFLATED,
  790. 'expected' => 'Deflated',
  791. ],
  792. ];
  793. if (\extension_loaded('bz2')) {
  794. $entries['3'] = [
  795. 'data' => random_bytes(255),
  796. 'method' => ZipCompressionMethod::BZIP2,
  797. 'expected' => 'BZIP2',
  798. ];
  799. }
  800. $zipFile = new ZipFile();
  801. foreach ($entries as $entryName => $item) {
  802. $zipFile->addFromString($entryName, $item['data'], $item['method']);
  803. }
  804. $zipFile->saveAsFile($this->outputFilename);
  805. $zipFile->close();
  806. static::assertCorrectZipArchive($this->outputFilename);
  807. $zipFile->openFile($this->outputFilename);
  808. $zipFile->setCompressionLevel(ZipCompressionLevel::MAXIMUM);
  809. $zipAllInfo = $zipFile->getAllInfo();
  810. foreach ($zipAllInfo as $entryName => $info) {
  811. static::assertSame($zipFile[$entryName], $entries[$entryName]['data']);
  812. static::assertSame($info->getMethodName(), $entries[$entryName]['expected']);
  813. $entryInfo = $zipFile->getEntryInfo($entryName);
  814. static::assertEquals($entryInfo, $info);
  815. }
  816. $zipFile->close();
  817. }
  818. /**
  819. * @dataProvider provideInvalidCompressionLevels
  820. *
  821. * @param int $compressionLevel
  822. */
  823. public function testSetInvalidCompressionLevel($compressionLevel)
  824. {
  825. $this->setExpectedException(
  826. InvalidArgumentException::class,
  827. 'Invalid compression level. Minimum level 1. Maximum level 9'
  828. );
  829. $zipFile = new ZipFile();
  830. $zipFile['file 1'] = 'contents';
  831. $zipFile->setCompressionLevel($compressionLevel);
  832. }
  833. /**
  834. * @return array
  835. */
  836. public function provideInvalidCompressionLevels()
  837. {
  838. return [
  839. [-10],
  840. [-2],
  841. [10],
  842. [0xffff],
  843. ];
  844. }
  845. /**
  846. * Test extract all files.
  847. *
  848. * @throws ZipException
  849. * @throws \Exception
  850. */
  851. public function testExtract()
  852. {
  853. $entries = [
  854. 'test1.txt' => random_bytes(255),
  855. 'test2.txt' => random_bytes(255),
  856. 'test/test 2/test3.txt' => random_bytes(255),
  857. 'test empty/dir' => null,
  858. ];
  859. $zipFile = new ZipFile();
  860. foreach ($entries as $entryName => $value) {
  861. if ($value === null) {
  862. $zipFile->addEmptyDir($entryName);
  863. } else {
  864. $zipFile->addFromString($entryName, $value);
  865. }
  866. }
  867. $zipFile->saveAsFile($this->outputFilename);
  868. $zipFile->close();
  869. static::assertTrue(mkdir($this->outputDirname, 0755, true));
  870. $zipFile->openFile($this->outputFilename);
  871. $zipFile->extractTo($this->outputDirname);
  872. foreach ($entries as $entryName => $value) {
  873. $fullExtractedFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . $entryName;
  874. if ($value === null) {
  875. static::assertTrue(is_dir($fullExtractedFilename));
  876. static::assertTrue(FilesUtil::isEmptyDir($fullExtractedFilename));
  877. } else {
  878. static::assertTrue(is_file($fullExtractedFilename));
  879. $contents = file_get_contents($fullExtractedFilename);
  880. static::assertSame($contents, $value);
  881. }
  882. }
  883. $zipFile->close();
  884. }
  885. /**
  886. * Test extract some files.
  887. *
  888. * @throws ZipException
  889. * @throws \Exception
  890. */
  891. public function testExtractSomeFiles()
  892. {
  893. $entries = [
  894. 'test1.txt' => random_bytes(255),
  895. 'test2.txt' => random_bytes(255),
  896. 'test3.txt' => random_bytes(255),
  897. 'test4.txt' => random_bytes(255),
  898. 'test5.txt' => random_bytes(255),
  899. 'test/test/test.txt' => random_bytes(255),
  900. 'test/test/test 2.txt' => random_bytes(255),
  901. 'test empty/dir/' => null,
  902. 'test empty/dir2/' => null,
  903. ];
  904. $extractEntries = [
  905. 'test1.txt',
  906. 'test3.txt',
  907. 'test5.txt',
  908. 'test/test/test 2.txt',
  909. 'test empty/dir2/',
  910. ];
  911. static::assertTrue(mkdir($this->outputDirname, 0755, true));
  912. $zipFile = new ZipFile();
  913. $zipFile->addAll($entries);
  914. $zipFile->saveAsFile($this->outputFilename);
  915. $zipFile->close();
  916. $zipFile->openFile($this->outputFilename);
  917. $zipFile->extractTo($this->outputDirname, $extractEntries);
  918. foreach ($entries as $entryName => $value) {
  919. $fullExtractFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . $entryName;
  920. if (\in_array($entryName, $extractEntries, true)) {
  921. if ($value === null) {
  922. static::assertTrue(is_dir($fullExtractFilename));
  923. static::assertTrue(FilesUtil::isEmptyDir($fullExtractFilename));
  924. } else {
  925. static::assertTrue(is_file($fullExtractFilename));
  926. $contents = file_get_contents($fullExtractFilename);
  927. static::assertEquals($contents, $value);
  928. }
  929. } elseif ($value === null) {
  930. static::assertFalse(is_dir($fullExtractFilename));
  931. } else {
  932. static::assertFalse(is_file($fullExtractFilename));
  933. }
  934. }
  935. static::assertFalse(is_file($this->outputDirname . \DIRECTORY_SEPARATOR . 'test/test/test.txt'));
  936. $zipFile->extractTo($this->outputDirname, 'test/test/test.txt');
  937. static::assertTrue(is_file($this->outputDirname . \DIRECTORY_SEPARATOR . 'test/test/test.txt'));
  938. $zipFile->close();
  939. }
  940. /**
  941. * @throws ZipException
  942. */
  943. public function testExtractFail()
  944. {
  945. $this->setExpectedException(ZipException::class, 'not found');
  946. $zipFile = new ZipFile();
  947. $zipFile['file'] = 'content';
  948. $zipFile->saveAsFile($this->outputFilename);
  949. $zipFile->close();
  950. $zipFile->openFile($this->outputFilename);
  951. $zipFile->extractTo('path/to/path');
  952. }
  953. /**
  954. * @throws ZipException
  955. */
  956. public function testExtractFail2()
  957. {
  958. $this->setExpectedException(ZipException::class, 'Destination is not directory');
  959. $zipFile = new ZipFile();
  960. $zipFile['file'] = 'content';
  961. $zipFile->saveAsFile($this->outputFilename);
  962. $zipFile->close();
  963. $zipFile->openFile($this->outputFilename);
  964. $zipFile->extractTo($this->outputFilename);
  965. }
  966. /**
  967. * @throws ZipException
  968. */
  969. public function testExtractFail3()
  970. {
  971. $this->setExpectedException(ZipException::class, 'Destination is not writable directory');
  972. if (static::skipTestForRootUser()) {
  973. return;
  974. }
  975. $zipFile = new ZipFile();
  976. $zipFile['file'] = 'content';
  977. $zipFile->saveAsFile($this->outputFilename);
  978. $zipFile->close();
  979. static::assertTrue(mkdir($this->outputDirname, 0444, true));
  980. static::assertTrue(chmod($this->outputDirname, 0444));
  981. $zipFile->openFile($this->outputFilename);
  982. $zipFile->extractTo($this->outputDirname);
  983. }
  984. /**
  985. * @noinspection OnlyWritesOnParameterInspection
  986. */
  987. public function testAddFromArrayAccessNullName()
  988. {
  989. $this->setExpectedException(
  990. InvalidArgumentException::class,
  991. 'Key must not be null, but must contain the name of the zip entry.'
  992. );
  993. $zipFile = new ZipFile();
  994. $zipFile[null] = 'content';
  995. }
  996. /**
  997. * @noinspection OnlyWritesOnParameterInspection
  998. */
  999. public function testAddFromArrayAccessEmptyName()
  1000. {
  1001. $this->setExpectedException(
  1002. InvalidArgumentException::class,
  1003. 'Key is empty, but must contain the name of the zip entry.'
  1004. );
  1005. $zipFile = new ZipFile();
  1006. $zipFile[''] = 'content';
  1007. }
  1008. /**
  1009. * @throws ZipException
  1010. */
  1011. public function testAddFromStringNullContents()
  1012. {
  1013. $this->setExpectedException(InvalidArgumentException::class, 'Contents is null');
  1014. $zipFile = new ZipFile();
  1015. $zipFile->addFromString('file', null);
  1016. }
  1017. /**
  1018. * @throws ZipException
  1019. */
  1020. public function testAddFromStringNullEntryName()
  1021. {
  1022. $this->setExpectedException(InvalidArgumentException::class, 'Entry name is null');
  1023. $zipFile = new ZipFile();
  1024. $zipFile->addFromString(null, 'contents');
  1025. }
  1026. /**
  1027. * @throws ZipException
  1028. */
  1029. public function testAddFromStringUnsupportedMethod()
  1030. {
  1031. $this->setExpectedException(
  1032. ZipUnsupportMethodException::class,
  1033. 'Compression method 99 (AES Encryption) is not supported.'
  1034. );
  1035. $zipFile = new ZipFile();
  1036. $zipFile->addFromString('file', 'contents', ZipCompressionMethod::WINZIP_AES);
  1037. $zipFile->outputAsString();
  1038. }
  1039. /**
  1040. * @throws ZipException
  1041. */
  1042. public function testAddFromStringEmptyEntryName()
  1043. {
  1044. $this->setExpectedException(InvalidArgumentException::class, 'Empty entry name');
  1045. $zipFile = new ZipFile();
  1046. $zipFile->addFromString('', 'contents');
  1047. }
  1048. /**
  1049. * Test compression method from add string.
  1050. *
  1051. * @throws ZipException
  1052. */
  1053. public function testAddFromStringCompressionMethod()
  1054. {
  1055. $fileStored = sys_get_temp_dir() . '/zip-stored.txt';
  1056. $fileDeflated = sys_get_temp_dir() . '/zip-deflated.txt';
  1057. static::assertNotFalse(file_put_contents($fileStored, 'content'));
  1058. static::assertNotFalse(file_put_contents($fileDeflated, str_repeat('content', 200)));
  1059. $zipFile = new ZipFile();
  1060. $zipFile->addFromString(basename($fileStored), file_get_contents($fileStored));
  1061. $zipFile->addFromString(basename($fileDeflated), file_get_contents($fileDeflated));
  1062. $zipFile->saveAsFile($this->outputFilename);
  1063. $zipFile->close();
  1064. unlink($fileStored);
  1065. unlink($fileDeflated);
  1066. $zipFile->openFile($this->outputFilename);
  1067. $infoStored = $zipFile->getEntryInfo(basename($fileStored));
  1068. $infoDeflated = $zipFile->getEntryInfo(basename($fileDeflated));
  1069. static::assertSame($infoStored->getMethodName(), 'Stored');
  1070. static::assertSame($infoDeflated->getMethodName(), 'Deflated');
  1071. $zipFile->close();
  1072. }
  1073. /**
  1074. * @throws ZipException
  1075. */
  1076. public function testAddFromStreamInvalidResource()
  1077. {
  1078. $this->setExpectedException(InvalidArgumentException::class, 'Stream is not resource');
  1079. $zipFile = new ZipFile();
  1080. /** @noinspection PhpParamsInspection */
  1081. $zipFile->addFromStream('invalid resource', 'name');
  1082. }
  1083. /**
  1084. * @throws ZipException
  1085. */
  1086. public function testAddFromStreamEmptyEntryName()
  1087. {
  1088. $this->setExpectedException(InvalidArgumentException::class, 'Empty entry name');
  1089. $handle = fopen(__FILE__, 'rb');
  1090. $zipFile = new ZipFile();
  1091. $zipFile->addFromStream($handle, '');
  1092. }
  1093. /**
  1094. * @throws ZipException
  1095. */
  1096. public function testAddFromStreamUnsupportedMethod()
  1097. {
  1098. $this->setExpectedException(
  1099. ZipUnsupportMethodException::class,
  1100. 'Compression method 99 (AES Encryption) is not supported.'
  1101. );
  1102. $handle = fopen(__FILE__, 'rb');
  1103. $zipFile = new ZipFile();
  1104. $zipFile->addFromStream($handle, basename(__FILE__), ZipCompressionMethod::WINZIP_AES);
  1105. $zipFile->outputAsString();
  1106. }
  1107. /**
  1108. * Test compression method from add stream.
  1109. *
  1110. * @throws ZipException
  1111. */
  1112. public function testAddFromStreamCompressionMethod()
  1113. {
  1114. $fileStored = sys_get_temp_dir() . '/zip-stored.txt';
  1115. $fileDeflated = sys_get_temp_dir() . '/zip-deflated.txt';
  1116. static::assertNotFalse(file_put_contents($fileStored, 'content'));
  1117. static::assertNotFalse(file_put_contents($fileDeflated, str_repeat('content', 200)));
  1118. $fpStored = fopen($fileStored, 'rb');
  1119. $fpDeflated = fopen($fileDeflated, 'rb');
  1120. $zipFile = new ZipFile();
  1121. $zipFile->addFromStream($fpStored, basename($fileStored));
  1122. $zipFile->addFromStream($fpDeflated, basename($fileDeflated));
  1123. $zipFile->saveAsFile($this->outputFilename);
  1124. $zipFile->close();
  1125. unlink($fileStored);
  1126. unlink($fileDeflated);
  1127. $zipFile->openFile($this->outputFilename);
  1128. $infoStored = $zipFile->getEntryInfo(basename($fileStored));
  1129. $infoDeflated = $zipFile->getEntryInfo(basename($fileDeflated));
  1130. static::assertSame($infoStored->getMethodName(), 'Stored');
  1131. static::assertSame($infoDeflated->getMethodName(), 'Deflated');
  1132. $zipFile->close();
  1133. }
  1134. /**
  1135. * @throws ZipException
  1136. */
  1137. public function testAddFileNullFileName()
  1138. {
  1139. $this->setExpectedException(InvalidArgumentException::class, 'Filename is null');
  1140. $zipFile = new ZipFile();
  1141. $zipFile->addFile(null);
  1142. }
  1143. /**
  1144. * @throws ZipException
  1145. */
  1146. public function testAddFileCantExists()
  1147. {
  1148. $this->setExpectedException(InvalidArgumentException::class, 'File path/to/file is not readable');
  1149. $zipFile = new ZipFile();
  1150. $zipFile->addFile('path/to/file');
  1151. }
  1152. /**
  1153. * @throws ZipException
  1154. */
  1155. public function testAddFileUnsupportedMethod()
  1156. {
  1157. $this->setExpectedException(
  1158. ZipUnsupportMethodException::class,
  1159. 'Compression method 99 (AES Encryption) is not supported.'
  1160. );
  1161. $zipFile = new ZipFile();
  1162. $zipFile->addFile(__FILE__, null, ZipCompressionMethod::WINZIP_AES);
  1163. $zipFile->outputAsString();
  1164. }
  1165. /**
  1166. * @throws ZipException
  1167. */
  1168. public function testAddFileCantOpen()
  1169. {
  1170. $this->setExpectedException(InvalidArgumentException::class, 'is not readable');
  1171. if (static::skipTestForRootUser()) {
  1172. return;
  1173. }
  1174. static::assertNotFalse(file_put_contents($this->outputFilename, ''));
  1175. static::assertTrue(chmod($this->outputFilename, 0244));
  1176. $zipFile = new ZipFile();
  1177. $zipFile->addFile($this->outputFilename);
  1178. }
  1179. /**
  1180. * @throws ZipException
  1181. */
  1182. public function testAddDirNullDirname()
  1183. {
  1184. $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
  1185. $zipFile = new ZipFile();
  1186. $zipFile->addDir(null);
  1187. }
  1188. /**
  1189. * @throws ZipException
  1190. */
  1191. public function testAddDirEmptyDirname()
  1192. {
  1193. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1194. $zipFile = new ZipFile();
  1195. $zipFile->addDir('');
  1196. }
  1197. /**
  1198. * @throws ZipException
  1199. */
  1200. public function testAddDirCantExists()
  1201. {
  1202. $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
  1203. $zipFile = new ZipFile();
  1204. $zipFile->addDir(uniqid('', true));
  1205. }
  1206. /**
  1207. * @throws ZipException
  1208. */
  1209. public function testAddDirRecursiveNullDirname()
  1210. {
  1211. $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
  1212. $zipFile = new ZipFile();
  1213. $zipFile->addDirRecursive(null);
  1214. }
  1215. /**
  1216. * @throws ZipException
  1217. */
  1218. public function testAddDirRecursiveEmptyDirname()
  1219. {
  1220. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1221. $zipFile = new ZipFile();
  1222. $zipFile->addDirRecursive('');
  1223. }
  1224. /**
  1225. * @throws ZipException
  1226. */
  1227. public function testAddDirRecursiveCantExists()
  1228. {
  1229. $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
  1230. $zipFile = new ZipFile();
  1231. $zipFile->addDirRecursive(uniqid('', true));
  1232. }
  1233. /**
  1234. * @throws ZipException
  1235. */
  1236. public function testAddFilesFromGlobNull()
  1237. {
  1238. $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
  1239. $zipFile = new ZipFile();
  1240. $zipFile->addFilesFromGlob(null, '*.png');
  1241. }
  1242. /**
  1243. * @throws ZipException
  1244. */
  1245. public function testAddFilesFromGlobEmpty()
  1246. {
  1247. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1248. $zipFile = new ZipFile();
  1249. $zipFile->addFilesFromGlob('', '*.png');
  1250. }
  1251. /**
  1252. * @throws ZipException
  1253. */
  1254. public function testAddFilesFromGlobCantExists()
  1255. {
  1256. $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
  1257. $zipFile = new ZipFile();
  1258. $zipFile->addFilesFromGlob('path/to/path', '*.png');
  1259. }
  1260. /**
  1261. * @throws ZipException
  1262. */
  1263. public function testAddFilesFromGlobNullPattern()
  1264. {
  1265. $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
  1266. $zipFile = new ZipFile();
  1267. $zipFile->addFilesFromGlob(__DIR__, null);
  1268. }
  1269. /**
  1270. * @throws ZipException
  1271. */
  1272. public function testAddFilesFromGlobEmptyPattern()
  1273. {
  1274. $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
  1275. $zipFile = new ZipFile();
  1276. $zipFile->addFilesFromGlob(__DIR__, '');
  1277. }
  1278. /**
  1279. * @throws ZipException
  1280. */
  1281. public function testAddFilesFromGlobRecursiveNull()
  1282. {
  1283. $this->setExpectedException(InvalidArgumentException::class, 'Input dir is null');
  1284. $zipFile = new ZipFile();
  1285. $zipFile->addFilesFromGlobRecursive(null, '*.png');
  1286. }
  1287. /**
  1288. * @throws ZipException
  1289. */
  1290. public function testAddFilesFromGlobRecursiveEmpty()
  1291. {
  1292. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1293. $zipFile = new ZipFile();
  1294. $zipFile->addFilesFromGlobRecursive('', '*.png');
  1295. }
  1296. /**
  1297. * @throws ZipException
  1298. */
  1299. public function testAddFilesFromGlobRecursiveCantExists()
  1300. {
  1301. $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
  1302. $zipFile = new ZipFile();
  1303. $zipFile->addFilesFromGlobRecursive('path/to/path', '*.png');
  1304. }
  1305. /**
  1306. * @throws ZipException
  1307. */
  1308. public function testAddFilesFromGlobRecursiveNullPattern()
  1309. {
  1310. $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
  1311. $zipFile = new ZipFile();
  1312. $zipFile->addFilesFromGlobRecursive(__DIR__, null);
  1313. }
  1314. /**
  1315. * @throws ZipException
  1316. */
  1317. public function testAddFilesFromGlobRecursiveEmptyPattern()
  1318. {
  1319. $this->setExpectedException(InvalidArgumentException::class, 'The glob pattern is not specified');
  1320. $zipFile = new ZipFile();
  1321. $zipFile->addFilesFromGlobRecursive(__DIR__, '');
  1322. }
  1323. /**
  1324. * @throws ZipException
  1325. */
  1326. public function testAddFilesFromRegexDirectoryNull()
  1327. {
  1328. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1329. $zipFile = new ZipFile();
  1330. $zipFile->addFilesFromRegex(null, '~\.png$~i');
  1331. }
  1332. /**
  1333. * @throws ZipException
  1334. */
  1335. public function testAddFilesFromRegexDirectoryEmpty()
  1336. {
  1337. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1338. $zipFile = new ZipFile();
  1339. $zipFile->addFilesFromRegex('', '~\.png$~i');
  1340. }
  1341. /**
  1342. * @throws ZipException
  1343. */
  1344. public function testAddFilesFromRegexCantExists()
  1345. {
  1346. $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
  1347. $zipFile = new ZipFile();
  1348. $zipFile->addFilesFromRegex('path/to/path', '~\.png$~i');
  1349. }
  1350. /**
  1351. * @throws ZipException
  1352. */
  1353. public function testAddFilesFromRegexNullPattern()
  1354. {
  1355. $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
  1356. $zipFile = new ZipFile();
  1357. $zipFile->addFilesFromRegex(__DIR__, null);
  1358. }
  1359. /**
  1360. * @throws ZipException
  1361. */
  1362. public function testAddFilesFromRegexEmptyPattern()
  1363. {
  1364. $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
  1365. $zipFile = new ZipFile();
  1366. $zipFile->addFilesFromRegex(__DIR__, '');
  1367. }
  1368. /**
  1369. * @throws ZipException
  1370. */
  1371. public function testAddFilesFromRegexRecursiveDirectoryNull()
  1372. {
  1373. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1374. $zipFile = new ZipFile();
  1375. $zipFile->addFilesFromRegexRecursive(null, '~\.png$~i');
  1376. }
  1377. /**
  1378. * @throws ZipException
  1379. */
  1380. public function testAddFilesFromRegexRecursiveEmpty()
  1381. {
  1382. $this->setExpectedException(InvalidArgumentException::class, 'The input directory is not specified');
  1383. $zipFile = new ZipFile();
  1384. $zipFile->addFilesFromRegexRecursive('', '~\.png$~i');
  1385. }
  1386. /**
  1387. * @throws ZipException
  1388. */
  1389. public function testAddFilesFromRegexRecursiveCantExists()
  1390. {
  1391. $this->setExpectedException(InvalidArgumentException::class, 'does not exist');
  1392. $zipFile = new ZipFile();
  1393. $zipFile->addFilesFromGlobRecursive('path/to/path', '~\.png$~i');
  1394. }
  1395. /**
  1396. * @throws ZipException
  1397. */
  1398. public function testAddFilesFromRegexRecursiveNullPattern()
  1399. {
  1400. $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
  1401. $zipFile = new ZipFile();
  1402. $zipFile->addFilesFromRegexRecursive(__DIR__, null);
  1403. }
  1404. /**
  1405. * @throws ZipException
  1406. */
  1407. public function testAddFilesFromRegexRecursiveEmptyPattern()
  1408. {
  1409. $this->setExpectedException(InvalidArgumentException::class, 'The regex pattern is not specified');
  1410. $zipFile = new ZipFile();
  1411. $zipFile->addFilesFromRegexRecursive(__DIR__, '');
  1412. }
  1413. /**
  1414. * @throws ZipException
  1415. */
  1416. public function testSaveAsStreamBadStream()
  1417. {
  1418. $this->setExpectedException(InvalidArgumentException::class, 'handle is not resource');
  1419. $zipFile = new ZipFile();
  1420. /** @noinspection PhpParamsInspection */
  1421. $zipFile->saveAsStream('bad stream');
  1422. }
  1423. /**
  1424. * @throws ZipException
  1425. */
  1426. public function testSaveAsFileNotWritable()
  1427. {
  1428. $this->setExpectedException(InvalidArgumentException::class, 'can not open from write');
  1429. if (static::skipTestForRootUser()) {
  1430. return;
  1431. }
  1432. static::assertTrue(mkdir($this->outputDirname, 0444, true));
  1433. static::assertTrue(chmod($this->outputDirname, 0444));
  1434. $this->outputFilename = $this->outputDirname . \DIRECTORY_SEPARATOR . basename($this->outputFilename);
  1435. $zipFile = new ZipFile();
  1436. $zipFile->saveAsFile($this->outputFilename);
  1437. }
  1438. /**
  1439. * Test `ZipFile` implemented \ArrayAccess, \Countable and |iterator.
  1440. *
  1441. * @throws ZipException
  1442. * @throws \Exception
  1443. */
  1444. public function testZipFileArrayAccessAndCountableAndIterator()
  1445. {
  1446. $files = [];
  1447. $numFiles = mt_rand(20, 100);
  1448. for ($i = 0; $i < $numFiles; $i++) {
  1449. $files['file' . $i . '.txt'] = random_bytes(255);
  1450. }
  1451. $compressionMethods = [ZipCompressionMethod::STORED, ZipCompressionMethod::DEFLATED];
  1452. if (\extension_loaded('bz2')) {
  1453. $compressionMethods[] = ZipCompressionMethod::BZIP2;
  1454. }
  1455. $zipFile = new ZipFile();
  1456. $zipFile->setCompressionLevel(ZipCompressionLevel::SUPER_FAST);
  1457. $i = 0;
  1458. $countMethods = \count($compressionMethods);
  1459. foreach ($files as $entryName => $content) {
  1460. $compressionMethod = $compressionMethods[$i++ % $countMethods];
  1461. $zipFile->addFromString($entryName, $content, $compressionMethod);
  1462. }
  1463. $zipFile->saveAsFile($this->outputFilename);
  1464. $zipFile->close();
  1465. static::assertCorrectZipArchive($this->outputFilename);
  1466. $zipFile->openFile($this->outputFilename);
  1467. // Test \Countable
  1468. static::assertSame($zipFile->count(), $numFiles);
  1469. static::assertSame(\count($zipFile), $numFiles);
  1470. // Test \ArrayAccess
  1471. reset($files);
  1472. foreach ($zipFile as $entryName => $content) {
  1473. static::assertSame($entryName, key($files));
  1474. static::assertSame($content, current($files));
  1475. next($files);
  1476. }
  1477. // Test \Iterator
  1478. reset($files);
  1479. $iterator = new \ArrayIterator($zipFile);
  1480. $iterator->rewind();
  1481. while ($iterator->valid()) {
  1482. $key = $iterator->key();
  1483. $value = $iterator->current();
  1484. static::assertSame($key, key($files));
  1485. static::assertSame($value, current($files));
  1486. next($files);
  1487. $iterator->next();
  1488. }
  1489. $zipFile->close();
  1490. $zipFile = new ZipFile();
  1491. $zipFile['file1.txt'] = 'content 1';
  1492. $zipFile['dir/file2.txt'] = 'content 1';
  1493. $zipFile['dir/empty dir/'] = null;
  1494. $zipFile->saveAsFile($this->outputFilename);
  1495. $zipFile->close();
  1496. static::assertCorrectZipArchive($this->outputFilename);
  1497. $zipFile->openFile($this->outputFilename);
  1498. static::assertTrue(isset($zipFile['file1.txt']));
  1499. static::assertTrue(isset($zipFile['dir/file2.txt']));
  1500. static::assertTrue(isset($zipFile['dir/empty dir/']));
  1501. static::assertFalse(isset($zipFile['dir/empty dir/2/']));
  1502. $zipFile['dir/empty dir/2/'] = null;
  1503. unset($zipFile['dir/file2.txt'], $zipFile['dir/empty dir/']);
  1504. $zipFile->saveAsFile($this->outputFilename);
  1505. $zipFile->close();
  1506. static::assertCorrectZipArchive($this->outputFilename);
  1507. $zipFile->openFile($this->outputFilename);
  1508. static::assertTrue(isset($zipFile['file1.txt']));
  1509. static::assertFalse(isset($zipFile['dir/file2.txt']));
  1510. static::assertFalse(isset($zipFile['dir/empty dir/']));
  1511. static::assertTrue(isset($zipFile['dir/empty dir/2/']));
  1512. $zipFile->close();
  1513. }
  1514. /**
  1515. * @throws ZipException
  1516. */
  1517. public function testArrayAccessAddFile()
  1518. {
  1519. $entryName = 'path/to/file.dat';
  1520. $entryNameStream = 'path/to/' . basename(__FILE__);
  1521. $zipFile = new ZipFile();
  1522. $zipFile[$entryName] = new \SplFileInfo(__FILE__);
  1523. $zipFile[$entryNameStream] = fopen(__FILE__, 'rb');
  1524. $zipFile->saveAsFile($this->outputFilename);
  1525. $zipFile->close();
  1526. static::assertCorrectZipArchive($this->outputFilename);
  1527. $zipFile->openFile($this->outputFilename);
  1528. static::assertSame(\count($zipFile), 2);
  1529. static::assertTrue(isset($zipFile[$entryName]));
  1530. static::assertTrue(isset($zipFile[$entryNameStream]));
  1531. static::assertSame($zipFile[$entryName], file_get_contents(__FILE__));
  1532. static::assertSame($zipFile[$entryNameStream], file_get_contents(__FILE__));
  1533. $zipFile->close();
  1534. }
  1535. /**
  1536. * @throws ZipEntryNotFoundException
  1537. * @throws ZipException
  1538. * @throws \Exception
  1539. */
  1540. public function testUnknownCompressionMethod()
  1541. {
  1542. $zipFile = new ZipFile();
  1543. $zipFile->addFromString('file', 'content', ZipEntry::UNKNOWN);
  1544. $zipFile->addFromString('file2', base64_encode(random_bytes(512)), ZipEntry::UNKNOWN);
  1545. static::assertSame($zipFile->getEntryInfo('file')->getMethodName(), 'Stored');
  1546. static::assertSame($zipFile->getEntryInfo('file2')->getMethodName(), 'Deflated');
  1547. $zipFile->saveAsFile($this->outputFilename);
  1548. $zipFile->close();
  1549. $zipFile->openFile($this->outputFilename);
  1550. static::assertSame($zipFile->getEntryInfo('file')->getMethodName(), 'Stored');
  1551. static::assertSame($zipFile->getEntryInfo('file2')->getMethodName(), 'Deflated');
  1552. $zipFile->close();
  1553. }
  1554. /**
  1555. * @throws ZipException
  1556. */
  1557. public function testAddEmptyDirNullName()
  1558. {
  1559. $this->setExpectedException(InvalidArgumentException::class, 'Dir name is null');
  1560. $zipFile = new ZipFile();
  1561. $zipFile->addEmptyDir(null);
  1562. }
  1563. /**
  1564. * @throws ZipException
  1565. */
  1566. public function testAddEmptyDirEmptyName()
  1567. {
  1568. $this->setExpectedException(InvalidArgumentException::class, 'Empty dir name');
  1569. $zipFile = new ZipFile();
  1570. $zipFile->addEmptyDir('');
  1571. }
  1572. public function testNotFoundEntry()
  1573. {
  1574. $this->setExpectedException(ZipEntryNotFoundException::class, '"bad entry name"');
  1575. $zipFile = new ZipFile();
  1576. $zipFile['bad entry name'];
  1577. }
  1578. /**
  1579. * Test rewrite input file.
  1580. *
  1581. * @throws ZipException
  1582. */
  1583. public function testRewriteFile()
  1584. {
  1585. $zipFile = new ZipFile();
  1586. $zipFile['file'] = 'content';
  1587. $zipFile['file2'] = 'content2';
  1588. static::assertSame(\count($zipFile), 2);
  1589. $zipFile
  1590. ->saveAsFile($this->outputFilename)
  1591. ->close()
  1592. ;
  1593. $md5file = md5_file($this->outputFilename);
  1594. $zipFile->openFile($this->outputFilename);
  1595. static::assertSame(\count($zipFile), 2);
  1596. static::assertTrue(isset($zipFile['file']));
  1597. static::assertTrue(isset($zipFile['file2']));
  1598. $zipFile['file3'] = 'content3';
  1599. static::assertSame(\count($zipFile), 3);
  1600. $zipFile = $zipFile->rewrite();
  1601. static::assertSame(\count($zipFile), 3);
  1602. static::assertTrue(isset($zipFile['file']));
  1603. static::assertTrue(isset($zipFile['file2']));
  1604. static::assertTrue(isset($zipFile['file3']));
  1605. $zipFile->close();
  1606. static::assertNotSame(md5_file($this->outputFilename), $md5file);
  1607. }
  1608. /**
  1609. * Test rewrite for string.
  1610. *
  1611. * @throws ZipException
  1612. */
  1613. public function testRewriteString()
  1614. {
  1615. $zipFile = new ZipFile();
  1616. $zipFile['file'] = 'content';
  1617. $zipFile['file2'] = 'content2';
  1618. $zipFile->saveAsFile($this->outputFilename);
  1619. $zipFile->close();
  1620. $zipFile->openFromString(file_get_contents($this->outputFilename));
  1621. static::assertSame(\count($zipFile), 2);
  1622. static::assertTrue(isset($zipFile['file']));
  1623. static::assertTrue(isset($zipFile['file2']));
  1624. $zipFile['file3'] = 'content3';
  1625. $zipFile = $zipFile->rewrite();
  1626. static::assertSame(\count($zipFile), 3);
  1627. static::assertTrue(isset($zipFile['file']));
  1628. static::assertTrue(isset($zipFile['file2']));
  1629. static::assertTrue(isset($zipFile['file3']));
  1630. $zipFile->close();
  1631. }
  1632. /**
  1633. * @throws ZipException
  1634. */
  1635. public function testRewriteNullStream()
  1636. {
  1637. $this->setExpectedException(ZipException::class, 'input stream is null');
  1638. $zipFile = new ZipFile();
  1639. $zipFile->rewrite();
  1640. }
  1641. /**
  1642. * @throws ZipException
  1643. */
  1644. public function testFilename0()
  1645. {
  1646. $zipFile = new ZipFile();
  1647. $zipFile[0] = 0;
  1648. static::assertTrue(isset($zipFile[0]));
  1649. static::assertTrue(isset($zipFile['0']));
  1650. static::assertCount(1, $zipFile);
  1651. $zipFile
  1652. ->saveAsFile($this->outputFilename)
  1653. ->close()
  1654. ;
  1655. static::assertCorrectZipArchive($this->outputFilename);
  1656. $zipFile->openFile($this->outputFilename);
  1657. static::assertTrue(isset($zipFile[0]));
  1658. static::assertTrue(isset($zipFile['0']));
  1659. static::assertSame($zipFile['0'], '0');
  1660. static::assertCount(1, $zipFile);
  1661. $zipFile->close();
  1662. static::assertTrue(unlink($this->outputFilename));
  1663. $zipFile = new ZipFile();
  1664. $zipFile->addFromString(0, 0);
  1665. static::assertTrue(isset($zipFile[0]));
  1666. static::assertTrue(isset($zipFile['0']));
  1667. static::assertCount(1, $zipFile);
  1668. $zipFile
  1669. ->saveAsFile($this->outputFilename)
  1670. ->close()
  1671. ;
  1672. static::assertCorrectZipArchive($this->outputFilename);
  1673. }
  1674. /**
  1675. * @throws ZipException
  1676. */
  1677. public function testPsrResponse()
  1678. {
  1679. $zipFile = new ZipFile();
  1680. for ($i = 0; $i < 10; $i++) {
  1681. $zipFile[$i] = $i;
  1682. }
  1683. $filename = 'file.jar';
  1684. $response = $zipFile->outputAsResponse(new Response(), $filename);
  1685. static::assertInstanceOf(ResponseInterface::class, $response);
  1686. static::assertSame('application/java-archive', $response->getHeaderLine('content-type'));
  1687. static::assertSame('attachment; filename="file.jar"', $response->getHeaderLine('content-disposition'));
  1688. }
  1689. /**
  1690. * @dataProvider provideCompressionLevels
  1691. *
  1692. * @param int $compressionLevel
  1693. *
  1694. * @throws ZipEntryNotFoundException
  1695. * @throws ZipException
  1696. * @throws \Exception
  1697. */
  1698. public function testCompressionLevel($compressionLevel)
  1699. {
  1700. $fileContent = random_bytes(512);
  1701. $entryName = 'file.txt';
  1702. $zipFile = new ZipFile();
  1703. $zipFile
  1704. ->addFromString($entryName, $fileContent, ZipCompressionMethod::DEFLATED)
  1705. ->setCompressionLevelEntry($entryName, $compressionLevel)
  1706. ->saveAsFile($this->outputFilename)
  1707. ->close()
  1708. ;
  1709. static::assertCorrectZipArchive($this->outputFilename);
  1710. $zipFile->openFile($this->outputFilename);
  1711. static::assertSame($zipFile->getEntryContents($entryName), $fileContent);
  1712. static::assertSame($zipFile->getEntry($entryName)->getCompressionLevel(), $compressionLevel);
  1713. static::assertSame($zipFile->getEntryInfo($entryName)->getCompressionLevel(), $compressionLevel);
  1714. $zipFile->close();
  1715. }
  1716. /**
  1717. * @return array
  1718. */
  1719. public function provideCompressionLevels()
  1720. {
  1721. return [
  1722. [ZipCompressionLevel::MAXIMUM],
  1723. [ZipCompressionLevel::NORMAL],
  1724. [ZipCompressionLevel::FAST],
  1725. [ZipCompressionLevel::SUPER_FAST],
  1726. ];
  1727. }
  1728. /**
  1729. * @throws ZipException
  1730. */
  1731. public function testInvalidCompressionLevel()
  1732. {
  1733. $this->setExpectedException(InvalidArgumentException::class, 'Invalid compression level');
  1734. $zipFile = new ZipFile();
  1735. $zipFile->addFromString('file', 'content');
  1736. $zipFile->setCompressionLevel(15);
  1737. }
  1738. /**
  1739. * @throws ZipException
  1740. */
  1741. public function testInvalidCompressionLevelEntry()
  1742. {
  1743. $this->setExpectedException(InvalidArgumentException::class, 'Invalid compression level');
  1744. $zipFile = new ZipFile();
  1745. $zipFile->addFromString('file', 'content');
  1746. $zipFile->setCompressionLevelEntry('file', 15);
  1747. }
  1748. /**
  1749. * @throws ZipException
  1750. */
  1751. public function testCompressionGlobal()
  1752. {
  1753. $zipFile = new ZipFile();
  1754. for ($i = 0; $i < 10; $i++) {
  1755. $zipFile->addFromString('file' . $i, 'content', ZipCompressionMethod::DEFLATED);
  1756. }
  1757. $zipFile
  1758. ->setCompressionLevel(ZipCompressionLevel::SUPER_FAST)
  1759. ->saveAsFile($this->outputFilename)
  1760. ->close()
  1761. ;
  1762. static::assertCorrectZipArchive($this->outputFilename);
  1763. $zipFile->openFile($this->outputFilename);
  1764. $infoList = $zipFile->getAllInfo();
  1765. array_walk(
  1766. $infoList,
  1767. function (ZipInfo $zipInfo) {
  1768. $this->assertSame($zipInfo->getCompressionLevel(), ZipCompressionLevel::SUPER_FAST);
  1769. }
  1770. );
  1771. $zipFile->close();
  1772. }
  1773. /**
  1774. * @throws ZipEntryNotFoundException
  1775. * @throws ZipException
  1776. */
  1777. public function testCompressionMethodEntry()
  1778. {
  1779. $zipFile = new ZipFile();
  1780. $zipFile->addFromString('file', 'content', ZipCompressionMethod::STORED);
  1781. $zipFile->saveAsFile($this->outputFilename);
  1782. $zipFile->close();
  1783. $zipFile->openFile($this->outputFilename);
  1784. static::assertSame($zipFile->getEntryInfo('file')->getMethodName(), 'Stored');
  1785. $zipFile->setCompressionMethodEntry('file', ZipCompressionMethod::DEFLATED);
  1786. static::assertSame($zipFile->getEntryInfo('file')->getMethodName(), 'Deflated');
  1787. $zipFile->rewrite();
  1788. static::assertSame($zipFile->getEntryInfo('file')->getMethodName(), 'Deflated');
  1789. }
  1790. /**
  1791. * @throws ZipException
  1792. */
  1793. public function testInvalidCompressionMethodEntry()
  1794. {
  1795. $this->setExpectedException(
  1796. ZipUnsupportMethodException::class,
  1797. 'Compression method 99 (AES Encryption) is not supported.'
  1798. );
  1799. $zipFile = new ZipFile();
  1800. $zipFile->addFromString('file', 'content', ZipCompressionMethod::STORED);
  1801. $zipFile->setCompressionMethodEntry('file', 99);
  1802. $zipFile->outputAsString();
  1803. }
  1804. /**
  1805. * @throws ZipException
  1806. */
  1807. public function testUnchangeAll()
  1808. {
  1809. $zipFile = new ZipFile();
  1810. for ($i = 0; $i < 10; $i++) {
  1811. $zipFile[$i] = $i;
  1812. }
  1813. $zipFile->setArchiveComment('comment');
  1814. static::assertCount(10, $zipFile);
  1815. static::assertSame($zipFile->getArchiveComment(), 'comment');
  1816. $zipFile->saveAsFile($this->outputFilename);
  1817. $zipFile->unchangeAll();
  1818. static::assertCount(0, $zipFile);
  1819. static::assertNull($zipFile->getArchiveComment());
  1820. $zipFile->close();
  1821. $zipFile->openFile($this->outputFilename);
  1822. static::assertCount(10, $zipFile);
  1823. static::assertSame($zipFile->getArchiveComment(), 'comment');
  1824. for ($i = 10; $i < 100; $i++) {
  1825. $zipFile[$i] = $i;
  1826. }
  1827. $zipFile->setArchiveComment('comment 2');
  1828. static::assertCount(100, $zipFile);
  1829. static::assertSame($zipFile->getArchiveComment(), 'comment 2');
  1830. $zipFile->unchangeAll();
  1831. static::assertCount(10, $zipFile);
  1832. static::assertSame($zipFile->getArchiveComment(), 'comment');
  1833. $zipFile->close();
  1834. }
  1835. /**
  1836. * @throws ZipException
  1837. */
  1838. public function testUnchangeArchiveComment()
  1839. {
  1840. $zipFile = new ZipFile();
  1841. for ($i = 0; $i < 10; $i++) {
  1842. $zipFile[$i] = $i;
  1843. }
  1844. $zipFile->setArchiveComment('comment');
  1845. static::assertSame($zipFile->getArchiveComment(), 'comment');
  1846. $zipFile->saveAsFile($this->outputFilename);
  1847. $zipFile->unchangeArchiveComment();
  1848. static::assertNull($zipFile->getArchiveComment());
  1849. $zipFile->close();
  1850. $zipFile->openFile($this->outputFilename);
  1851. static::assertSame($zipFile->getArchiveComment(), 'comment');
  1852. $zipFile->setArchiveComment('comment 2');
  1853. static::assertSame($zipFile->getArchiveComment(), 'comment 2');
  1854. $zipFile->unchangeArchiveComment();
  1855. static::assertSame($zipFile->getArchiveComment(), 'comment');
  1856. $zipFile->close();
  1857. }
  1858. /**
  1859. * @throws ZipEntryNotFoundException
  1860. * @throws ZipException
  1861. */
  1862. public function testUnchangeEntry()
  1863. {
  1864. $zipFile = new ZipFile();
  1865. $zipFile['file 1'] = 'content 1';
  1866. $zipFile['file 2'] = 'content 2';
  1867. $zipFile
  1868. ->saveAsFile($this->outputFilename)
  1869. ->close()
  1870. ;
  1871. $zipFile->openFile($this->outputFilename);
  1872. $zipFile['file 1'] = 'modify content 1';
  1873. $zipFile->setPasswordEntry('file 1', 'password');
  1874. static::assertSame($zipFile['file 1'], 'modify content 1');
  1875. static::assertTrue($zipFile->getEntryInfo('file 1')->isEncrypted());
  1876. static::assertSame($zipFile['file 2'], 'content 2');
  1877. static::assertFalse($zipFile->getEntryInfo('file 2')->isEncrypted());
  1878. $zipFile->unchangeEntry('file 1');
  1879. static::assertSame($zipFile['file 1'], 'content 1');
  1880. static::assertFalse($zipFile->getEntryInfo('file 1')->isEncrypted());
  1881. static::assertSame($zipFile['file 2'], 'content 2');
  1882. static::assertFalse($zipFile->getEntryInfo('file 2')->isEncrypted());
  1883. $zipFile->close();
  1884. }
  1885. /**
  1886. * @runInSeparateProcess
  1887. *
  1888. * @dataProvider provideOutputAsAttachment
  1889. *
  1890. * @param string $zipFilename
  1891. * @param string|null $mimeType
  1892. * @param string $expectedMimeType
  1893. * @param bool $attachment
  1894. * @param string $expectedAttachment
  1895. *
  1896. * @throws ZipException
  1897. */
  1898. public function testOutputAsAttachment($zipFilename, $mimeType, $expectedMimeType, $attachment, $expectedAttachment)
  1899. {
  1900. $zipFile = new ZipFile();
  1901. $file1Contents = 'content 1';
  1902. $zipFile['file 1'] = $file1Contents;
  1903. ob_start();
  1904. $zipFile->outputAsAttachment($zipFilename, $mimeType, $attachment);
  1905. $zipContents = ob_get_clean();
  1906. $zipFile->close();
  1907. $length = \strlen($zipContents);
  1908. static::assertTrue($length > 0);
  1909. $zipFile->openFromString($zipContents);
  1910. static::assertSame($zipFile['file 1'], $file1Contents);
  1911. $zipFile->close();
  1912. if (\function_exists('xdebug_get_headers')) {
  1913. $expectedHeaders = [
  1914. 'Content-Disposition: ' . $expectedAttachment . '; filename="' . $zipFilename . '"',
  1915. 'Content-Type: ' . $expectedMimeType,
  1916. 'Content-Length: ' . $length,
  1917. ];
  1918. /** @noinspection ForgottenDebugOutputInspection */
  1919. /** @noinspection PhpComposerExtensionStubsInspection */
  1920. static::assertSame($expectedHeaders, xdebug_get_headers());
  1921. }
  1922. }
  1923. /**
  1924. * @return array
  1925. */
  1926. public function provideOutputAsAttachment()
  1927. {
  1928. return [
  1929. ['file.zip', null, 'application/zip', true, 'attachment'],
  1930. ['file.zip', 'application/x-zip', 'application/x-zip', false, 'inline'],
  1931. ['file.apk', null, 'application/vnd.android.package-archive', true, 'attachment'],
  1932. ];
  1933. }
  1934. /**
  1935. * @dataProvider provideGetEntryStream
  1936. *
  1937. * @param ZipFile $zipFile
  1938. * @param string $entryName
  1939. * @param string $contents
  1940. *
  1941. * @throws ZipEntryNotFoundException
  1942. * @throws ZipException
  1943. */
  1944. public function testReopenEntryStream(ZipFile $zipFile, $entryName, $contents)
  1945. {
  1946. for ($i = 0; $i < 2; $i++) {
  1947. $fp = $zipFile->getEntryStream($entryName);
  1948. static::assertInternalType('resource', $fp);
  1949. static::assertSame(stream_get_contents($fp), $contents);
  1950. fclose($fp);
  1951. }
  1952. $zipFile->close();
  1953. }
  1954. /**
  1955. * @throws \Exception
  1956. *
  1957. * @return array
  1958. */
  1959. public function provideGetEntryStream()
  1960. {
  1961. $entryName = 'entry';
  1962. $contents = random_bytes(1024);
  1963. $zipFileSpl = new ZipFile();
  1964. $zipFileSpl->addSplFile(new \SplFileInfo(__FILE__), $entryName);
  1965. return [
  1966. [(new ZipFile())->addFromString($entryName, $contents), $entryName, $contents],
  1967. [(new ZipFile())->addFile(__FILE__, $entryName), $entryName, file_get_contents(__FILE__)],
  1968. [
  1969. (new ZipFile())->addFromStream(fopen(__FILE__, 'rb'), $entryName),
  1970. $entryName,
  1971. file_get_contents(__FILE__),
  1972. ],
  1973. [$zipFileSpl, $entryName, file_get_contents(__FILE__)],
  1974. ];
  1975. }
  1976. /**
  1977. * @throws ZipException
  1978. */
  1979. public function testGetEntries()
  1980. {
  1981. $zipFile = new ZipFile();
  1982. for ($i = 0; $i < 100; $i++) {
  1983. $zipFile->addFromString($i . '.txt', 'contents ' . $i);
  1984. }
  1985. $zipFile->saveAsFile($this->outputFilename);
  1986. $zipFile->close();
  1987. $zipFile->openFile($this->outputFilename);
  1988. $zipEntries = $zipFile->getEntries();
  1989. static::assertCount(100, $zipEntries);
  1990. foreach ($zipEntries as $zipEntry) {
  1991. static::assertInstanceOf(ZipEntry::class, $zipEntry);
  1992. static::assertNotSame($zipEntry->getDosTime(), 0);
  1993. $zipEntry->setDosTime(0);
  1994. $zipEntry->setCreatedOS(ZipPlatform::OS_DOS);
  1995. $zipEntry->setExtractedOS(ZipPlatform::OS_DOS);
  1996. $zipEntry->setInternalAttributes(1);
  1997. $zipEntry->setExternalAttributes(0);
  1998. }
  1999. $zipFile->rewrite();
  2000. self::assertCorrectZipArchive($this->outputFilename);
  2001. foreach ($zipFile->getEntries() as $zipEntry) {
  2002. static::assertSame($zipEntry->getDosTime(), 0);
  2003. static::assertSame($zipEntry->getExtractedOS(), ZipPlatform::OS_DOS);
  2004. static::assertSame($zipEntry->getCreatedOS(), ZipPlatform::OS_DOS);
  2005. static::assertSame($zipEntry->getInternalAttributes(), 1);
  2006. static::assertSame($zipEntry->getExternalAttributes(), 0);
  2007. }
  2008. $zipFile->close();
  2009. }
  2010. /**
  2011. * @throws ZipException
  2012. */
  2013. public function testRenameWithRecompressData()
  2014. {
  2015. $entryName = 'file.txt';
  2016. $newEntryName = 'rename_file.txt';
  2017. $contents = str_repeat('Test' . \PHP_EOL, 1024);
  2018. $zipFile = new ZipFile();
  2019. $zipFile->addFromString($entryName, $contents, ZipCompressionMethod::DEFLATED);
  2020. $zipFile->saveAsFile($this->outputFilename);
  2021. $zipFile->close();
  2022. self::assertCorrectZipArchive($this->outputFilename);
  2023. $zipFile->openFile($this->outputFilename);
  2024. $zipFile->rename($entryName, $newEntryName);
  2025. $zipFile->setCompressionMethodEntry($newEntryName, ZipCompressionMethod::STORED);
  2026. $zipFile->saveAsFile($this->outputFilename);
  2027. $zipFile->close();
  2028. self::assertCorrectZipArchive($this->outputFilename);
  2029. $zipFile->openFile($this->outputFilename);
  2030. static::assertSame($zipFile->getEntry($newEntryName)->getCompressionMethod(), ZipCompressionMethod::STORED);
  2031. $zipFile->close();
  2032. }
  2033. /**
  2034. * @throws ZipEntryNotFoundException
  2035. * @throws ZipException
  2036. */
  2037. public function testReplaceEntryContentsByFile()
  2038. {
  2039. $entryName = basename(__FILE__);
  2040. $zipFile = new ZipFile();
  2041. $zipFile[$entryName] = 'contents';
  2042. $zipFile->saveAsFile($this->outputFilename);
  2043. $zipFile->close();
  2044. $zipFile->openFile($this->outputFilename);
  2045. $entry = $zipFile->getEntry($entryName);
  2046. $data = new ZipFileData($entry, new \SplFileInfo(__FILE__));
  2047. $entry->setData($data);
  2048. $zipFile->saveAsFile($this->outputFilename);
  2049. $zipFile->close();
  2050. self::assertCorrectZipArchive($this->outputFilename);
  2051. $zipFile->openFile($this->outputFilename);
  2052. static::assertSame(
  2053. $zipFile->getEntryContents($entryName),
  2054. file_get_contents(__FILE__)
  2055. );
  2056. $zipFile->close();
  2057. }
  2058. }