Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 13 additions & 71 deletions src/Command/FixerApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,21 @@
use GuzzleHttp\RequestOptions;
use Nette\Utils\Json;
use Phar;
use PHPStan\Analyser\Ignore\IgnoredErrorHelper;
use PHPStan\Analyser\InternalError;
use PHPStan\DependencyInjection\AutowiredParameter;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\File\FileMonitor;
use PHPStan\File\FileMonitorResult;
use PHPStan\File\FileReader;
use PHPStan\File\FileWriter;
use PHPStan\File\PathNotFoundException;
use PHPStan\Internal\ComposerHelper;
use PHPStan\Internal\DirectoryCreator;
use PHPStan\Internal\DirectoryCreatorException;
use PHPStan\Internal\HttpClientFactory;
use PHPStan\Parallel\ForkParallelChecker;
use PHPStan\PhpDoc\StubFilesProvider;
use PHPStan\Process\ForkedProcessPromise;
use PHPStan\Process\ProcessCanceledException;
use PHPStan\Process\ProcessCrashedException;
use PHPStan\Process\ProcessHelper;
use PHPStan\Process\ProcessPromise;
use PHPStan\Process\SpawnedProcessPromise;
use PHPStan\ShouldNotHappenException;
use React\ChildProcess\Process;
Expand Down Expand Up @@ -76,7 +71,6 @@ final class FixerApplication
*/
public function __construct(
private FileMonitor $fileMonitor,
private IgnoredErrorHelper $ignoredErrorHelper,
private StubFilesProvider $stubFilesProvider,
#[AutowiredParameter]
private array $analysedPaths,
Expand All @@ -97,8 +91,6 @@ public function __construct(
#[AutowiredParameter]
private string $usedLevel,
private HttpClientFactory $httpClientFactory,
private ForkParallelChecker $forkParallelChecker,
private FixerWorkerRunner $fixerWorkerRunner,
)
{
}
Expand All @@ -121,7 +113,7 @@ public function run(
/** @var int<0, 65535> $serverPort */
$serverPort = parse_url($serverAddress, PHP_URL_PORT);

$server->on('connection', function (ConnectionInterface $connection) use ($loop, $inceptionResult, $projectConfigFile, $input, $output, $mainScript, $filesCount): void {
$server->on('connection', function (ConnectionInterface $connection) use ($loop, $projectConfigFile, $input, $output, $mainScript, $filesCount): void {
// phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly
$jsonInvalidUtf8Ignore = defined('JSON_INVALID_UTF8_IGNORE') ? JSON_INVALID_UTF8_IGNORE : 0;
// phpcs:enable
Expand Down Expand Up @@ -164,14 +156,13 @@ public function run(

$this->analyse(
$loop,
$inceptionResult,
$mainScript,
$projectConfigFile,
$input,
$encoder,
);

$this->monitorFileChanges($loop, function (FileMonitorResult $changes) use ($loop, $inceptionResult, $mainScript, $projectConfigFile, $input, $encoder): void {
$this->monitorFileChanges($loop, function (FileMonitorResult $changes) use ($loop, $mainScript, $projectConfigFile, $input, $encoder): void {
if ($this->processInProgress !== null) {
$this->processInProgress->cancel();
$this->processInProgress = null;
Expand All @@ -185,7 +176,6 @@ public function run(

$this->analyse(
$loop,
$inceptionResult,
$mainScript,
$projectConfigFile,
$input,
Expand Down Expand Up @@ -424,18 +414,12 @@ private function monitorFileChanges(LoopInterface $loop, callable $hasChangesCal

private function analyse(
LoopInterface $loop,
InceptionResult $inceptionResult,
string $mainScript,
?string $projectConfigFile,
InputInterface $input,
Encoder $phpstanFixerEncoder,
): void
{
$ignoredErrorHelperResult = $this->ignoredErrorHelper->initialize();
if (count($ignoredErrorHelperResult->getErrors()) > 0) {
throw new ShouldNotHappenException();
}

// TCP server for fixer:worker (TCP client)
$server = new TcpServer('127.0.0.1:0', $loop);
/** @var string $serverAddress */
Expand All @@ -453,16 +437,20 @@ private function analyse(
});
});

$process = $this->createProcessPromise(
$this->forkParallelChecker->isSupported(),
$loop,
$server,
// Always a fresh process, never a fork of this one: this process runs as long as
// PHPStan Pro does, so its container and config are the ones it started with.
// A spawned fixer:worker reads the config files again, which picks up edits
// like errors added to the baseline. It still forks its own analysis workers.
$process = new SpawnedProcessPromise($loop, ProcessHelper::getWorkerCommand(
$mainScript,
'fixer:worker',
$projectConfigFile,
[
'--server-port',
(string) $serverPort,
],
$input,
$serverPort,
$inceptionResult,
);
));
$this->processInProgress = $process->run();

$this->processInProgress->then(function () use ($server): void {
Expand Down Expand Up @@ -573,50 +561,4 @@ private function getStubFiles(): array
return $stubFiles;
}

/**
* @param int<0, 65535> $serverPort
*/
private function createProcessPromise(
bool $useFork,
LoopInterface $loop,
TcpServer $server,
string $mainScript,
?string $projectConfigFile,
InputInterface $input,
int $serverPort,
InceptionResult $inceptionResult,
): ProcessPromise
{
if ($useFork) {
try {
[$inceptionFiles, $isOnlyFiles] = $inceptionResult->getFiles();
} catch (PathNotFoundException | InceptionNotSuccessfulException) {
throw new ShouldNotHappenException();
}

return new ForkedProcessPromise(
$loop,
$this->fixerWorkerRunner,
$server,
$inceptionFiles,
$isOnlyFiles,
$inceptionResult->getProjectConfigArray(),
$projectConfigFile,
$serverPort,
$input,
);
}

return new SpawnedProcessPromise($loop, ProcessHelper::getWorkerCommand(
$mainScript,
'fixer:worker',
$projectConfigFile,
[
'--server-port',
(string) $serverPort,
],
$input,
));
}

}
3 changes: 0 additions & 3 deletions src/Command/FixerWorkerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
throw new ShouldNotHappenException();
}

// Everything after the boot lives in FixerWorkerRunner so a
// pcntl_fork()-ed child can reuse it without re-booting (see
// FixerApplication).
$fixerWorkerRunner = $container->getByType(FixerWorkerRunner::class);

return $fixerWorkerRunner->run(
Expand Down
10 changes: 3 additions & 7 deletions src/Command/FixerWorkerRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@
/**
* The PHPStan Pro worker logic that runs *after* the application boot.
*
* Extracted from FixerWorkerCommand so it can be reused without re-booting: a
* pcntl_fork()-ed child (see ForkedProcessPromise) inherits the already-booted
* DI container and calls run() directly, while FixerWorkerCommand still calls
* it after the expensive CommandHelper::begin() boot of a freshly spawned
* process.
* FixerWorkerCommand calls it after the CommandHelper::begin() boot of the
* process FixerApplication spawns for each analysis. The analysis itself
* forks its workers where that is supported (see ParallelAnalyser).
*
* It connects back to FixerApplication's TCP server, restores the result
* cache, analyses the changed files and streams the results.
Expand Down Expand Up @@ -82,8 +80,6 @@ public function run(
throw new ShouldNotHappenException();
}

// Always a fresh event loop: in a forked child the parent's inherited
// loop must never be touched.
$loop = new StreamSelectLoop();
$tcpConnector = new TcpConnector($loop);
$tcpConnector->connect(sprintf('127.0.0.1:%d', $serverPort))->then(function (ConnectionInterface $connection) use ($errorOutput, $inceptionFiles, $isOnlyFiles, $projectConfigArray, $configuration, $input, $ignoredErrorHelperResult, $loop): void {
Expand Down
Loading
Loading