Skip to content

Commit daf7d55

Browse files
committed
Use memory limit management trick from Laravel
See https://twitter.com/timacdonald87/status/1484804225658146823
1 parent 13cb996 commit daf7d55

10 files changed

+39
-79
lines changed

conf/config.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,6 @@ services:
465465
-
466466
class: PHPStan\Command\AnalyseApplication
467467
arguments:
468-
memoryLimitFile: %memoryLimitFile%
469468
internalErrorsCountLimit: %internalErrorsCountLimit%
470469

471470
-

phpstan-baseline.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ parameters:
3030
count: 1
3131
path: src/Command/CommandHelper.php
3232

33+
-
34+
message: "#^Static property PHPStan\\\\Command\\\\CommandHelper\\:\\:\\$reservedMemory is never read, only written\\.$#"
35+
count: 1
36+
path: src/Command/CommandHelper.php
37+
3338
-
3439
message: "#^Parameter \\#1 \\$headers \\(array\\<string\\>\\) of method PHPStan\\\\Command\\\\ErrorsConsoleStyle\\:\\:table\\(\\) should be contravariant with parameter \\$headers \\(array\\) of method Symfony\\\\Component\\\\Console\\\\Style\\\\StyleInterface\\:\\:table\\(\\)$#"
3540
count: 1

src/Command/AnalyseApplication.php

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,10 @@
99
use PHPStan\PhpDoc\StubValidator;
1010
use Symfony\Component\Console\Input\InputInterface;
1111
use function array_merge;
12-
use function ceil;
1312
use function count;
14-
use function error_get_last;
15-
use function file_put_contents;
1613
use function is_string;
1714
use function memory_get_peak_usage;
18-
use function register_shutdown_function;
1915
use function sprintf;
20-
use function strpos;
21-
use function unlink;
22-
use const E_ERROR;
2316

