• 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

94.74
/src/Detector/Strategy/SuffixTreeStrategy.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Systemsdk\PhpCPD\Detector\Strategy;
6

7
use Systemsdk\PhpCPD\CodeClone;
8
use Systemsdk\PhpCPD\CodeCloneFile;
9
use Systemsdk\PhpCPD\CodeCloneMap;
10
use Systemsdk\PhpCPD\Detector\Strategy\SuffixTree\AbstractToken;
11
use Systemsdk\PhpCPD\Detector\Strategy\SuffixTree\ApproximateCloneDetectingSuffixTree;
12
use Systemsdk\PhpCPD\Detector\Strategy\SuffixTree\Sentinel;
13
use Systemsdk\PhpCPD\Detector\Strategy\SuffixTree\Token;
14
use Systemsdk\PhpCPD\Exceptions\MissingResultException;
15

16
use function array_keys;
17
use function file_get_contents;
18
use function is_array;
19
use function token_get_all;
20

21
/**
22
 * The suffix tree strategy was implemented in PHP for PHPCPD by Olle Härstedt.
23
 *
24
 * This PHP implementation is based on the Java implementation archived that is
25
 * available at https://www.cqse.eu/en/news/blog/conqat-end-of-life/ under the
26
 * Apache License 2.0.
27
 *
28
 * The aforementioned Java implementation is based on the algorithm described in
29
 * https://dl.acm.org/doi/10.1109/ICSE.2009.5070547. This paper is available at
30
 * https://www.cqse.eu/fileadmin/content/news/publications/2009-do-code-clones-matter.pdf.
31
 */
32
final class SuffixTreeStrategy extends AbstractStrategy
33
{
34
    /**
35
     * @psalm-var list<AbstractToken>
36
     */
37
    private array $word = [];
38

39
    private ?CodeCloneMap $result = null;
40

41
    public function processFile(string $file, CodeCloneMap $result): void
42
    {
43
        $content = (string)file_get_contents($file);
1✔
44
        $tokens = token_get_all($content);
1✔
45

46
        foreach (array_keys($tokens) as $key) {
1✔
47
            $token = $tokens[$key];
1✔
48

49
            if (is_array($token) && !isset($this->tokensIgnoreList[$token[0]])) {
1✔
50
                $this->word[] = new Token(
1✔
51
                    $token[0],
1✔
52
                    token_name($token[0]),
1✔
53
                    $token[2],
1✔
54
                    $file,
1✔
55
                    $token[1]
1✔
56
                );
1✔
57
            }
58
        }
59

60
        $this->result = $result;
1✔
61
    }
62

63
    /**
64
     * @throws MissingResultException
65
     */
66
    public function postProcess(): void
67
    {
68
        if (empty($this->result)) {
1✔
UNCOV
69
            throw new MissingResultException('Missing result');
×
70
        }
71

72
        // Sentinel = End of word
73
        $this->word[] = new Sentinel();
1✔
74

75
        $cloneInfos = (new ApproximateCloneDetectingSuffixTree($this->word))->findClones(
1✔
76
            $this->config->minTokens(),
1✔
77
            $this->config->editDistance(),
1✔
78
            $this->config->headEquality()
1✔
79
        );
1✔
80

81
        foreach ($cloneInfos as $cloneInfo) {
1✔
82
            /** @var int[] */
83
            $others = $cloneInfo->otherClones->extractFirstList();
1✔
84

85
            for ($j = 0, $count = count($others); $j < $count; $j++) {
1✔
86
                $otherStart = $others[$j];
1✔
87
                $t = $this->word[$otherStart];
1✔
88
                $lastToken = $this->word[$cloneInfo->position + $cloneInfo->length];
1✔
89
                // If we stumbled upon the Sentinel, rewind one step.
90
                if ($lastToken instanceof Sentinel) {
1✔
UNCOV
91
                    $lastToken = $this->word[$cloneInfo->position + $cloneInfo->length - 2];
×
92
                }
93
                $lines = $lastToken->line - $cloneInfo->token->line;
1✔
94
                $this->result->add(
1✔
95
                    new CodeClone(
1✔
96
                        new CodeCloneFile($cloneInfo->token->file, $cloneInfo->token->line),
1✔
97
                        new CodeCloneFile($t->file, $t->line),
1✔
98
                        $lines,
1✔
99
                        // TODO: Double check this
100
                        $otherStart + 1 - $cloneInfo->position
1✔
101
                    )
1✔
102
                );
1✔
103
            }
104
        }
105
    }
106
}
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