Passed
Push — master ( a862c8...fef60d )
by Siad
05:54
created

ZipTask::addFilesetsToArchive()   B

Complexity

Conditions 8
Paths 11

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8.013

Importance

Changes 0
Metric Value
cc 8
eloc 17
c 0
b 0
f 0
nc 11
nop 1
dl 0
loc 28
ccs 16
cts 17
cp 0.9412
crap 8.013
rs 8.4444
1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * Creates a zip archive using PHP ZipArchive extension/
22
 *
23
 * @author  Michiel Rook <[email protected]>
24
 * @package phing.tasks.ext
25
 * @since   2.1.0
26
 */
27
class ZipTask extends MatchingTask
28
{
29
    /**
30
     * @var PhingFile
31
     */
32
    private $zipFile;
33
34
    /**
35
     * @var PhingFile
36
     */
37
    private $baseDir;
38
39
    /**
40
     * Whether to include empty dirs in the archive.
41
     */
42
    private $includeEmpty = true;
43
44
    private $filesets = [];
45
46
    private $ignoreLinks = false;
47
48
    /**
49
     * File path prefix in zip archive
50
     *
51
     * @var string
52
     */
53
    private $prefix = null;
54
55
    /**
56
     * Comment for zip archive.
57
     *
58
     * @var string $comment
59
     */
60
    private $comment = '';
61
62
    /**
63
     * Add a new fileset.
64
     *
65
     * @return ZipFileSet
66
     */
67 2
    public function createFileSet()
68
    {
69 2
        $this->fileset = new ZipFileSet();
70 2
        $this->filesets[] = $this->fileset;
71
72 2
        return $this->fileset;
73
    }
74
75
    /**
76
     * Add a new fileset.
77
     *
78
     * @param ZipFileSet $fileset
79
     */
80 1
    public function addZipFileSet(ZipFileSet $fileset)
81
    {
82 1
        $this->filesets[] = $fileset;
83 1
    }
84
85
    /**
86
     * Set is the name/location of where to create the zip file.
87
     *
88
     * @param PhingFile $destFile The output of the zip
89
     */
90 4
    public function setDestFile(PhingFile $destFile)
91
    {
92 4
        $this->zipFile = $destFile;
93 4
    }
94
95
    /**
96
     * This is the base directory to look in for things to zip.
97
     *
98
     * @param PhingFile $baseDir
99
     */
100 1
    public function setBasedir(PhingFile $baseDir)
101
    {
102 1
        $this->baseDir = $baseDir;
103 1
    }
104
105
    /**
106
     * Sets the file path prefix for file in the zip file.
107
     *
108
     * @param string $prefix Prefix
109
     *
110
     * @return void
111
     */
112
    public function setPrefix($prefix)
113
    {
114
        $this->prefix = $prefix;
115
    }
116
117
    /**
118
     * Set the include empty dirs flag.
119
     *
120
     * @param  boolean  Flag if empty dirs should be tarred too
0 ignored issues
show
Bug introduced by
The type Flag was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
121
     * @return void
122
     */
123
    public function setIncludeEmptyDirs($bool)
124
    {
125
        $this->includeEmpty = (bool) $bool;
126
    }
127
128
    /**
129
     * Set the ignore symlinks flag.
130
     *
131
     * @param  boolean $bool Flag if symlinks should be ignored
132
     * @return void
133
     */
134
    public function setIgnoreLinks($bool)
135
    {
136
        $this->ignoreLinks = (bool) $bool;
137
    }
138
139
    /**
140
     * Add a comment to the zip archive.
141
     *
142
     * @param string $text
143
     *
144
     * @return void
145
     */
146
    public function setComment($text)
147
    {
148
        $this->comment = $text;
149
    }
150
151
    /**
152
     * do the work
153
     *
154
     * @throws BuildException
155
     */
156 4
    public function main()
157
    {
158 4
        if (!extension_loaded('zip')) {
159
            throw new BuildException("Zip extension is required");
160
        }
161
162 4
        if ($this->zipFile === null) {
163
            throw new BuildException("zipfile attribute must be set!", $this->getLocation());
164
        }
165
166 4
        if ($this->zipFile->exists() && $this->zipFile->isDirectory()) {
167
            throw new BuildException("zipfile is a directory!", $this->getLocation());
168
        }
169
170 4
        if ($this->zipFile->exists() && !$this->zipFile->canWrite()) {
171
            throw new BuildException("Can not write to the specified zipfile!", $this->getLocation());
172
        }
173
174
        try {
175 4
            if ($this->baseDir !== null) {
176 1
                if (!$this->baseDir->exists()) {
177
                    throw new BuildException(
178
                        "basedir '" . (string) $this->baseDir . "' does not exist!",
179
                        $this->getLocation()
180
                    );
181
                }
182
183 1
                if (empty($this->filesets)) {
184
                    // add the main fileset to the list of filesets to process.
185 1
                    $mainFileSet = new ZipFileSet($this->fileset);
186 1
                    $mainFileSet->setDir($this->baseDir);
187 1
                    $this->filesets[] = $mainFileSet;
188
                }
189
            }
190
191 4
            if (empty($this->filesets)) {
192
                throw new BuildException(
193
                    "You must supply either a basedir "
194
                    . "attribute or some nested filesets.",
195
                    $this->getLocation()
196
                );
197
            }
198
199
            // check if zip is out of date with respect to each
200
            // fileset
201 4
            if ($this->areFilesetsUpToDate()) {
202
                $this->log("Nothing to do: " . $this->zipFile->__toString() . " is up to date.", Project::MSG_INFO);
203
204
                return;
205
            }
206
207 4
            $this->log("Building zip: " . $this->zipFile->__toString(), Project::MSG_INFO);
208
209 4
            $zip = new ZipArchive();
210 4
            $res = $zip->open($this->zipFile->getAbsolutePath(), ZipArchive::CREATE);
211
212 4
            if ($res !== true) {
213
                throw new Exception("ZipArchive::open() failed with code " . $res);
214
            }
215
216 4
            if ($this->comment !== '') {
217
                $isCommented = $zip->setArchiveComment($this->comment);
218
                if ($isCommented === false) {
219
                    $this->log("Could not add a comment for the Archive.", Project::MSG_INFO);
220
                }
221
            }
222
223 4
            $this->addFilesetsToArchive($zip);
224
225 4
            $zip->close();
226
        } catch (IOException $ioe) {
227
            $msg = "Problem creating ZIP: " . $ioe->getMessage();
228
            throw new BuildException($msg, $ioe, $this->getLocation());
229
        }
230 4
    }
231
232
    /**
233
     * @param  array $files array of filenames
234
     * @param  PhingFile $dir
235
     * @return boolean
236
     */
237 4
    private function archiveIsUpToDate($files, $dir)
238
    {
239 4
        $sfs = new SourceFileScanner($this);
240 4
        $mm = new MergeMapper();
241 4
        $mm->setTo($this->zipFile->getAbsolutePath());
242
243 4
        return count($sfs->restrict($files, $dir, null, $mm)) == 0;
244
    }
245
246
    /**
247
     * @return array
248
     * @throws BuildException
249
     */
250 4
    public function areFilesetsUpToDate()
251
    {
252
        /**
253
         * @var FileSet $fs
254
         */
255 4
        foreach ($this->filesets as $fs) {
256 4
            $files = $fs->getIterator($this->includeEmpty);
257 4
            if (!$this->archiveIsUpToDate($files, $fs->getDir($this->project))) {
258 4
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
259
            }
260
            for ($i = 0, $fcount = count($files); $i < $fcount; $i++) {
261
                if ($this->zipFile->equals(new PhingFile($fs->getDir($this->project), $files[$i]))) {
262
                    throw new BuildException("A zip file cannot include itself", $this->getLocation());
263
                }
264
            }
265
        }
266
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type array.
Loading history...
267
    }
268
269
    /**
270
     * @param $zip
271
     */
272 4
    private function addFilesetsToArchive($zip)
273
    {
274 4
        foreach ($this->filesets as $fs) {
275 4
            $fsBasedir = (null != $this->baseDir) ? $this->baseDir :
276 4
                $fs->getDir($this->project);
277
278 4
            $files = $fs->getIterator($this->includeEmpty);
279
280 4
            foreach ($files as $file) {
281 4
                $f = new PhingFile($fsBasedir, $file);
282
283 4
                $pathInZip = $this->prefix
284 4
                    . $f->getPathWithoutBase($fsBasedir);
285
286 4
                $pathInZip = str_replace('\\', '/', $pathInZip);
287
288 4
                if ($this->ignoreLinks && $f->isLink()) {
289
                    continue;
290
                }
291
292 4
                if ($f->isDirectory()) {
293 4
                    if ($pathInZip != '.') {
294 4
                        $zip->addEmptyDir($pathInZip);
295
                    }
296
                } else {
297 4
                    $zip->addFile($f->getAbsolutePath(), $pathInZip);
298
                }
299 4
                $this->log("Adding " . $f->getPath() . " as " . $pathInZip . " to archive.", Project::MSG_VERBOSE);
300
            }
301
        }
302 4
    }
303
}
304