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