1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Command;
4:
5: use Closure;
6: use PHPStan\Analyser\Error;
7: use PHPStan\Analyser\InternalError;
8: use PHPStan\Collectors\CollectedData;
9: use function count;
10: use function usort;
11:
12: /**
13: * @api
14: */
15: final class AnalysisResult
16: {
17:
18: /** @var list<Error> sorted by their file name, line number and message */
19: private array $fileSpecificErrors;
20:
21: /**
22: * @param list<Error> $fileSpecificErrors
23: * @param list<string> $notFileSpecificErrors
24: * @param list<InternalError> $internalErrors
25: * @param list<string> $warnings
26: * @param list<CollectedData>|(Closure(): list<CollectedData>) $collectedData A closure defers decoding the result cache until asked
27: * @param list<string> $processedFiles
28: */
29: public function __construct(
30: array $fileSpecificErrors,
31: private array $notFileSpecificErrors,
32: private array $internalErrors,
33: private array $warnings,
34: private array|Closure $collectedData,
35: private bool $defaultLevelUsed,
36: private ?string $projectConfigFile,
37: private bool $savedResultCache,
38: private int $peakMemoryUsageBytes,
39: private bool $isResultCacheUsed,
40: private array $processedFiles = [],
41: private bool $resultCacheExisted = true,
42: private int $workerCount = 0,
43: )
44: {
45: usort(
46: $fileSpecificErrors,
47: static fn (Error $a, Error $b): int => [
48: $a->getFile(),
49: $a->getLine(),
50: $a->getMessage(),
51: ] <=> [
52: $b->getFile(),
53: $b->getLine(),
54: $b->getMessage(),
55: ],
56: );
57:
58: $this->fileSpecificErrors = $fileSpecificErrors;
59: }
60:
61: public function hasErrors(): bool
62: {
63: return $this->getTotalErrorsCount() > 0;
64: }
65:
66: public function getTotalErrorsCount(): int
67: {
68: return count($this->fileSpecificErrors) + count($this->notFileSpecificErrors);
69: }
70:
71: /**
72: * @return list<Error> sorted by their file name, line number and message
73: */
74: public function getFileSpecificErrors(): array
75: {
76: return $this->fileSpecificErrors;
77: }
78:
79: /**
80: * @return list<string>
81: */
82: public function getNotFileSpecificErrors(): array
83: {
84: return $this->notFileSpecificErrors;
85: }
86:
87: /**
88: * @return list<InternalError>
89: */
90: public function getInternalErrorObjects(): array
91: {
92: return $this->internalErrors;
93: }
94:
95: /**
96: * @return list<string>
97: */
98: public function getWarnings(): array
99: {
100: return $this->warnings;
101: }
102:
103: public function hasWarnings(): bool
104: {
105: return count($this->warnings) > 0;
106: }
107:
108: /**
109: * @return list<CollectedData>
110: */
111: public function getCollectedData(): array
112: {
113: if ($this->collectedData instanceof Closure) {
114: $this->collectedData = ($this->collectedData)();
115: }
116:
117: return $this->collectedData;
118: }
119:
120: public function isDefaultLevelUsed(): bool
121: {
122: return $this->defaultLevelUsed;
123: }
124:
125: public function getProjectConfigFile(): ?string
126: {
127: return $this->projectConfigFile;
128: }
129:
130: public function hasInternalErrors(): bool
131: {
132: return count($this->internalErrors) > 0;
133: }
134:
135: public function isResultCacheSaved(): bool
136: {
137: return $this->savedResultCache;
138: }
139:
140: public function getPeakMemoryUsageBytes(): int
141: {
142: return $this->peakMemoryUsageBytes;
143: }
144:
145: /**
146: * How many parallel workers produced this result; 0 when the analysis ran in
147: * the main process.
148: */
149: public function getWorkerCount(): int
150: {
151: return $this->workerCount;
152: }
153:
154: public function isResultCacheUsed(): bool
155: {
156: return $this->isResultCacheUsed;
157: }
158:
159: /**
160: * Whether the result cache file existed when PHPStan started.
161: * False means the cache was never created; a stale/invalid cache that
162: * triggered a full re-analysis still counts as existing.
163: */
164: public function resultCacheExisted(): bool
165: {
166: return $this->resultCacheExisted;
167: }
168:
169: /**
170: * @deprecated
171: * @return array<string, string>
172: */
173: public function getChangedProjectExtensionFilesOutsideOfAnalysedPaths(): array
174: {
175: return [];
176: }
177:
178: /**
179: * @return list<string>
180: */
181: public function getProcessedFiles(): array
182: {
183: return $this->processedFiles;
184: }
185:
186: /**
187: * @api
188: * @param list<Error> $fileSpecificErrors
189: */
190: public function withFileSpecificErrors(array $fileSpecificErrors): self
191: {
192: return new self(
193: $fileSpecificErrors,
194: $this->notFileSpecificErrors,
195: $this->internalErrors,
196: $this->warnings,
197: $this->collectedData,
198: $this->defaultLevelUsed,
199: $this->projectConfigFile,
200: $this->savedResultCache,
201: $this->peakMemoryUsageBytes,
202: $this->isResultCacheUsed,
203: $this->processedFiles,
204: $this->resultCacheExisted,
205: );
206: }
207:
208: }
209: