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

move-elevator / composer-translation-validator / 24129989898

08 Apr 2026 10:10AM UTC coverage: 95.38% (-0.1%) from 95.491%
24129989898

Pull #112

github

konradmichalik
fix: resolve PHPStan nullable ParserCache errors
Pull Request #112: refactor: replace static ParserCache with constructor injection

17 of 20 new or added lines in 3 files covered. (85.0%)

16 existing lines in 2 files now uncovered.

2374 of 2489 relevant lines covered (95.38%)

8.12 hits per line

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

95.0
/src/Result/ValidationRun.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the "composer-translation-validator" Composer plugin.
7
 *
8
 * (c) 2025-2026 Konrad Michalik <km@move-elevator.de>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13

14
namespace MoveElevator\ComposerTranslationValidator\Result;
15

16
use MoveElevator\ComposerTranslationValidator\Config\TranslationValidatorConfig;
17
use MoveElevator\ComposerTranslationValidator\FileDetector\FileSet;
18
use MoveElevator\ComposerTranslationValidator\Parser\ParserCache;
19
use MoveElevator\ComposerTranslationValidator\Validator\{ResultType, ValidatorInterface};
20
use Psr\Log\LoggerInterface;
21
use Throwable;
22

23
use function count;
24
use function is_array;
25

26
/**
27
 * ValidationRun.
28
 *
29
 * @author Konrad Michalik <km@move-elevator.de>
30
 * @license GPL-3.0-or-later
31
 */
32
class ValidationRun
33
{
34
    private readonly ParserCache $parserCache;
35

36
    public function __construct(
8✔
37
        private readonly LoggerInterface $logger,
38
        ?ParserCache $parserCache = null,
39
    ) {
40
        $this->parserCache = $parserCache ?? new ParserCache();
8✔
41
    }
42

43
    /**
44
     * @param array<FileSet>                          $fileSets
45
     * @param array<class-string<ValidatorInterface>> $validatorClasses
46
     */
47
    public function executeFor(array $fileSets, array $validatorClasses, ?TranslationValidatorConfig $config = null): ValidationResult
8✔
48
    {
49
        $startTime = microtime(true);
8✔
50
        $validatorInstances = [];
8✔
51
        $validatorFileSetPairs = [];
8✔
52
        $overallResult = ResultType::SUCCESS;
8✔
53
        $filesChecked = 0;
8✔
54

55
        foreach ($fileSets as $fileSet) {
8✔
56
            $filesChecked += count($fileSet->getFiles());
6✔
57
            foreach ($validatorClasses as $validatorClass) {
6✔
58
                // Create a new validator instance for each FileSet to ensure isolation
59
                $validatorInstance = new $validatorClass($this->logger);
5✔
60

61
                // Share the ParserCache instance across all validators
62
                if (method_exists($validatorInstance, 'setParserCache')) {
5✔
NEW
UNCOV
63
                    $validatorInstance->setParserCache($this->parserCache);
×
64
                }
65

66
                // Pass config to validator if it supports it
67
                if (null !== $config && method_exists($validatorInstance, 'setConfig')) {
5✔
UNCOV
68
                    $validatorInstance->setConfig($config);
×
69
                }
70

71
                /** @var class-string<\MoveElevator\ComposerTranslationValidator\Parser\ParserInterface> $parserClass */
72
                $parserClass = $fileSet->getParser();
5✔
73
                $result = $validatorInstance->validate($fileSet->getFiles(), $parserClass);
5✔
74
                if (!empty($result)) {
5✔
75
                    $overallResult = $overallResult->max($validatorInstance->resultTypeOnValidationFailure());
4✔
76
                    $validatorInstances[] = $validatorInstance;
4✔
77
                    $validatorFileSetPairs[] = [
4✔
78
                        'validator' => $validatorInstance,
4✔
79
                        'fileSet' => $fileSet,
4✔
80
                    ];
4✔
81
                }
82
            }
83
        }
84

85
        $keysChecked = $this->countKeysChecked($fileSets);
8✔
86

87
        $validatorsRun = count($validatorClasses);
8✔
88

89
        // Get cache statistics before clearing cache
90
        $cacheStats = $this->parserCache->getCacheStats();
8✔
91
        $parsersCached = $cacheStats['cached_parsers'];
8✔
92

93
        $executionTime = microtime(true) - $startTime;
8✔
94
        $statistics = new ValidationStatistics(
8✔
95
            $executionTime,
8✔
96
            $filesChecked,
8✔
97
            $keysChecked,
8✔
98
            $validatorsRun,
8✔
99
            $parsersCached,
8✔
100
        );
8✔
101

102
        $validationResult = new ValidationResult($validatorInstances, $overallResult, $validatorFileSetPairs, $statistics);
8✔
103
        $this->parserCache->clear();
8✔
104

105
        return $validationResult;
8✔
106
    }
107

108
    /**
109
     * @param array<string, array<string, array<string, array<string>>>> $allFiles
110
     *
111
     * @return array<FileSet>
112
     */
113
    public static function createFileSetsFromArray(array $allFiles): array
3✔
114
    {
115
        $fileSets = [];
3✔
116

117
        foreach ($allFiles as $parser => $paths) {
3✔
118
            foreach ($paths as $path => $translationSets) {
2✔
119
                foreach ($translationSets as $setKey => $files) {
2✔
120
                    $fileSets[] = new FileSet($parser, $path, $setKey, $files);
2✔
121
                }
122
            }
123
        }
124

125
        return $fileSets;
3✔
126
    }
127

128
    /**
129
     * @param array<FileSet> $fileSets
130
     */
131
    private function countKeysChecked(array $fileSets): int
8✔
132
    {
133
        $keysChecked = 0;
8✔
134

135
        foreach ($fileSets as $fileSet) {
8✔
136
            $parserClass = $fileSet->getParser();
6✔
137

138
            foreach ($fileSet->getFiles() as $file) {
6✔
139
                try {
140
                    $parser = $this->parserCache->get($file, $parserClass);
6✔
141
                    if (false === $parser) {
2✔
UNCOV
142
                        continue;
×
143
                    }
144
                    $keys = $parser->extractKeys();
2✔
145
                    if (is_array($keys)) {
2✔
146
                        $keysChecked += count($keys);
2✔
147
                    }
148
                } catch (Throwable) {
4✔
149
                    // Skip files that can't be parsed
150
                }
151
            }
152
        }
153

154
        return $keysChecked;
8✔
155
    }
156
}
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