Skip to content

Commit bcd0be2

Browse files
Narrow types
1 parent 3a34631 commit bcd0be2

File tree

4 files changed

+120
-28
lines changed

4 files changed

+120
-28
lines changed

src/Node/Directory.php

+39-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
use SebastianBergmann\CodeCoverage\StaticAnalysis\LinesOfCode;
1818

1919
/**
20+
* @template-implements IteratorAggregate<int, AbstractNode>
21+
*
22+
* @phpstan-import-type ProcessedFunctionType from File
23+
* @phpstan-import-type ProcessedClassType from File
24+
* @phpstan-import-type ProcessedTraitType from File
25+
*
2026
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
2127
*/
2228
final class Directory extends AbstractNode implements IteratorAggregate
@@ -34,9 +40,21 @@ final class Directory extends AbstractNode implements IteratorAggregate
3440
/**
3541
* @var list<File>
3642
*/
37-
private array $files = [];
38-
private ?array $classes = null;
39-
private ?array $traits = null;
43+
private array $files = [];
44+
45+
/**
46+
* @var ?array<string, ProcessedClassType>
47+
*/
48+
private ?array $classes = null;
49+
50+
/**
51+
* @var ?array<string, ProcessedTraitType>
52+
*/
53+
private ?array $traits = null;
54+
55+
/**
56+
* @var ?array<string, ProcessedFunctionType>
57+
*/
4058
private ?array $functions = null;
4159
private ?LinesOfCode $linesOfCode = null;
4260
private int $numFiles = -1;
@@ -97,21 +115,33 @@ public function addFile(File $file): void
97115
$this->numExecutedLines = -1;
98116
}
99117

118+
/**
119+
* @return list<Directory>
120+
*/
100121
public function directories(): array
101122
{
102123
return $this->directories;
103124
}
104125

126+
/**
127+
* @return list<File>
128+
*/
105129
public function files(): array
106130
{
107131
return $this->files;
108132
}
109133

134+
/**
135+
* @return list<Directory|File>
136+
*/
110137
public function children(): array
111138
{
112139
return $this->children;
113140
}
114141

142+
/**
143+
* @return array<string, ProcessedClassType>
144+
*/
115145
public function classes(): array
116146
{
117147
if ($this->classes === null) {
@@ -128,6 +158,9 @@ public function classes(): array
128158
return $this->classes;
129159
}
130160

161+
/**
162+
* @return array<string, ProcessedTraitType>
163+
*/
131164
public function traits(): array
132165
{
133166
if ($this->traits === null) {
@@ -144,6 +177,9 @@ public function traits(): array
144177
return $this->traits;
145178
}
146179

180+
/**
181+
* @return array<string, ProcessedFunctionType>
182+
*/
147183
public function functions(): array
148184
{
149185
if ($this->functions === null) {

src/Report/Html/Renderer.php

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public function __construct(string $templatePath, string $generator, string $dat
4444
$this->hasBranchCoverage = $hasBranchCoverage;
4545
}
4646

47+
/**
48+
* @param array<non-empty-string, float|int|string> $data
49+
*/
4750
protected function renderItemTemplate(Template $template, array $data): string
4851
{
4952
$numSeparator = '&nbsp;/&nbsp;';

src/Report/Html/Renderer/Dashboard.php

+35-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use function array_values;
1313
use function arsort;
1414
use function asort;
15+
use function assert;
1516
use function count;
1617
use function explode;
1718
use function floor;
@@ -21,10 +22,14 @@
2122
use SebastianBergmann\CodeCoverage\FileCouldNotBeWrittenException;
2223
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
2324
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
25+
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
2426
use SebastianBergmann\Template\Exception;
2527
use SebastianBergmann\Template\Template;
2628

2729
/**
30+
* @phpstan-import-type ProcessedClassType from FileNode
31+
* @phpstan-import-type ProcessedTraitType from FileNode
32+
*
2833
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
2934
*/
3035
final class Dashboard extends Renderer
@@ -81,7 +86,9 @@ protected function activeBreadcrumb(AbstractNode $node): string
8186
}
8287

8388
/**
84-
* Returns the data for the Class/Method Complexity charts.
89+
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
90+
*
91+
* @return array{class: non-empty-string, method: non-empty-string}
8592
*/
8693
private function complexity(array $classes, string $baseLink): array
8794
{
@@ -115,14 +122,21 @@ private function complexity(array $classes, string $baseLink): array
115122
];
116123
}
117124

118-
return [
119-
'class' => json_encode($result['class']),
120-
'method' => json_encode($result['method']),
121-
];
125+
$class = json_encode($result['class']);
126+
127+
assert($class !== false);
128+
129+
$method = json_encode($result['method']);
130+
131+
assert($method !== false);
132+
133+
return ['class' => $class, 'method' => $method];
122134
}
123135

124136
/**
125-
* Returns the data for the Class / Method Coverage Distribution chart.
137+
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
138+
*
139+
* @return array{class: non-empty-string, method: non-empty-string}
126140
*/
127141
private function coverageDistribution(array $classes): array
128142
{
@@ -181,14 +195,21 @@ private function coverageDistribution(array $classes): array
181195
}
182196
}
183197

184-
return [
185-
'class' => json_encode(array_values($result['class'])),
186-
'method' => json_encode(array_values($result['method'])),
187-
];
198+
$class = json_encode(array_values($result['class']));
199+
200+
assert($class !== false);
201+
202+
$method = json_encode(array_values($result['method']));
203+
204+
assert($method !== false);
205+
206+
return ['class' => $class, 'method' => $method];
188207
}
189208

