PageRenderTime 68ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/spout/src/Spout/Writer/Common/Helper/ZipHelper.php

https://bitbucket.org/moodle/moodle
PHP | 217 lines | 92 code | 24 blank | 101 comment | 5 complexity | 4c670cfdb4fd3b1f6f2ced9c783fd9f6 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  1. <?php
  2. namespace Box\Spout\Writer\Common\Helper;
  3. use Box\Spout\Writer\Common\Creator\InternalEntityFactory;
  4. /**
  5. * Class ZipHelper
  6. * This class provides helper functions to create zip files
  7. */
  8. class ZipHelper
  9. {
  10. const ZIP_EXTENSION = '.zip';
  11. /** Controls what to do when trying to add an existing file */
  12. const EXISTING_FILES_SKIP = 'skip';
  13. const EXISTING_FILES_OVERWRITE = 'overwrite';
  14. /** @var InternalEntityFactory Factory to create entities */
  15. private $entityFactory;
  16. /**
  17. * @param InternalEntityFactory $entityFactory Factory to create entities
  18. */
  19. public function __construct($entityFactory)
  20. {
  21. $this->entityFactory = $entityFactory;
  22. }
  23. /**
  24. * Returns a new ZipArchive instance pointing at the given path.
  25. *
  26. * @param string $tmpFolderPath Path of the temp folder where the zip file will be created
  27. * @return \ZipArchive
  28. */
  29. public function createZip($tmpFolderPath)
  30. {
  31. $zip = $this->entityFactory->createZipArchive();
  32. $zipFilePath = $tmpFolderPath . self::ZIP_EXTENSION;
  33. $zip->open($zipFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
  34. return $zip;
  35. }
  36. /**
  37. * @param \ZipArchive $zip An opened zip archive object
  38. * @return string Path where the zip file of the given folder will be created
  39. */
  40. public function getZipFilePath(\ZipArchive $zip)
  41. {
  42. return $zip->filename;
  43. }
  44. /**
  45. * Adds the given file, located under the given root folder to the archive.
  46. * The file will be compressed.
  47. *
  48. * Example of use:
  49. * addFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
  50. * => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
  51. *
  52. * @param \ZipArchive $zip An opened zip archive object
  53. * @param string $rootFolderPath Path of the root folder that will be ignored in the archive tree.
  54. * @param string $localFilePath Path of the file to be added, under the root folder
  55. * @param string $existingFileMode Controls what to do when trying to add an existing file
  56. * @return void
  57. */
  58. public function addFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
  59. {
  60. $this->addFileToArchiveWithCompressionMethod(
  61. $zip,
  62. $rootFolderPath,
  63. $localFilePath,
  64. $existingFileMode,
  65. \ZipArchive::CM_DEFAULT
  66. );
  67. }
  68. /**
  69. * Adds the given file, located under the given root folder to the archive.
  70. * The file will NOT be compressed.
  71. *
  72. * Example of use:
  73. * addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
  74. * => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
  75. *
  76. * @param \ZipArchive $zip An opened zip archive object
  77. * @param string $rootFolderPath Path of the root folder that will be ignored in the archive tree.
  78. * @param string $localFilePath Path of the file to be added, under the root folder
  79. * @param string $existingFileMode Controls what to do when trying to add an existing file
  80. * @return void
  81. */
  82. public function addUncompressedFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
  83. {
  84. $this->addFileToArchiveWithCompressionMethod(
  85. $zip,
  86. $rootFolderPath,
  87. $localFilePath,
  88. $existingFileMode,
  89. \ZipArchive::CM_STORE
  90. );
  91. }
  92. /**
  93. * Adds the given file, located under the given root folder to the archive.
  94. * The file will NOT be compressed.
  95. *
  96. * Example of use:
  97. * addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');
  98. * => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'
  99. *
  100. * @param \ZipArchive $zip An opened zip archive object
  101. * @param string $rootFolderPath Path of the root folder that will be ignored in the archive tree.
  102. * @param string $localFilePath Path of the file to be added, under the root folder
  103. * @param string $existingFileMode Controls what to do when trying to add an existing file
  104. * @param int $compressionMethod The compression method
  105. * @return void
  106. */
  107. protected function addFileToArchiveWithCompressionMethod($zip, $rootFolderPath, $localFilePath, $existingFileMode, $compressionMethod)
  108. {
  109. if (!$this->shouldSkipFile($zip, $localFilePath, $existingFileMode)) {
  110. $normalizedFullFilePath = $this->getNormalizedRealPath($rootFolderPath . '/' . $localFilePath);
  111. $zip->addFile($normalizedFullFilePath, $localFilePath);
  112. if (self::canChooseCompressionMethod()) {
  113. $zip->setCompressionName($localFilePath, $compressionMethod);
  114. }
  115. }
  116. }
  117. /**
  118. * @return bool Whether it is possible to choose the desired compression method to be used
  119. */
  120. public static function canChooseCompressionMethod()
  121. {
  122. // setCompressionName() is a PHP7+ method...
  123. return (\method_exists(new \ZipArchive(), 'setCompressionName'));
  124. }
  125. /**
  126. * @param \ZipArchive $zip An opened zip archive object
  127. * @param string $folderPath Path to the folder to be zipped
  128. * @param string $existingFileMode Controls what to do when trying to add an existing file
  129. * @return void
  130. */
  131. public function addFolderToArchive($zip, $folderPath, $existingFileMode = self::EXISTING_FILES_OVERWRITE)
  132. {
  133. $folderRealPath = $this->getNormalizedRealPath($folderPath) . '/';
  134. $itemIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
  135. foreach ($itemIterator as $itemInfo) {
  136. $itemRealPath = $this->getNormalizedRealPath($itemInfo->getPathname());
  137. $itemLocalPath = \str_replace($folderRealPath, '', $itemRealPath);
  138. if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) {
  139. $zip->addFile($itemRealPath, $itemLocalPath);
  140. }
  141. }
  142. }
  143. /**
  144. * @param \ZipArchive $zip
  145. * @param string $itemLocalPath
  146. * @param string $existingFileMode
  147. * @return bool Whether the file should be added to the archive or skipped
  148. */
  149. protected function shouldSkipFile($zip, $itemLocalPath, $existingFileMode)
  150. {
  151. // Skip files if:
  152. // - EXISTING_FILES_SKIP mode chosen
  153. // - File already exists in the archive
  154. return ($existingFileMode === self::EXISTING_FILES_SKIP && $zip->locateName($itemLocalPath) !== false);
  155. }
  156. /**
  157. * Returns canonicalized absolute pathname, containing only forward slashes.
  158. *
  159. * @param string $path Path to normalize
  160. * @return string Normalized and canonicalized path
  161. */
  162. protected function getNormalizedRealPath($path)
  163. {
  164. $realPath = \realpath($path);
  165. return \str_replace(DIRECTORY_SEPARATOR, '/', $realPath);
  166. }
  167. /**
  168. * Closes the archive and copies it into the given stream
  169. *
  170. * @param \ZipArchive $zip An opened zip archive object
  171. * @param resource $streamPointer Pointer to the stream to copy the zip
  172. * @return void
  173. */
  174. public function closeArchiveAndCopyToStream($zip, $streamPointer)
  175. {
  176. $zipFilePath = $zip->filename;
  177. $zip->close();
  178. $this->copyZipToStream($zipFilePath, $streamPointer);
  179. }
  180. /**
  181. * Streams the contents of the zip file into the given stream
  182. *
  183. * @param string $zipFilePath Path of the zip file
  184. * @param resource $pointer Pointer to the stream to copy the zip
  185. * @return void
  186. */
  187. protected function copyZipToStream($zipFilePath, $pointer)
  188. {
  189. $zipFilePointer = \fopen($zipFilePath, 'r');
  190. \stream_copy_to_stream($zipFilePointer, $pointer);
  191. \fclose($zipFilePointer);
  192. }
  193. }