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

keradus / PHP-CS-Fixer / 19958239208

05 Dec 2025 09:13AM UTC coverage: 93.181% (-1.0%) from 94.158%
19958239208

push

github

keradus
chore: .php-cs-fixer.dist.php - remove no longer needed rule, 'expectedDeprecation' annotation does not exist for long time

28928 of 31045 relevant lines covered (93.18%)

44.49 hits per line

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

91.94
/src/Console/Report/FixReport/GitlabReporter.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Console\Report\FixReport;
16

17
use PhpCsFixer\Console\Application;
18
use PhpCsFixer\Documentation\DocumentationLocator;
19
use PhpCsFixer\Fixer\FixerInterface;
20
use PhpCsFixer\FixerFactory;
21
use SebastianBergmann\Diff\Chunk;
22
use SebastianBergmann\Diff\Diff;
23
use SebastianBergmann\Diff\Line;
24
use SebastianBergmann\Diff\Parser;
25
use Symfony\Component\Console\Formatter\OutputFormatter;
26

27
/**
28
 * Generates a report according to gitlabs subset of codeclimate json files.
29
 *
30
 * @author Hans-Christian Otto <c.otto@suora.com>
31
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
32
 *
33
 * @see https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#data-types
34
 *
35
 * @readonly
36
 *
37
 * @internal
38
 *
39
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
40
 */
41
final class GitlabReporter implements ReporterInterface
42
{
43
    private Parser $diffParser;
44
    private DocumentationLocator $documentationLocator;
45
    private FixerFactory $fixerFactory;
46

47
    /**
48
     * @var array<string, FixerInterface>
49
     */
50
    private array $fixers;
51

52
    public function __construct()
53
    {
54
        $this->diffParser = new Parser();
8✔
55
        $this->documentationLocator = new DocumentationLocator();
8✔
56

57
        $this->fixerFactory = new FixerFactory();
8✔
58
        $this->fixerFactory->registerBuiltInFixers();
8✔
59

60
        $this->fixers = $this->createFixers();
8✔
61
    }
62

63
    public function getFormat(): string
64
    {
65
        return 'gitlab';
1✔
66
    }
67

68
    /**
69
     * Process changed files array. Returns generated report.
70
     */
71
    public function generate(ReportSummary $reportSummary): string
72
    {
73
        $about = Application::getAbout();
6✔
74

75
        $report = [];
6✔
76
        foreach ($reportSummary->getChanged() as $fileName => $change) {
6✔
77
            foreach ($change['appliedFixers'] as $fixerName) {
5✔
78
                $fixer = $this->fixers[$fixerName] ?? null;
5✔
79

80
                $report[] = [
5✔
81
                    'check_name' => 'PHP-CS-Fixer.'.$fixerName,
5✔
82
                    'description' => null !== $fixer
5✔
83
                        ? $fixer->getDefinition()->getSummary()
×
84
                        : 'PHP-CS-Fixer.'.$fixerName.' (custom rule)',
5✔
85
                    'content' => [
5✔
86
                        'body' => \sprintf(
5✔
87
                            "%s\n%s",
5✔
88
                            $about,
5✔
89
                            null !== $fixer
5✔
90
                                ? \sprintf(
×
91
                                    'Check [docs](https://cs.symfony.com/doc/rules/%s.html) for more information.',
×
92
                                    substr($this->documentationLocator->getFixerDocumentationFileRelativePath($fixer), 0, -4) // -4 to drop `.rst`
×
93
                                )
×
94
                                : 'Check performed with a custom rule.'
5✔
95
                        ),
5✔
96
                    ],
5✔
97
                    'categories' => ['Style'],
5✔
98
                    'fingerprint' => md5($fileName.$fixerName),
5✔
99
                    'severity' => 'minor',
5✔
100
                    'location' => [
5✔
101
                        'path' => $fileName,
5✔
102
                        'lines' => self::getLines($this->diffParser->parse($change['diff'])),
5✔
103
                    ],
5✔
104
                ];
5✔
105
            }
106
        }
107

108
        $jsonString = json_encode($report, \JSON_THROW_ON_ERROR);
6✔
109

110
        return $reportSummary->isDecoratedOutput() ? OutputFormatter::escape($jsonString) : $jsonString;
6✔
111
    }
112

113
    /**
114
     * @param list<Diff> $diffs
115
     *
116
     * @return array{begin: int, end: int}
117
     */
118
    private static function getLines(array $diffs): array
119
    {
120
        if (isset($diffs[0])) {
5✔
121
            $firstDiff = $diffs[0];
3✔
122

123
            $firstChunk = \Closure::bind(static fn (Diff $diff) => array_shift($diff->chunks), null, $firstDiff)($firstDiff);
3✔
124

125
            if ($firstChunk instanceof Chunk) {
3✔
126
                return self::getBeginEndForDiffChunk($firstChunk);
3✔
127
            }
128
        }
129

130
        return ['begin' => 0, 'end' => 0];
2✔
131
    }
132

133
    /**
134
     * @return array{begin: int, end: int}
135
     */
136
    private static function getBeginEndForDiffChunk(Chunk $chunk): array
137
    {
138
        $start = \Closure::bind(static fn (Chunk $chunk): int => $chunk->start, null, $chunk)($chunk);
3✔
139
        $startRange = \Closure::bind(static fn (Chunk $chunk): int => $chunk->startRange, null, $chunk)($chunk);
3✔
140
        $lines = \Closure::bind(static fn (Chunk $chunk): array => $chunk->lines, null, $chunk)($chunk);
3✔
141

142
        \assert(\count($lines) > 0);
3✔
143

144
        $firstModifiedLineOffset = array_find_key($lines, static function (Line $line): bool {
3✔
145
            $type = \Closure::bind(static fn (Line $line): int => $line->type, null, $line)($line);
3✔
146

147
            return Line::UNCHANGED !== $type;
3✔
148
        });
3✔
149
        \assert(\is_int($firstModifiedLineOffset));
3✔
150

151
        return [
3✔
152
            // offset the start by where the first line is actually modified
153
            'begin' => $start + $firstModifiedLineOffset,
3✔
154
            // it's not where last modification takes place, only where diff (with --context) ends
155
            'end' => $start + $startRange,
3✔
156
        ];
3✔
157
    }
158

159
    /**
160
     * @return array<string, FixerInterface>
161
     */
162
    private function createFixers(): array
163
    {
164
        $fixers = [];
8✔
165

166
        foreach ($this->fixerFactory->getFixers() as $fixer) {
8✔
167
            $fixers[$fixer->getName()] = $fixer;
8✔
168
        }
169

170
        ksort($fixers);
8✔
171

172
        return $fixers;
8✔
173
    }
174
}
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