190209
/**
191-
* Returns the classes / methods with insufficient coverage.
210+
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
211+
*
212+
* @return array{class: string, method: string}
192213
*/
193214
private function insufficientCoverage(array $classes, string $baseLink): array
194215
{
@@ -242,7 +263,9 @@ private function insufficientCoverage(array $classes, string $baseLink): array
242263
}
243264

244265
/**
245-
* Returns the project risks according to the CRAP index.
266+
* @param array<string, ProcessedClassType|ProcessedTraitType> $classes
267+
*
268+
* @return array{class: string, method: string}
246269
*/
247270
private function projectRisks(array $classes, string $baseLink): array
248271
{

src/Report/Html/Renderer/File.php

+43-13
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@
108108
use SebastianBergmann\Template\Template;
109109

110110
/**
111+
* @phpstan-import-type ProcessedClassType from FileNode
112+
* @phpstan-import-type ProcessedTraitType from FileNode
113+
* @phpstan-import-type ProcessedMethodType from FileNode
114+
* @phpstan-import-type ProcessedFunctionType from FileNode
115+
*
111116
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
112117
*/
113118
final class File extends Renderer
@@ -185,8 +190,13 @@ final class File extends Renderer
185190
T_YIELD => true,
186191
T_YIELD_FROM => true,
187192
];
193+
194+
private const int HTML_SPECIAL_CHARS_FLAGS = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE;
195+
196+
/**
197+
* @var array<non-empty-string, list<string>>
198+
*/
188199
private static array $formattedSourceCache = [];
189-
private int $htmlSpecialCharsFlags = ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE;
190200

191201
public function render(FileNode $node, string $file): void
192202
{
@@ -314,6 +324,9 @@ private function renderItems(FileNode $node): string
314324
return $items;
315325
}
316326

327+
/**
328+
* @param array<string, ProcessedClassType|ProcessedTraitType> $items
329+
*/
317330
private function renderTraitOrClassItems(array $items, Template $template, Template $methodItemTemplate): string
318331
{
319332
$buffer = '';
@@ -418,6 +431,9 @@ private function renderTraitOrClassItems(array $items, Template $template, Templ
418431
return $buffer;
419432
}
420433

434+
/**
435+
* @param array<string, ProcessedFunctionType> $functions
436+
*/
421437
private function renderFunctionItems(array $functions, Template $template): string
422438
{
423439
if (empty($functions)) {
@@ -436,6 +452,9 @@ private function renderFunctionItems(array $functions, Template $template): stri
436452
return $buffer;
437453
}
438454

455+
/**
456+
* @param ProcessedFunctionType|ProcessedMethodType $item
457+
*/
439458
private function renderFunctionOrMethodItem(Template $template, array $item, string $indent = ''): string
440459
{
441460
$numMethods = 0;
@@ -476,7 +495,7 @@ private function renderFunctionOrMethodItem(Template $template, array $item, str
476495
'%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
477496
$indent,
478497
$item['startLine'],
479-
htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags),
498+
htmlspecialchars($item['signature'], self::HTML_SPECIAL_CHARS_FLAGS),
480499
$item['functionName'] ?? $item['methodName'],
481500
),
482501
'numMethods' => $numMethods,
@@ -554,7 +573,7 @@ private function renderSourceWithLineCoverage(FileNode $node): string
554573
$popover = sprintf(
555574
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
556575
$popoverTitle,
557-
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
576+
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
558577
);
559578
}
560579

@@ -641,7 +660,7 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
641660
$popover = sprintf(
642661
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
643662
$popoverTitle,
644-
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
663+
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
645664
);
646665
}
647666

