Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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