2417
class AnalyseApplication
2518
{
@@ -29,7 +22,6 @@ public function __construct(
2922
private StubValidator $stubValidator,
3023
private ResultCacheManagerFactory $resultCacheManagerFactory,
3124
private IgnoredErrorHelper $ignoredErrorHelper,
32-
private string $memoryLimitFile,
3325
private int $internalErrorsCountLimit,
3426
)
3527
{
@@ -51,29 +43,12 @@ public function analyse(
5143
InputInterface $input,
5244
): AnalysisResult
5345
{
54-
$this->updateMemoryLimitFile();
5546
$projectStubFiles = [];
5647
if ($projectConfigArray !== null) {
5748
$projectStubFiles = $projectConfigArray['parameters']['stubFiles'] ?? [];
5849
}
5950
$stubErrors = $this->stubValidator->validate($projectStubFiles, $debug);
6051

61-
register_shutdown_function(function (): void {
62-
$error = error_get_last();
63-
if ($error === null) {
64-
return;
65-
}
66-
if ($error['type'] !== E_ERROR) {
67-
return;
68-
}
69-
70-
if (strpos($error['message'], 'Allowed memory size') !== false) {
71-
return;
72-
}
73-
74-
@unlink($this->memoryLimitFile);
75-
});
76-
7752
$resultCacheManager = $this->resultCacheManagerFactory->create([]);
7853

7954
$ignoredErrorHelperResult = $this->ignoredErrorHelper->initialize();
@@ -155,21 +130,14 @@ private function runAnalyser(
155130

156131
if (!$debug) {
157132
$progressStarted = false;
158-
$fileOrder = 0;
159133
$preFileCallback = null;
160-
$postFileCallback = function (int $step) use ($errorOutput, &$progressStarted, $allAnalysedFilesCount, $filesCount, &$fileOrder): void {
134+
$postFileCallback = static function (int $step) use ($errorOutput, &$progressStarted, $allAnalysedFilesCount, $filesCount): void {
161135
if (!$progressStarted) {
162136
$errorOutput->getStyle()->progressStart($allAnalysedFilesCount);
163137
$errorOutput->getStyle()->progressAdvance($allAnalysedFilesCount - $filesCount);
164138
$progressStarted = true;
165139
}
166140
$errorOutput->getStyle()->progressAdvance($step);
167-
168-
if ($fileOrder >= 100) {
169-
$this->updateMemoryLimitFile();
170-
$fileOrder = 0;
171-
}
172-
$fileOrder += $step;
173141
};
174142
} else {
175143
$preFileCallback = static function (string $file) use ($stdOutput): void {
@@ -195,11 +163,4 @@ private function runAnalyser(
195163
return $analyserResult;
196164
}
197165

198-
private function updateMemoryLimitFile(): void
199-
{
200-
$bytes = memory_get_peak_usage(true);
201-
$megabytes = ceil($bytes / 1024 / 1024);
202-
file_put_contents($this->memoryLimitFile, sprintf('%d MB', $megabytes));
203-
}
204-
205166
}

src/Command/AnalyseCommand.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
148148
$generateBaselineFile,
149149
$level,
150150
$allowXdebug,
151-
true,
152151
$debugEnabled,
153152
);
154153
} catch (InceptionNotSuccessfulException $e) {
@@ -409,7 +408,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
409408
return $inceptionResult->handleReturn(1);
410409
}
411410

412-
$inceptionResult->handleReturn(0); // delete memory limit file
411+
$inceptionResult->handleReturn(0);
413412

414413
/** @var FixerApplication $fixerApplication */
415414
$fixerApplication = $container->getByType(FixerApplication::class);

src/Command/ClearResultCacheCommand.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6262
null,
6363
'0',
6464
false,
65-
false,
6665
);
6766
} catch (InceptionNotSuccessfulException) {
6867
return 1;

src/Command/CommandHelper.php

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,8 @@
2323
use PHPStan\DependencyInjection\LoaderFactory;
2424
use PHPStan\DependencyInjection\NeonAdapter;
2525
use PHPStan\ExtensionInstaller\GeneratedConfig;
26-
use PHPStan\File\CouldNotReadFileException;
2726
use PHPStan\File\FileFinder;
2827
use PHPStan\File\FileHelper;
29-
use PHPStan\File\FileReader;
3028
use PHPStan\ShouldNotHappenException;
3129
use ReflectionClass;
3230
use Symfony\Component\Console\Input\InputInterface;
@@ -41,11 +39,13 @@
4139
use function class_exists;
4240
use function count;
4341
use function dirname;
42+
use function error_get_last;
4443
use function function_exists;
4544
use function get_class;
4645
use function getcwd;
4746
use function gettype;
4847
use function implode;
48+
use function ini_get;
4949
use function ini_set;
5050
use function is_dir;
5151
use function is_file;
@@ -54,11 +54,14 @@
5454
use function mkdir;
5555
use function pcntl_async_signals;
5656
use function pcntl_signal;
57+
use function register_shutdown_function;
5758
use function sprintf;
5859
use function str_ends_with;
60+
use function str_repeat;
61+
use function strpos;
5962
use function sys_get_temp_dir;
60-
use function unlink;
6163
use const DIRECTORY_SEPARATOR;
64+
use const E_ERROR;
6265
use const PHP_VERSION_ID;
6366
use const SIGINT;
6467

@@ -67,6 +70,8 @@ class CommandHelper
6770

6871
public const DEFAULT_LEVEL = '0';
6972

73+
private static ?string $reservedMemory = null;
74+
7075
/**
7176
* @param string[] $paths
7277
* @param string[] $composerAutoloaderProjectPaths
@@ -84,7 +89,6 @@ public static function begin(
8489
?string $generateBaselineFile,
8590
?string $level,
8691
bool $allowXdebug,
87-
bool $manageMemoryLimitFile = true,
8892
bool $debugEnabled = false,
8993
?string $singleReflectionFile = null,
9094
?string $singleReflectionInsteadOfFile = null,
@@ -97,6 +101,7 @@ public static function begin(
97101
$xdebug->check();
98102
unset($xdebug);
99103
}
104+
100105
$stdOutput = new SymfonyOutput($output, new SymfonyStyle(new ErrorsConsoleStyle($input, $output)));
101106

102107
/** @var Output $errorOutput */
@@ -115,6 +120,26 @@ public static function begin(
115120
}
116121
}
117122

123+
self::$reservedMemory = str_repeat('PHPStan', 1463); // reserve 10 kB of space
124+
register_shutdown_function(static function () use ($errorOutput): void {
125+
self::$reservedMemory = null;
126+
$error = error_get_last();
127+
if ($error === null) {
128+
return;
129+
}
130+
if ($error['type'] !== E_ERROR) {
131+
return;
132+
}
133+
134+
if (strpos($error['message'], 'Allowed memory size') === false) {
135+
return;
136+
}
137+
138+
$errorOutput->writeLineFormatted('');
139+
$errorOutput->writeLineFormatted(sprintf('<error>PHPStan process crashed because it reached configured PHP memory limit</error>: %s', ini_get('memory_limit')));
140+
$errorOutput->writeLineFormatted('Increase your memory limit in php.ini or run PHPStan with --memory-limit CLI option.');
141+
});
142+
118143
$currentWorkingDirectory = getcwd();
119144
if ($currentWorkingDirectory === false) {
120145
throw new ShouldNotHappenException();
@@ -317,24 +342,7 @@ public static function begin(
317342
throw new InceptionNotSuccessfulException();
318343
}
319344

320-
$memoryLimitFile = $container->getParameter('memoryLimitFile');
321-
if ($manageMemoryLimitFile && is_file($memoryLimitFile)) {
322-
$errorOutput->writeLineFormatted('PHPStan crashed in the previous run probably because of excessive memory consumption.');
323-
324-
try {
325-
$memoryLimitFileContents = FileReader::read($memoryLimitFile);
326-
$errorOutput->writeLineFormatted(sprintf('It consumed around %s of memory.', $memoryLimitFileContents));
327-
$errorOutput->writeLineFormatted('');
328-
} catch (CouldNotReadFileException) {
329-
// pass
330-
}
331-
332-
$errorOutput->writeLineFormatted('');
333-
$errorOutput->writeLineFormatted('To avoid this issue, allow to use more memory with the --memory-limit option.');
334-
@unlink($memoryLimitFile);
335-
}
336-
337-
self::setUpSignalHandler($errorOutput, $manageMemoryLimitFile ? $memoryLimitFile : null);
345+
self::setUpSignalHandler($errorOutput);
338346
if (!$container->hasParameter('customRulesetUsed')) {
339347
$errorOutput->writeLineFormatted('');
340348
$errorOutput->writeLineFormatted('<comment>No rules detected</comment>');
@@ -457,7 +465,6 @@ public static function begin(
457465
$errorOutput,
458466
$container,
459467
$defaultLevelUsed,
460-
$memoryLimitFile,
461468
$projectConfigFile,
462469
$projectConfig,
463470
$generateBaselineFile,
@@ -493,17 +500,14 @@ private static function executeBootstrapFile(
493500
}
494501
}
495502

496-
private static function setUpSignalHandler(Output $output, ?string $memoryLimitFile): void
503+
private static function setUpSignalHandler(Output $output): void
497504
{
498505
if (!function_exists('pcntl_signal')) {
499506
return;
500507
}
501508

502509
pcntl_async_signals(true);
503-
pcntl_signal(SIGINT, static function () use ($output, $memoryLimitFile): void {
504-
if ($memoryLimitFile !== null && is_file($memoryLimitFile)) {
505-
@unlink($memoryLimitFile);
506-
}
510+
pcntl_signal(SIGINT, static function () use ($output): void {
507511
$output->writeLineFormatted('');
508512
exit(1);
509513
});

src/Command/FixerWorkerCommand.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
113113
$level,
114114
$allowXdebug,
115115
false,
116-
false,
117116
$singleReflectionFile,
118117
$insteadOfFile,
119118
false,

src/Command/InceptionResult.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use PHPStan\Internal\BytesHelper;
77
use function memory_get_peak_usage;
88
use function sprintf;
9-
use function unlink;
109

1110
class InceptionResult
1211
{
@@ -24,7 +23,6 @@ public function __construct(
2423
private Output $errorOutput,
2524
private Container $container,
2625
private bool $isDefaultLevelUsed,
27-
private string $memoryLimitFile,
2826
private ?string $projectConfigFile,
2927
private ?array $projectConfigArray,
3028
private ?string $generateBaselineFile,
@@ -87,7 +85,6 @@ public function handleReturn(int $exitCode): int
8785
$this->getErrorOutput()->writeLineFormatted(sprintf('Used memory: %s', BytesHelper::bytes(memory_get_peak_usage(true))));
8886
}
8987

90-
@unlink($this->memoryLimitFile);
9188
return $exitCode;
9289
}
9390

src/Command/WorkerCommand.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
114114
$level,
115115
$allowXdebug,
116116
false,
117-
false,
118117
$singleReflectionFile,
119118
null,
120119
false,

tests/PHPStan/Command/CommandHelperTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ public function testBegin(
124124
null,
125125
$level,
126126
false,
127-
true,
128127
);
129128
if ($expectException) {
130129
$this->fail();
@@ -308,7 +307,6 @@ public function testResolveParameters(
308307
null,
309308
'0',
310309
false,
311-
true,
312310
);
313311
$parameters = $result->getContainer()->getParameters();
314312
foreach ($expectedParameters as $name => $expectedValue) {

0 commit comments

Comments
 (0)