Passed
Push — master ( d62b89...abb8c8 )
by Michiel
06:34
created

PHPCPDTask::loadDependencies()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 11.643

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 31
ccs 5
cts 14
cp 0.357
rs 9.3222
c 0
b 0
f 0
cc 5
nc 5
nop 0
crap 11.643
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
use Phing\Exception\BuildException;
21
use Phing\Io\File;
22
use Phing\Task;
23
use Phing\Type\Element\FileSetAware;
24
use SebastianBergmann\PHPCPD\Detector\Detector;
25
use SebastianBergmann\PHPCPD\Detector\Strategy\DefaultStrategy;
26
use Composer\Autoload\ClassLoader;
27
28
/**
29
 * Runs PHP Copy & Paste Detector. Checking PHP files for duplicated code.
30
 * Refactored original PhpCpdTask provided by
31
 * Timo Haberkern <[email protected]>
32
 *
33
 * @package phing.tasks.ext.phpcpd
34
 * @author  Benjamin Schultz <[email protected]>
35
 */
36
class PHPCPDTask extends Task
37
{
38
    use FileSetAware;
39
40
    /**
41
     * A php source code filename or directory
42
     *
43
     * @var File
44
     */
45
    protected $file = null;
46
47
    /**
48
     * Minimum number of identical lines.
49
     *
50
     * @var integer
51
     */
52
    protected $minLines = 5;
53
54
    /**
55
     * Minimum number of identical tokens.
56
     *
57
     * @var integer
58
     */
59
    protected $minTokens = 70;
60
61
    /**
62
     * Allow for fuzzy matches.
63
     *
64
     * @var boolean
65
     */
66
    protected $fuzzy = false;
67
68
    /**
69
     * List of valid file extensions for analyzed files.
70
     *
71
     * @var array
72
     */
73
    protected $allowedFileExtensions = ['php'];
74
75
    /**
76
     * List of exclude directory patterns.
77
     *
78
     * @var array
79
     */
80
    protected $ignorePatterns = ['.git', '.svn', 'CVS', '.bzr', '.hg'];
81
82
    /**
83
     * The format for the report
84
     *
85
     * @var string
86
     */
87
    protected $format = 'default';
88
89
    /**
90
     * Formatter elements.
91
     *
92
     * @var PHPCPDFormatterElement[]
93
     */
94
    protected $formatters = [];
95
96
    /**
97
     * @var string
98
     */
99
    private $pharLocation = "";
100
101
    /**
102
     * Set the input source file or directory.
103
     *
104
     * @param File $file The input source file or directory.
105
     */
106
    public function setFile(File $file)
107
    {
108
        $this->file = $file;
109
    }
110
111
    /**
112
     * Sets the minimum number of identical lines (default: 5).
113
     *
114
     * @param integer $minLines Minimum number of identical lines
115
     */
116
    public function setMinLines($minLines)
117
    {
118
        $this->minLines = $minLines;
119
    }
120
121
    /**
122
     * Sets the minimum number of identical tokens (default: 70).
123
     *
124
     * @param integer $minTokens Minimum number of identical tokens
125
     */
126
    public function setMinTokens($minTokens)
127
    {
128
        $this->minTokens = $minTokens;
129
    }
130
131
    /**
132
     * Sets the fuzzy match (default: false).
133
     *
134
     * @param boolean $fuzzy fuzzy match
135
     */
136
    public function setFuzzy($fuzzy)
137
    {
138
        $this->fuzzy = $fuzzy;
139
    }
140
141
    /**
142
     * Sets a list of filename extensions for valid php source code files.
143
     *
144
     * @param string $fileExtensions List of valid file extensions.
145
     */
146
    public function setAllowedFileExtensions($fileExtensions)
147
    {
148
        $this->allowedFileExtensions = [];
149
150
        $token = ' ,;';
151
        $ext = strtok($fileExtensions, $token);
152
153
        while ($ext !== false) {
154
            $this->allowedFileExtensions[] = $ext;
155
            $ext = strtok($token);
156
        }
157
    }
158
159
    /**
160
     * Sets a list of ignore patterns that is used to exclude directories from the source analysis.
161
     *
162
     * @param string $ignorePatterns List of ignore patterns.
163
     */
164
    public function setIgnorePatterns($ignorePatterns)
165
    {
166
        $this->ignorePatterns = [];
167
168
        $token = ' ,;';
169
        $pattern = strtok($ignorePatterns, $token);
170
171
        while ($pattern !== false) {
172
            $this->ignorePatterns[] = $pattern;
173
            $pattern = strtok($token);
174
        }
175
    }
176
177
    /**
178
     * Sets the output format
179
     *
180
     * @param string $format Format of the report
181
     */
182
    public function setFormat($format)
183
    {
184
        $this->format = $format;
185
    }
186
187
    /**
188
     * Create object for nested formatter element.
189
     *
190
     * @return PHPCPDFormatterElement
191
     */
192 3
    public function createFormatter()
193
    {
194 3
        $num = array_push($this->formatters, new PHPCPDFormatterElement($this));
195
196 3
        return $this->formatters[$num - 1];
197
    }
198
199
    /**
200
     * @param string $pharLocation
201
     */
202
    public function setPharLocation($pharLocation)
203
    {
204
        $this->pharLocation = $pharLocation;
205
    }
206
207
    /**
208
     * @throws BuildException if the phpcpd classes can't be loaded
209
     */
210 3
    private function loadDependencies()
211
    {
212 3
        if (!empty($this->pharLocation)) {
213
            // hack to prevent PHPCPD from starting in CLI mode and halting Phing
214
            eval(
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
215
                "namespace SebastianBergmann\PHPCPD\CLI;
216
class Application
217
{
218
    public function run() {}
219
}"
220
            );
221
222
            ob_start();
223
            include $this->pharLocation;
224
            ob_end_clean();
225
226
            if (class_exists('\\SebastianBergmann\\PHPCPD\\Detector\\Strategy\\DefaultStrategy')) {
227
                return;
228
            }
229
        }
230
231
        if (
232 3
            class_exists(ClassLoader::class, false) &&
233 3
            class_exists(DefaultStrategy::class)
234
        ) {
235 3
            return;
236
        }
237
238
        throw new BuildException(
239
            'PHPCPDTask depends on PHPCPD being installed and on include_path.',
240
            $this->getLocation()
241
        );
242
    }
243
244
    /**
245
     * Executes PHPCPD against PhingFile or a FileSet
246
     *
247
     * @throws BuildException
248
     */
249 3
    public function main()
250
    {
251 3
        $this->loadDependencies();
252
253 3
        if (!isset($this->file) && count($this->filesets) == 0) {
254
            throw new BuildException('Missing either a nested fileset or attribute "file" set');
255
        }
256
257 3
        if (count($this->formatters) == 0) {
258
            // turn legacy format attribute into formatter
259
            $fmt = new PHPCPDFormatterElement($this);
260
            $fmt->setType($this->format);
261
            $fmt->setUseFile(false);
262
263
            $this->formatters[] = $fmt;
264
        }
265
266 3
        $this->validateFormatters();
267
268 3
        $filesToParse = [];
269
270 3
        if ($this->file instanceof File) {
0 ignored issues
show
introduced by
$this->file is always a sub-type of Phing\Io\File.
Loading history...
271
            $filesToParse[] = $this->file->getPath();
272
        } else {
273
            // append any files in filesets
274 3
            foreach ($this->filesets as $fs) {
275 3
                $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
276
277 3
                foreach ($files as $filename) {
278 3
                    $f = new File($fs->getDir($this->project), $filename);
279 3
                    $filesToParse[] = $f->getAbsolutePath();
280
                }
281
            }
282
        }
283
284 3
        $this->log('Processing files...');
285
286 3
        $detector = new Detector(new DefaultStrategy());
287 3
        $clones = $detector->copyPasteDetection(
288 3
            $filesToParse,
289 3
            $this->minLines,
290 3
            $this->minTokens,
291 3
            $this->fuzzy
292
        );
293
294 3
        $this->log('Finished copy/paste detection');
295
296 3
        foreach ($this->formatters as $fe) {
297 3
            $formatter = $fe->getFormatter();
298 3
            $formatter->processClones(
299 3
                $clones,
300 3
                $this->project,
301 3
                $fe->getUseFile(),
302 3
                $fe->getOutfile()
303
            );
304
        }
305 3
    }
306
307
    /**
308
     * Validates the available formatters
309
     *
310
     * @throws BuildException
311
     */
312 3
    protected function validateFormatters()
313
    {
314 3
        foreach ($this->formatters as $fe) {
315 3
            if ($fe->getType() == '') {
316
                throw new BuildException('Formatter missing required "type" attribute.');
317
            }
318
319 3
            if ($fe->getUsefile() && $fe->getOutfile() === null) {
320
                throw new BuildException('Formatter requires "outfile" attribute when "useFile" is true.');
321
            }
322
        }
323 3
    }
324
}
325