@@ -731,7 +750,7 @@ private function renderSourceWithPathCoverage(FileNode $node): string
731750
$popover = sprintf(
732751
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
733752
$popoverTitle,
734-
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
753+
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
735754
);
736755
}
737756

@@ -768,7 +787,7 @@ private function renderBranchStructure(FileNode $node): string
768787
}
769788

770789
if ($branchStructure !== '') { // don't show empty branches
771-
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, $this->htmlSpecialCharsFlags) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
790+
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
772791
$branches .= $branchStructure;
773792
}
774793
}
@@ -778,6 +797,9 @@ private function renderBranchStructure(FileNode $node): string
778797
return $branchesTemplate->render();
779798
}
780799

800+
/**
801+
* @param list<string> $codeLines
802+
*/
781803
private function renderBranchLines(array $branch, array $codeLines, array $testData): string
782804
{
783805
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
@@ -829,7 +851,7 @@ private function renderBranchLines(array $branch, array $codeLines, array $testD
829851
$popover = sprintf(
830852
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
831853
$popoverTitle,
832-
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
854+
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
833855
);
834856
}
835857

@@ -874,7 +896,7 @@ private function renderPathStructure(FileNode $node): string
874896
}
875897

876898
if ($pathStructure !== '') {
877-
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, $this->htmlSpecialCharsFlags) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
899+
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
878900
$paths .= $pathStructure;
879901
}
880902
}
@@ -884,6 +906,9 @@ private function renderPathStructure(FileNode $node): string
884906
return $pathsTemplate->render();
885907
}
886908

909+
/**
910+
* @param list<string> $codeLines
911+
*/
887912
private function renderPathLines(array $path, array $branches, array $codeLines, array $testData): string
888913
{
889914
$linesTemplate = new Template($this->templatePath . 'lines.html.dist', '{{', '}}');
@@ -944,7 +969,7 @@ private function renderPathLines(array $path, array $branches, array $codeLines,
944969
$popover = sprintf(
945970
' data-bs-title="%s" data-bs-content="%s" data-bs-placement="top" data-bs-html="true"',
946971
$popoverTitle,
947-
htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags),
972+
htmlspecialchars($popoverContent, self::HTML_SPECIAL_CHARS_FLAGS),
948973
);
949974
}
950975

@@ -975,6 +1000,11 @@ private function renderLine(Template $template, int $lineNumber, string $lineCon
9751000
return $template->render();
9761001
}
9771002

1003+
/**
1004+
* @param non-empty-string $file
1005+
*
1006+
* @return list<string>
1007+
*/
9781008
private function loadFile(string $file): array
9791009
{
9801010
if (isset(self::$formattedSourceCache[$file])) {
@@ -995,14 +1025,14 @@ private function loadFile(string $file): array
9951025
if ($token === '"' && $tokens[$j - 1] !== '\\') {
9961026
$result[$i] .= sprintf(
9971027
'<span class="string">%s</span>',
998-
htmlspecialchars($token, $this->htmlSpecialCharsFlags),
1028+
htmlspecialchars($token, self::HTML_SPECIAL_CHARS_FLAGS),
9991029
);
10001030

10011031
$stringFlag = !$stringFlag;
10021032
} else {
10031033
$result[$i] .= sprintf(
10041034
'<span class="keyword">%s</span>',
1005-
htmlspecialchars($token, $this->htmlSpecialCharsFlags),
1035+
htmlspecialchars($token, self::HTML_SPECIAL_CHARS_FLAGS),
10061036
);
10071037
}
10081038

@@ -1014,7 +1044,7 @@ private function loadFile(string $file): array
10141044
$value = str_replace(
10151045
["\t", ' '],
10161046
['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
1017-
htmlspecialchars($value, $this->htmlSpecialCharsFlags),
1047+
htmlspecialchars($value, self::HTML_SPECIAL_CHARS_FLAGS),
10181048
);
10191049

10201050
if ($value === "\n") {
@@ -1113,7 +1143,7 @@ private function createPopoverContentForTest(string $test, array $testData): str
11131143
return sprintf(
11141144
'<li%s>%s</li>',
11151145
$testCSS,
1116-
htmlspecialchars($test, $this->htmlSpecialCharsFlags),
1146+
htmlspecialchars($test, self::HTML_SPECIAL_CHARS_FLAGS),
11171147
);
11181148
}
11191149

0 commit comments

Comments
 (0)