PhpCpd::canExecuteOnStage()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Fabrica\Tools\Plugin;
4
5
use Fabrica\Tools\Builder;
6
use Fabrica\Models\Infra\Ci\Build;
7
use Fabrica\Models\Infra\Ci\BuildError;
8
use Fabrica\Tools\Plugin;
9
use Fabrica\Tools\ZeroConfigPluginInterface;
10
11
/**
12
 * PHP Copy / Paste Detector - Allows PHP Copy / Paste Detector testing.
13
 *
14
 * @author Ricardo Sierra <[email protected]>
15
 */
16
class PhpCpd extends Plugin implements ZeroConfigPluginInterface
17
{
18
    /**
19
     * @return string
20
     */
21
    public static function pluginName()
22
    {
23
        return 'php_cpd';
24
    }
25
26
    /**
27
     * {@inheritdoc}
28
     */
29
    public function __construct(Builder $builder, Build $build, array $options = [])
30
    {
31
        parent::__construct($builder, $build, $options);
32
33
        $this->executable = $this->findBinary('phpcpd');
34
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public static function canExecuteOnStage($stage, Build $build)
40
    {
41
        if (Build::STAGE_TEST === $stage) {
42
            return true;
43
        }
44
45
        return false;
46
    }
47
48
    /**
49
     * Runs PHP Copy/Paste Detector in a specified directory.
50
     */
51
    public function execute()
52
    {
53
        $ignore = '';
54
        if (is_array($this->ignore)) {
55
            foreach ($this->ignore as $item) {
56
                $item = rtrim($item, '/');
57
                if (is_file($this->builder->buildPath . $item)) {
58
                    $ignoredFile     = explode('/', $item);
59
                    $filesToIgnore[] = array_pop($ignoredFile);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$filesToIgnore was never initialized. Although not strictly required by PHP, it is generally a good practice to add $filesToIgnore = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
60
                } else {
61
                    $ignore .= sprintf(' --exclude="%s"', $item);
62
                }
63
            }
64
        }
65
66
        if (isset($filesToIgnore)) {
67
            $filesToIgnore = sprintf(' --names-exclude="%s"', implode(',', $filesToIgnore));
68
            $ignore        = $ignore . $filesToIgnore;
69
        }
70
71
        $phpcpd = $this->executable;
72
73
        $tmpFileName = tempnam(sys_get_temp_dir(), (self::pluginName() . '_'));
74
75
        $cmd     = 'cd "%s" && ' . $phpcpd . ' --log-pmd "%s" %s "%s"';
76
        $success = $this->builder->executeCommand($cmd, $this->builder->buildPath, $tmpFileName, $ignore, $this->directory);
77
78
        $errorCount = $this->processReport(file_get_contents($tmpFileName));
79
80
        $this->build->storeMeta((self::pluginName() . '-warnings'), $errorCount);
81
82
        unlink($tmpFileName);
83
84
        return $success;
85
    }
86
87
    /**
88
     * Process the PHPCPD XML report.
89
     *
90
     * @param $xmlString
91
     *
92
     * @return int
93
     *
94
     * @throws \Exception
95
     */
96
    protected function processReport($xmlString)
97
    {
98
        $xml = simplexml_load_string($xmlString);
99
100
        if (false === $xml) {
101
            $this->builder->log($xmlString);
102
            throw new \Exception('Could not process the report generated by PHPCpd.');
103
        }
104
105
        $warnings = 0;
106
        foreach ($xml->duplication as $duplication) {
107
            foreach ($duplication->file as $file) {
108
                $fileName = (string) $file['path'];
109
                $fileName = str_replace($this->builder->buildPath, '', $fileName);
110
111
                $message = <<<CPD
112
Copy and paste detected:
113
114
```
115
{$duplication->codefragment}
116
```
117
CPD;
118
119
                $this->build->reportError(
120
                    $this->builder,
121
                    self::pluginName(),
122
                    $message,
123
                    BuildError::SEVERITY_NORMAL,
124
                    $fileName,
125
                    (int) $file['line'],
126
                    (int) $file['line'] + (int) $duplication['lines']
127
                );
128
            }
129
130
            $warnings++;
131
        }
132
133
        return $warnings;
134
    }
135
}
136