diff --git a/src/Cli.php b/src/Cli.php index 4cb359d..dfca919 100644 --- a/src/Cli.php +++ b/src/Cli.php @@ -30,6 +30,8 @@ class Cli public const PARAMETER_ONLY = 'only'; + public const PARAMETER_MEMORY_LIMIT = 'memory-limit'; + public static function isOnWindows(): bool { return PHP_OS_FAMILY === 'Windows'; diff --git a/src/Commands/RunCommand.php b/src/Commands/RunCommand.php index 28c02f0..c734484 100644 --- a/src/Commands/RunCommand.php +++ b/src/Commands/RunCommand.php @@ -107,6 +107,13 @@ protected function configure(): void 'Only returns with exit code 0, regardless of any errors/warnings' ); + $this->addOption( + Cli::PARAMETER_MEMORY_LIMIT, + 'm', + InputOption::VALUE_REQUIRED, + 'Sets the PHP memory limit for child processes (e.g. "512M", "-1" for unlimited)' + ); + $this->addArgument( 'files', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, @@ -129,6 +136,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $context->isFixing = (bool) $input->getOption(Cli::FLAG_FIX) || $this->config->shouldAutoFix(); $context->runningInCi = (bool) $input->getOption(Cli::FLAG_CI); $context->files = $files; + $context->memoryLimit = $input->getOption(Cli::PARAMETER_MEMORY_LIMIT); if ($context->runningInCi) { $input->setInteractive(false); @@ -185,6 +193,7 @@ private function executeContext(InputInterface $input, OutputInterface $output, $tool->setInput($input); $tool->setOutput($output); + $tool->setContext($context); $start = time(); $result = $tool->run($context); diff --git a/src/Context.php b/src/Context.php index 1790b42..ae576f7 100644 --- a/src/Context.php +++ b/src/Context.php @@ -26,6 +26,9 @@ class Context /** @var string[] */ public $toolsExecuted = []; + /** @var string|null */ + public $memoryLimit = null; + /** @var Result */ public $result; diff --git a/src/Tools/ComposerNormalize.php b/src/Tools/ComposerNormalize.php index bd19987..4600409 100644 --- a/src/Tools/ComposerNormalize.php +++ b/src/Tools/ComposerNormalize.php @@ -24,6 +24,11 @@ class ComposerNormalize extends Tool /** @var string */ protected $name = 'composer-normalize'; + protected function supportsMemoryLimit(): bool + { + return false; + } + public function shouldRun(Context $context): bool { // TODO does not check against file names, only full paths diff --git a/src/Tools/Deptrac.php b/src/Tools/Deptrac.php index 83a3b32..8638d3d 100644 --- a/src/Tools/Deptrac.php +++ b/src/Tools/Deptrac.php @@ -16,6 +16,11 @@ class Deptrac extends Tool /** @var string */ protected $name = 'deptrac'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $outputFile = $this->createTempReportFile(); diff --git a/src/Tools/EasyCodingStandard.php b/src/Tools/EasyCodingStandard.php index 1782d39..75da122 100644 --- a/src/Tools/EasyCodingStandard.php +++ b/src/Tools/EasyCodingStandard.php @@ -24,6 +24,11 @@ class EasyCodingStandard extends Tool /** @var string */ protected $name = 'ecs'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $output = []; diff --git a/src/Tools/Phan.php b/src/Tools/Phan.php index 4b5f128..4fc8af8 100644 --- a/src/Tools/Phan.php +++ b/src/Tools/Phan.php @@ -16,6 +16,11 @@ class Phan extends Tool /** @var string */ protected $name = 'phan'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $outputFile = $this->createTempReportFile(); diff --git a/src/Tools/PhpCodeSniffer.php b/src/Tools/PhpCodeSniffer.php index 0ef63eb..eb01604 100644 --- a/src/Tools/PhpCodeSniffer.php +++ b/src/Tools/PhpCodeSniffer.php @@ -14,6 +14,11 @@ class PhpCodeSniffer extends Tool /** @var string */ protected $name = 'php_codesniffer'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { if ($context->isFixing) { diff --git a/src/Tools/PhpMessDetector.php b/src/Tools/PhpMessDetector.php index 6474d1a..24e96ad 100644 --- a/src/Tools/PhpMessDetector.php +++ b/src/Tools/PhpMessDetector.php @@ -14,6 +14,11 @@ class PhpMessDetector extends Tool /** @var string */ protected $name = 'phpmd'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $output = []; diff --git a/src/Tools/PhpParallelLint/PhpParallelLint.php b/src/Tools/PhpParallelLint/PhpParallelLint.php index 9890fbe..4803b56 100644 --- a/src/Tools/PhpParallelLint/PhpParallelLint.php +++ b/src/Tools/PhpParallelLint/PhpParallelLint.php @@ -15,6 +15,11 @@ class PhpParallelLint extends Tool /** @var string */ protected $name = self::NAME; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $config = $context->config->getPart($this->name); diff --git a/src/Tools/Phpstan.php b/src/Tools/Phpstan.php index f2ac2dc..0581bdc 100644 --- a/src/Tools/Phpstan.php +++ b/src/Tools/Phpstan.php @@ -17,6 +17,11 @@ class Phpstan extends Tool /** @var string */ protected $name = 'phpstan'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $ignoreSources = (bool) ($context->config->getPart($this->getName())[Config::IGNORE_SOURCES] ?? false); diff --git a/src/Tools/Psalm.php b/src/Tools/Psalm.php index 72c454d..6f81b65 100644 --- a/src/Tools/Psalm.php +++ b/src/Tools/Psalm.php @@ -20,6 +20,11 @@ class Psalm extends Tool /** @var string */ protected $name = 'psalm'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $binary = self::vendorBinary($this->name); diff --git a/src/Tools/Rector.php b/src/Tools/Rector.php index 7c50e84..c2bea4b 100644 --- a/src/Tools/Rector.php +++ b/src/Tools/Rector.php @@ -21,6 +21,11 @@ class Rector extends Tool /** @var string */ protected $name = 'rector'; + protected function supportsMemoryLimit(): bool + { + return true; + } + public function run(Context $context): bool { $binary = self::vendorBinary('rector'); diff --git a/src/Tools/Tool.php b/src/Tools/Tool.php index 58e6e87..305081e 100644 --- a/src/Tools/Tool.php +++ b/src/Tools/Tool.php @@ -35,6 +35,9 @@ abstract class Tool /** @var OutputInterface */ protected $output; + /** @var Context|null */ + protected $context; + public function setInput(InputInterface $input): void { $this->input = $input; @@ -45,6 +48,11 @@ public function setOutput(OutputInterface $output): void $this->output = $output; } + public function setContext(Context $context): void + { + $this->context = $context; + } + /** * Runs this tool with the given context. */ @@ -84,7 +92,13 @@ protected function execute( return $argument !== ''; }); - $command = array_merge([$binary], $arguments); + $memoryLimit = $this->context !== null ? $this->context->memoryLimit : null; + + if ($memoryLimit !== null && $this->supportsMemoryLimit()) { + $command = array_merge([PHP_BINARY, '-d', "memory_limit={$memoryLimit}", $binary], $arguments); + } else { + $command = array_merge([$binary], $arguments); + } if ($this->output->isVeryVerbose()) { $this->output->writeln('Executing: ' . implode(' ', $command), Output::OUTPUT_RAW); @@ -129,6 +143,8 @@ protected function execute( return (int) $process->getExitCode(); } + abstract protected function supportsMemoryLimit(): bool; + protected static function vendorBinary(string $binary): string { $binary = PHPCSTD_BINARY_PATH . $binary;