• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemsdk / phpcpd / #4

30 Dec 2024 04:42PM UTC coverage: 75.789%. Remained the same
#4

push

DKravtsov
phpcpd 8.0.0 release. Made codebase refactoring. Updated requirements php 8.3, updated composer dependencies, updated tests to the PHPUnit 11. Updated dev environment to the php 8.4, Phing 3.0, added code quality tools: ecs, phpstan.

271 of 379 new or added lines in 20 files covered. (71.5%)

65 existing lines in 13 files now uncovered.

648 of 855 relevant lines covered (75.79%)

2.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Cli/Application.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Systemsdk\PhpCPD\Cli;
6

7
use SebastianBergmann\FileIterator\Facade;
8
use SebastianBergmann\Timer\ResourceUsageFormatter;
9
use SebastianBergmann\Timer\Timer;
10
use SebastianBergmann\Version;
11
use Systemsdk\PhpCPD\Detector\Detector;
12
use Systemsdk\PhpCPD\Detector\Strategy\AbstractStrategy;
13
use Systemsdk\PhpCPD\Detector\Strategy\DefaultStrategy;
14
use Systemsdk\PhpCPD\Detector\Strategy\StrategyConfiguration;
15
use Systemsdk\PhpCPD\Detector\Strategy\SuffixTreeStrategy;
16
use Systemsdk\PhpCPD\Exceptions\Exception;
17
use Systemsdk\PhpCPD\Exceptions\InvalidStrategyException;
18
use Systemsdk\PhpCPD\Exceptions\LoggerException;
19
use Systemsdk\PhpCPD\Exceptions\ProcessingResultException;
20
use Systemsdk\PhpCPD\Log\PMD;
21
use Systemsdk\PhpCPD\Log\Text;
22

23
use function count;
24
use function dirname;
25
use function printf;
26

27
use const PHP_EOL;
28

29
final class Application
30
{
31
    public const string VERSION = '8.0.0';
32

33
    /**
34
     * @param array<int, string> $argv
35
     */
36
    public function run(array $argv): int
37
    {
NEW
38
        $this->printVersion();
×
39

40
        try {
NEW
41
            $arguments = (new ArgumentsBuilder())->build($argv);
×
NEW
42
        } catch (Exception $exception) {
×
NEW
43
            print PHP_EOL . $exception->getMessage() . PHP_EOL;
×
44

NEW
45
            return 1;
×
46
        }
47

NEW
48
        print PHP_EOL;
×
49

NEW
50
        if ($arguments->version()) {
×
NEW
51
            return 0;
×
52
        }
53

NEW
54
        if ($arguments->help()) {
×
NEW
55
            $this->help();
×
56

NEW
57
            return 0;
×
58
        }
59

60
        /** @var list<non-empty-string> $paths */
NEW
61
        $paths = $arguments->directories();
×
62
        /** @var list<non-empty-string> $suffixes */
NEW
63
        $suffixes = $arguments->suffixes();
×
64
        /** @var list<non-empty-string> $exclude */
NEW
65
        $exclude = $arguments->exclude();
×
NEW
66
        $files = (new Facade())->getFilesAsArray(
×
NEW
67
            $paths,
×
NEW
68
            $suffixes,
×
NEW
69
            '',
×
NEW
70
            $exclude
×
NEW
71
        );
×
72

NEW
73
        if (empty($files)) {
×
NEW
74
            print 'No files found to scan' . PHP_EOL;
×
75

NEW
76
            return 1;
×
77
        }
78

79
        try {
NEW
80
            $strategy = $this->pickStrategy($arguments->algorithm(), new StrategyConfiguration($arguments));
×
NEW
81
        } catch (InvalidStrategyException $exception) {
×
NEW
82
            print $exception->getMessage() . PHP_EOL;
×
83

NEW
84
            return 1;
×
85
        }
86

NEW
87
        $timer = new Timer();
×
NEW
88
        $timer->start();
×
89

90
        try {
NEW
91
            $clones = (new Detector($strategy))->copyPasteDetection($files);
×
NEW
92
        } catch (ProcessingResultException $exception) {
×
NEW
93
            print 'Processing error: ' . $exception->getMessage() . PHP_EOL;
×
94

NEW
95
            return 1;
×
96
        }
97

NEW
98
        (new Text())->printResult($clones, $arguments->verbose());
×
99

NEW
100
        if ($arguments->pmdCpdXmlLogfile()) {
×
101
            try {
NEW
102
                (new PMD($arguments->pmdCpdXmlLogfile()))->processClones($clones);
×
NEW
103
            } catch (LoggerException $exception) {
×
NEW
104
                print 'Logger error: ' . $exception->getMessage() . PHP_EOL;
×
105

NEW
106
                return 1;
×
107
            }
108
        }
109

NEW
110
        print (new ResourceUsageFormatter())->resourceUsage($timer->stop()) . PHP_EOL;
×
111

NEW
112
        return count($clones) > 0 ? 1 : 0;
×
113
    }
114

115
    private function printVersion(): void
116
    {
117
        /** @var non-empty-string $path */
NEW
118
        $path = dirname(__DIR__);
×
NEW
119
        printf('%s', (new Version(self::VERSION, $path))->asString());
×
120
    }
121

122
    /**
123
     * @throws InvalidStrategyException
124
     */
125
    private function pickStrategy(string $algorithm, StrategyConfiguration $config): AbstractStrategy
126
    {
NEW
127
        return match ($algorithm) {
×
NEW
128
            ArgumentsBuilder::ALGORITHM_RABIN_KARP_NAME => new DefaultStrategy($config),
×
NEW
129
            ArgumentsBuilder::ALGORITHM_SUFFIX_TREE_NAME => new SuffixTreeStrategy($config),
×
NEW
130
            default => throw new InvalidStrategyException('Unsupported algorithm: ' . $algorithm),
×
NEW
131
        };
×
132
    }
133

134
    private function help(): void
135
    {
NEW
136
        print <<<'EOT'
×
137
Usage:
138
  phpcpd [options] <directory>
139

140
Options for selecting files:
141

142
  --suffix <suffix> Include files with names ending on <suffix> (default: .php; can be given multiple times)
143
  --exclude <path>  Exclude files with <path> in their path (can be given multiple times)
144

145
Options for analysing files:
146

147
  --fuzzy           Fuzz variable names
148
  --min-lines <N>   Minimum number of identical lines (default: 5)
149
  --min-tokens <N>  Minimum number of identical tokens (default: 70)
150
  --verbose         Print progress bar
151

152
Options for report generation:
153

154
  --log-pmd <file>  Write log in PMD-CPD XML format to <file>
155

156
General options:
157

158
  --version         Display version
159
  --help            Display help
160

NEW
161
EOT;
×
162
        /**
163
         * TODO: check it
164
         * --algorithm <name>  Select which algorithm to use ('rabin-karp' (default) or 'suffixtree')
165
         * --edit-distance <N> Distance in number of edits between two clones (only for suffixtree; default: 5)
166
         * --head-equality <N> Minimum equality at start of clone (only for suffixtree; default 10)
167
         */
168
    }
169
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc