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

move-elevator / composer-translation-validator / 16137064113

08 Jul 2025 07:41AM UTC coverage: 78.302% (-16.2%) from 94.526%
16137064113

Pull #19

github

web-flow
Merge 5f8b3255c into 3e904fb25
Pull Request #19: refactor: improve output style

93 of 278 new or added lines in 7 files covered. (33.45%)

103 existing lines in 7 files now uncovered.

830 of 1060 relevant lines covered (78.3%)

4.18 hits per line

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

82.61
/src/Validator/DuplicateValuesValidator.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace MoveElevator\ComposerTranslationValidator\Validator;
6

7
use MoveElevator\ComposerTranslationValidator\Parser\ParserInterface;
8
use MoveElevator\ComposerTranslationValidator\Parser\XliffParser;
9
use MoveElevator\ComposerTranslationValidator\Parser\YamlParser;
10
use MoveElevator\ComposerTranslationValidator\Result\Issue;
11
use Symfony\Component\Console\Helper\Table;
12
use Symfony\Component\Console\Helper\TableSeparator;
13
use Symfony\Component\Console\Helper\TableStyle;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16

17
class DuplicateValuesValidator extends AbstractValidator implements ValidatorInterface
18
{
19
    /** @var array<string, array<string, array<int, string>>> */
20
    protected array $valuesArray = [];
21

22
    /**
23
     * @return array<string, int>
24
     */
25
    public function processFile(ParserInterface $file): array
3✔
26
    {
27
        $keys = $file->extractKeys();
3✔
28

29
        if (!$keys) {
3✔
30
            $this->logger?->error('The source file '.$file->getFileName().' is not valid.');
1✔
31

32
            return [];
1✔
33
        }
34

35
        foreach ($keys as $key) {
2✔
36
            $value = $file->getContentByKey($key);
2✔
37
            if (null === $value) {
2✔
UNCOV
38
                continue;
×
39
            }
40
            $this->valuesArray[$file->getFileName()][$value][] = $key;
2✔
41
        }
42

43
        return [];
2✔
44
    }
45

46
    public function postProcess(): void
2✔
47
    {
48
        foreach ($this->valuesArray as $file => $valueKeyArray) {
2✔
49
            $duplicates = [];
2✔
50
            foreach ($valueKeyArray as $value => $keys) {
2✔
51
                if (count(array_unique($keys)) > 1) {
2✔
52
                    $duplicates[$value] = array_unique($keys);
1✔
53
                }
54
            }
55
            if (!empty($duplicates)) {
2✔
56
                $this->addIssue(new Issue(
1✔
57
                    $file,
1✔
58
                    $duplicates,
1✔
59
                    '',
1✔
60
                    'DuplicateValuesValidator'
1✔
61
                ));
1✔
62
            }
63
        }
64
    }
65

66
    /**
67
     * @param array<string, array<int, array{file: string, issues: array<string, array<int, string>>}>> $issueSets
68
     */
69
    public function renderIssueSets(InputInterface $input, OutputInterface $output, array $issueSets): void
1✔
70
    {
71
        $rows = [];
1✔
72
        $currentFile = null;
1✔
73

74
        foreach ($issueSets as $issueSet) {
1✔
75
            foreach ($issueSet as $duplicate) {
1✔
76
                if ($currentFile !== $duplicate['file'] && null !== $currentFile) {
1✔
77
                    $rows[] = new TableSeparator();
1✔
78
                }
79
                $currentFile = $duplicate['file'];
1✔
80
                $fileName = $duplicate['file'];
1✔
81
                foreach ($duplicate['issues'] as $value => $keys) {
1✔
82
                    $rows[] = [
1✔
83
                        '<fg=red>'.$fileName.'</>',
1✔
84
                        implode("\n", $keys),
1✔
85
                        '<fg=yellow>'.$value.'</>',
1✔
86
                    ];
1✔
87
                    $fileName = '';
1✔
88
                }
89
            }
90
        }
91

92
        (new Table($output))
1✔
93
            ->setHeaders(['File', 'Key', 'Value'])
1✔
94
            ->setRows($rows)
1✔
95
            ->setStyle(
1✔
96
                (new TableStyle())
1✔
97
                    ->setCellHeaderFormat('%s')
1✔
98
            )
1✔
99
            ->render();
1✔
100
    }
101

102
    public function explain(): string
1✔
103
    {
104
        return 'This validator checks for duplicate values in translation files. '
1✔
105
            .'If a value is assigned to multiple keys in a file, it will be reported as an issue.';
1✔
106
    }
107

108
    /**
109
     * @return class-string<ParserInterface>[]
110
     */
111
    public function supportsParser(): array
1✔
112
    {
113
        return [XliffParser::class, YamlParser::class];
1✔
114
    }
115

116
    protected function resetState(): void
1✔
117
    {
118
        parent::resetState();
1✔
119
        $this->valuesArray = [];
1✔
120
    }
121

122
    public function resultTypeOnValidationFailure(): ResultType
1✔
123
    {
124
        return ResultType::WARNING;
1✔
125
    }
126

NEW
UNCOV
127
    public function formatIssueMessage(Issue $issue, string $prefix = '', bool $isVerbose = false): string
×
128
    {
NEW
UNCOV
129
        $details = $issue->getDetails();
×
NEW
UNCOV
130
        $resultType = $this->resultTypeOnValidationFailure();
×
131

NEW
UNCOV
132
        $level = $resultType->toString();
×
NEW
UNCOV
133
        $color = $resultType->toColorString();
×
134

NEW
UNCOV
135
        $messages = [];
×
NEW
UNCOV
136
        foreach ($details as $value => $keys) {
×
NEW
UNCOV
137
            if (is_string($value) && is_array($keys)) {
×
NEW
UNCOV
138
                $keyList = implode('`, `', $keys);
×
NEW
UNCOV
139
                $messages[] = "- <fg=$color>$level</> {$prefix}the translation value `$value` occurs in multiple keys (`$keyList`)";
×
140
            }
141
        }
142

NEW
UNCOV
143
        return implode("\n", $messages);
×
144
    }
145
}
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