AutorÃa | Ultima modificación | Ver Log |
<?phpdeclare(strict_types=1);namespace OpenSpout\Writer\Common\Helper;use RecursiveDirectoryIterator;use RecursiveIteratorIterator;use SplFileInfo;use ZipArchive;/*** @internal*/final class ZipHelper{public const ZIP_EXTENSION = '.zip';/*** Controls what to do when trying to add an existing file.*/public const EXISTING_FILES_SKIP = 'skip';public const EXISTING_FILES_OVERWRITE = 'overwrite';/*** Returns a new ZipArchive instance pointing at the given path.** @param string $tmpFolderPath Path of the temp folder where the zip file will be created*/public function createZip(string $tmpFolderPath): ZipArchive{$zip = new ZipArchive();$zipFilePath = $tmpFolderPath.self::ZIP_EXTENSION;$zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE);return $zip;}/*** @param ZipArchive $zip An opened zip archive object** @return string Path where the zip file of the given folder will be created*/public function getZipFilePath(ZipArchive $zip): string{return $zip->filename;}/*** Adds the given file, located under the given root folder to the archive.* The file will be compressed.** Example of use:* addFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'** @param ZipArchive $zip An opened zip archive object* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree* @param string $localFilePath Path of the file to be added, under the root folder* @param string $existingFileMode Controls what to do when trying to add an existing file*/public function addFileToArchive(ZipArchive $zip, string $rootFolderPath, string $localFilePath, string $existingFileMode = self::EXISTING_FILES_OVERWRITE): void{$this->addFileToArchiveWithCompressionMethod($zip,$rootFolderPath,$localFilePath,$existingFileMode,ZipArchive::CM_DEFAULT);}/*** Adds the given file, located under the given root folder to the archive.* The file will NOT be compressed.** Example of use:* addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'** @param ZipArchive $zip An opened zip archive object* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree* @param string $localFilePath Path of the file to be added, under the root folder* @param string $existingFileMode Controls what to do when trying to add an existing file*/public function addUncompressedFileToArchive(ZipArchive $zip, string $rootFolderPath, string $localFilePath, string $existingFileMode = self::EXISTING_FILES_OVERWRITE): void{$this->addFileToArchiveWithCompressionMethod($zip,$rootFolderPath,$localFilePath,$existingFileMode,ZipArchive::CM_STORE);}/*** @param ZipArchive $zip An opened zip archive object* @param string $folderPath Path to the folder to be zipped* @param string $existingFileMode Controls what to do when trying to add an existing file*/public function addFolderToArchive(ZipArchive $zip, string $folderPath, string $existingFileMode = self::EXISTING_FILES_OVERWRITE): void{$folderRealPath = $this->getNormalizedRealPath($folderPath).'/';$itemIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folderPath, RecursiveDirectoryIterator::SKIP_DOTS),RecursiveIteratorIterator::SELF_FIRST);foreach ($itemIterator as $itemInfo) {\assert($itemInfo instanceof SplFileInfo);$itemRealPath = $this->getNormalizedRealPath($itemInfo->getPathname());$itemLocalPath = str_replace($folderRealPath, '', $itemRealPath);if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) {$zip->addFile($itemRealPath, $itemLocalPath);}}}/*** Closes the archive and copies it into the given stream.** @param ZipArchive $zip An opened zip archive object* @param resource $streamPointer Pointer to the stream to copy the zip*/public function closeArchiveAndCopyToStream(ZipArchive $zip, $streamPointer): void{$zipFilePath = $zip->filename;$zip->close();$this->copyZipToStream($zipFilePath, $streamPointer);}/*** Adds the given file, located under the given root folder to the archive.* The file will NOT be compressed.** Example of use:* addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml');* => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml'** @param ZipArchive $zip An opened zip archive object* @param string $rootFolderPath path of the root folder that will be ignored in the archive tree* @param string $localFilePath Path of the file to be added, under the root folder* @param string $existingFileMode Controls what to do when trying to add an existing file* @param int $compressionMethod The compression method*/private function addFileToArchiveWithCompressionMethod(ZipArchive $zip, string $rootFolderPath, string $localFilePath, string $existingFileMode, int $compressionMethod): void{$normalizedLocalFilePath = str_replace('\\', '/', $localFilePath);if (!$this->shouldSkipFile($zip, $normalizedLocalFilePath, $existingFileMode)) {$normalizedFullFilePath = $this->getNormalizedRealPath($rootFolderPath.'/'.$normalizedLocalFilePath);$zip->addFile($normalizedFullFilePath, $normalizedLocalFilePath);$zip->setCompressionName($normalizedLocalFilePath, $compressionMethod);}}/*** @return bool Whether the file should be added to the archive or skipped*/private function shouldSkipFile(ZipArchive $zip, string $itemLocalPath, string $existingFileMode): bool{// Skip files if:// - EXISTING_FILES_SKIP mode chosen// - File already exists in the archivereturn self::EXISTING_FILES_SKIP === $existingFileMode && false !== $zip->locateName($itemLocalPath);}/*** Returns canonicalized absolute pathname, containing only forward slashes.** @param string $path Path to normalize** @return string Normalized and canonicalized path*/private function getNormalizedRealPath(string $path): string{$realPath = realpath($path);\assert(false !== $realPath);return str_replace(\DIRECTORY_SEPARATOR, '/', $realPath);}/*** Streams the contents of the zip file into the given stream.** @param string $zipFilePath Path of the zip file* @param resource $pointer Pointer to the stream to copy the zip*/private function copyZipToStream(string $zipFilePath, $pointer): void{$zipFilePointer = fopen($zipFilePath, 'r');\assert(false !== $zipFilePointer);stream_copy_to_stream($zipFilePointer, $pointer);fclose($zipFilePointer);}}