DeleteTask   F
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 68
eloc 140
c 2
b 1
f 0
dl 0
loc 302
rs 2.96

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setIncludeEmptyDirs() 0 3 1
A setFile() 0 3 1
A setVerbose() 0 6 2
A setFailOnError() 0 3 1
F main() 0 112 34
A setQuiet() 0 5 2
A setDir() 0 3 1
C removeFiles() 0 44 16
B removeDir() 0 36 10

How to fix   Complexity   

Complex Class

Complex classes like DeleteTask often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DeleteTask, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\System;
22
23
use Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Io\File;
26
use Phing\Project;
27
use Phing\Task;
28
use Phing\Type\Element\ResourceAware;
29
30
/**
31
 * Deletes a file or directory, or set of files defined by a fileset.
32
 */
33
class DeleteTask extends Task
34
{
35
    use ResourceAware;
36
37
    protected $file;
38
    protected $dir;
39
    protected $includeEmpty = false;
40
41
    protected $quiet = false;
42
    protected $failonerror = false;
43
    protected $verbosity = Project::MSG_VERBOSE;
44
45
    /**
46
     * Set the name of a single file to be removed.
47
     */
48
    public function setFile(File $file)
49
    {
50
        $this->file = $file;
51
    }
52
53
    /**
54
     * Set the directory from which files are to be deleted.
55
     */
56
    public function setDir(File $dir)
57
    {
58
        $this->dir = $dir;
59
    }
60
61
    /**
62
     * Used to force listing of all names of deleted files.
63
     *
64
     * @param bool $verbosity
65
     */
66
    public function setVerbose($verbosity)
67
    {
68
        if ($verbosity) {
69
            $this->verbosity = Project::MSG_INFO;
70
        } else {
71
            $this->verbosity = Project::MSG_VERBOSE;
72
        }
73
    }
74
75
    /**
76
     * If the file does not exist, do not display a diagnostic
77
     * message or modify the exit status to reflect an error.
78
     * This means that if a file or directory cannot be deleted,
79
     * then no error is reported. This setting emulates the
80
     * -f option to the Unix rm command. Default is false
81
     * meaning things are verbose.
82
     *
83
     * @param bool $bool
84
     */
85
    public function setQuiet($bool)
86
    {
87
        $this->quiet = $bool;
88
        if ($this->quiet) {
89
            $this->failonerror = false;
90
        }
91
    }
92
93
    /**
94
     * this flag means 'note errors to the output, but keep going'.
95
     *
96
     * @param bool $bool
97
     * @retujrn void
98
     */
99
    public function setFailOnError($bool)
100
    {
101
        $this->failonerror = $bool;
102
    }
103
104
    /**
105
     * Used to delete empty directories.
106
     *
107
     * @param bool $includeEmpty
108
     */
109
    public function setIncludeEmptyDirs($includeEmpty)
110
    {
111
        $this->includeEmpty = (bool) $includeEmpty;
112
    }
113
114
    /**
115
     * Delete the file(s).
116
     *
117
     * @throws BuildException
118
     */
119
    public function main()
120
    {
121
        if (
122
            null === $this->file
123
            && null === $this->dir
124
            && 0 === count($this->dirsets)
125
            && 0 === count($this->filesets)
126
            && 0 === count($this->filelists)
127
        ) {
128
            throw new BuildException(
129
                'At least one of the file or dir attributes, or a fileset, filelist or dirset element must be set.'
130
            );
131
        }
132
133
        if ($this->quiet && $this->failonerror) {
134
            throw new BuildException('quiet and failonerror cannot both be set to true', $this->getLocation());
135
        }
136
137
        // delete a single file
138
        if (null !== $this->file) {
139
            if ($this->file->exists()) {
140
                if ($this->file->isDirectory()) {
141
                    $this->log(
142
                        'Directory ' . $this->file->__toString() . ' cannot be removed using the file attribute. Use dir instead.'
143
                    );
144
                } else {
145
                    $this->log('Deleting: ' . $this->file->__toString());
146
147
                    try {
148
                        $this->file->delete();
149
                    } catch (Exception $e) {
150
                        $message = 'Unable to delete file ' . $this->file->__toString() . ': ' . $e->getMessage();
151
                        if ($this->failonerror) {
152
                            throw new BuildException($message);
153
                        }
154
155
                        $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
156
                    }
157
                }
158
            } else {
159
                $message = 'Could not find file ' . $this->file->getAbsolutePath() . ' to delete.';
160
161
                if ($this->failonerror) {
162
                    throw new BuildException($message);
163
                }
164
165
                $this->log($message, ($this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN));
166
            }
167
        }
168
169
        if (null !== $this->dir) {
170
            $this->dirsets[] = $this->dir;
171
        }
172
        foreach ($this->dirsets as $dirset) {
173
            if (!$dirset instanceof File) {
174
                $ds = $dirset->getDirectoryScanner($this->getProject());
175
                $dirs = $ds->getIncludedDirectories();
176
                $baseDir = $ds->getBasedir();
177
            } else {
178
                $dirs[0] = $dirset;
179
            }
180
            foreach ($dirs as $dir) {
181
                if (!$dir instanceof File) {
182
                    $dir = new File($baseDir, $dir);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $baseDir does not seem to be defined for all execution paths leading up to this point.
Loading history...
183
                }
184
                if ($dir->exists() && $dir->isDirectory()) {
185
                    if (Project::MSG_VERBOSE === $this->verbosity) {
186
                        $this->log('Deleting directory ' . $dir->__toString());
187
                    }
188
                    $this->removeDir($dir);
189
                } else {
190
                    $message = 'Directory ' . $dir->getAbsolutePath() . ' does not exist or is not a directory.';
191
192
                    if ($this->failonerror) {
193
                        throw new BuildException($message);
194
                    }
195
196
                    $this->log($message, ($this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN));
197
                }
198
            }
199
        }
200
201
        // delete the files in the filelists
202
        foreach ($this->filelists as $fl) {
203
            try {
204
                $files = $fl->getFiles($this->project);
205
                $empty = [];
206
                $this->removeFiles($fl->getDir($this->project), $files, $empty);
207
            } catch (BuildException $be) {
208
                // directory doesn't exist or is not readable
209
                if ($this->failonerror) {
210
                    throw $be;
211
                }
212
213
                $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
214
            }
215
        }
216
217
        // delete the files in the filesets
218
        foreach ($this->filesets as $fs) {
219
            try {
220
                $ds = $fs->getDirectoryScanner($this->project);
221
                $files = $ds->getIncludedFiles();
222
                $dirs = $ds->getIncludedDirectories();
223
                $this->removeFiles($fs->getDir($this->project), $files, $dirs);
224
            } catch (BuildException $be) {
225
                // directory doesn't exist or is not readable
226
                if ($this->failonerror) {
227
                    throw $be;
228
                }
229
230
                $this->log($be->getMessage(), $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
231
            }
232
        }
233
    }
234
235
    /**
236
     * Recursively removes a directory.
237
     *
238
     * @param File $d the directory to remove
239
     *
240
     * @throws BuildException
241
     */
242
    private function removeDir($d)
243
    {
244
        $list = $d->listDir();
245
        if (null === $list) {
246
            $list = [];
247
        }
248
249
        foreach ($list as $s) {
250
            $f = new File($d, $s);
251
            if ($f->isDirectory()) {
252
                $this->removeDir($f);
253
            } else {
254
                $this->log('Deleting ' . $f->__toString(), $this->verbosity);
255
256
                try {
257
                    $f->delete();
258
                } catch (Exception $e) {
259
                    $message = 'Unable to delete file ' . $f->__toString() . ': ' . $e->getMessage();
260
                    if ($this->failonerror) {
261
                        throw new BuildException($message);
262
                    }
263
264
                    $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
265
                }
266
            }
267
        }
268
269
        try {
270
            $d->delete();
271
        } catch (Exception $e) {
272
            $message = 'Unable to delete directory ' . $d->__toString() . ': ' . $e->getMessage();
273
            if ($this->failonerror) {
274
                throw new BuildException($message);
275
            }
276
277
            $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
278
        }
279
    }
280
281
    /**
282
     * remove an array of files in a directory, and a list of subdirectories
283
     * which will only be deleted if 'includeEmpty' is true.
284
     *
285
     * @param File  $d     directory to work from
286
     * @param array $files array of files to delete; can be of zero length
287
     * @param array $dirs  array of directories to delete; can of zero length
288
     *
289
     * @throws BuildException
290
     */
291
    private function removeFiles(File $d, &$files, &$dirs)
292
    {
293
        if (count($files) > 0) {
294
            $this->log('Deleting ' . count($files) . ' files from ' . $d->__toString());
295
            for ($j = 0, $_j = count($files); $j < $_j; ++$j) {
296
                $f = new File($d, $files[$j]);
297
                $this->log('Deleting ' . $f->getAbsolutePath(), $this->verbosity);
298
299
                try {
300
                    $f->delete();
301
                } catch (Exception $e) {
302
                    $message = 'Unable to delete file ' . $f->__toString() . ': ' . $e->getMessage();
303
                    if ($this->failonerror) {
304
                        throw new BuildException($message);
305
                    }
306
307
                    $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
308
                }
309
            }
310
        }
311
312
        if (count($dirs) > 0 && $this->includeEmpty) {
313
            $dirCount = 0;
314
            for ($j = count($dirs) - 1; $j >= 0; --$j) {
315
                $dir = new File($d, $dirs[$j]);
316
                $dirFiles = $dir->listDir();
317
                if (null === $dirFiles || 0 === count($dirFiles)) {
318
                    $this->log('Deleting ' . $dir->__toString(), $this->verbosity);
319
320
                    try {
321
                        $dir->delete();
322
                        ++$dirCount;
323
                    } catch (Exception $e) {
324
                        $message = 'Unable to delete directory ' . $dir->__toString();
325
                        if ($this->failonerror) {
326
                            throw new BuildException($message);
327
                        }
328
329
                        $this->log($message, $this->quiet ? Project::MSG_VERBOSE : Project::MSG_WARN);
330
                    }
331
                }
332
            }
333
            if ($dirCount > 0) {
334
                $this->log("Deleted {$dirCount} director" . (1 == $dirCount ? 'y' : 'ies') . ' from ' . $d->__toString());
0 ignored issues
show
introduced by
The condition 1 == $dirCount is always false.
Loading history...
335
            }
336
        }
337
    }
338
}
339