From e51506da86e87a239a3a88ce849a307c462abc81 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 28 Oct 2026 05:33:27 +0700 Subject: [PATCH 1/2] Restore preload-split-package.php to fix PHPStan Turbo notice on rector-* packages --- bin/rector.php | 11 + build/build-preload.php | 54 ++- ecs.php | 1 + preload-split-package.php | 386 +++++++++++++++++++ src/Testing/PHPUnit/AbstractLazyTestCase.php | 4 + 5 files changed, 449 insertions(+), 7 deletions(-) create mode 100644 preload-split-package.php diff --git a/bin/rector.php b/bin/rector.php index 64d1c0ea3ad..2b6c158fffb 100755 --- a/bin/rector.php +++ b/bin/rector.php @@ -32,6 +32,12 @@ $autoloadIncluder = new AutoloadIncluder(); $autoloadIncluder->includeDependencyOrRepositoryVendorAutoloadIfExists(); +// rector-src on split packages: Turbo resolves php-parser classes through the project autoloader, +// as preload-split-package.php declares them only after Turbo is activated +if (file_exists(__DIR__ . '/../preload-split-package.php') && is_dir(__DIR__ . '/../../../../vendor')) { + $autoloadIncluder->autoloadProjectAutoloaderFile(); +} + // PHPStan Turbo replaces php-parser classes, so it is activated before preload.php declares them. // The main process only gets Turbo when php.ini loads it; the workers get it from WorkerCommandLineFactory. $phpstanBootstrapFile = __DIR__ . '/../../../phpstan/phpstan/bootstrap.php'; @@ -140,6 +146,11 @@ public function loadIfExistsAndNotLoadedYet(string $filePath): void require_once __DIR__ . '/../preload.php'; } +// require rector-src on split packages +if (file_exists(__DIR__ . '/../preload-split-package.php') && is_dir(__DIR__ . '/../../../../vendor')) { + require_once __DIR__ . '/../preload-split-package.php'; +} + $autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php'); $autoloadIncluder->autoloadProjectAutoloaderFile(); $autoloadIncluder->autoloadRectorInstalledAsGlobalDependency(); diff --git a/build/build-preload.php b/build/build-preload.php index a2216797532..30058d6d685 100755 --- a/build/build-preload.php +++ b/build/build-preload.php @@ -39,6 +39,7 @@ $preloadBuilder = new PreloadBuilder(); $preloadBuilder->buildPreloadScript($buildDirectory, $buildDirectory . '/preload.php'); +$preloadBuilder->buildPreloadScriptForSplitPackage($buildDirectory, $buildDirectory . '/preload-split-package.php'); final class PreloadBuilder { @@ -141,6 +142,44 @@ public function buildPreloadScript(string $buildDirectory, string $preloadFile): $this->buildPreloadScriptPhpDocParser($buildDirectory, $preloadFile); } + public function buildPreloadScriptForSplitPackage(string $buildDirectory, string $preloadFile): void + { + $this->buildPreloadScriptForSplitPhpParser($buildDirectory, $preloadFile); + $this->buildPreloadScriptForSplitPhpDocParser($buildDirectory, $preloadFile); + } + + private function buildPreloadScriptForSplitPhpParser(string $buildDirectory, string $preloadFile): void + { + $vendorDir = $buildDirectory . '/vendor'; + if (! is_dir($vendorDir . '/nikic/php-parser/lib/PhpParser')) { + return; + } + + // 1. find php-parser file infos + $fileInfos = $this->findPhpParserFilesAndSortThem($vendorDir); + + // 2. create preload-split-package.php from provided files + $preloadFileContent = $this->createPreloadFileContent($fileInfos, '/../../../vendor/'); + + file_put_contents($preloadFile, $preloadFileContent); + } + + private function buildPreloadScriptForSplitPhpDocParser(string $buildDirectory, string $preloadFile): void + { + $vendorDir = $buildDirectory . '/vendor'; + if (! is_dir($vendorDir . '/phpstan/phpdoc-parser')) { + return; + } + + // 1. find phpdoc-parser file infos + $fileInfos = $this->findPhpDocParserFilesAndSortThem($vendorDir); + + // 2. create preload-split-package.php from provided files + $preloadFileContent = $this->createPreloadFileContent($fileInfos, '/../../../vendor/', true); + + file_put_contents($preloadFile, $preloadFileContent, FILE_APPEND); + } + private function buildPreloadScriptPhpDocParser(string $buildDirectory, string $preloadFile): void { $vendorDir = $buildDirectory . '/vendor'; @@ -152,7 +191,7 @@ private function buildPreloadScriptPhpDocParser(string $buildDirectory, string $ $fileInfos = $this->findPhpDocParserFilesAndSortThem($vendorDir); // 2. create preload.php from provided files - $preloadFileContent = $this->createPreloadFileContent($fileInfos, true); + $preloadFileContent = $this->createPreloadFileContent($fileInfos, '/vendor/', true); file_put_contents($preloadFile, $preloadFileContent, FILE_APPEND); } @@ -168,7 +207,7 @@ private function buildPreloadScriptPhpParser(string $buildDirectory, string $pre $fileInfos = $this->findPhpParserFilesAndSortThem($vendorDir); // 3. create preload.php from provided files - $preloadFileContent = $this->createPreloadFileContent($fileInfos); + $preloadFileContent = $this->createPreloadFileContent($fileInfos, '/vendor/'); file_put_contents($preloadFile, $preloadFileContent); } @@ -207,7 +246,7 @@ private function findPhpDocParserFiles(string $vendorDir): array /** * @param SplFileInfo[] $fileInfos */ - private function createPreloadFileContent(array $fileInfos, bool $append = false): string + private function createPreloadFileContent(array $fileInfos, string $vendorPath, bool $append = false): string { $preloadFileContent = $append ? '' : self::PRELOAD_FILE_TEMPLATE . PHP_EOL . PHP_EOL; @@ -217,23 +256,24 @@ private function createPreloadFileContent(array $fileInfos, bool $append = false continue; } - $preloadFileContent .= $this->createRequireOnceFilePathLine($realPath); + $preloadFileContent .= $this->createRequireOnceFilePathLine($realPath, $vendorPath); } return $preloadFileContent; } - private function createRequireOnceFilePathLine(string $realPath): string + private function createRequireOnceFilePathLine(string $realPath, string $vendorPath): string { if (! str_contains($realPath, 'vendor')) { $filePath = '/src/' . Strings::after($realPath, '/src/'); return "require_once __DIR__ . '" . $filePath . "';" . PHP_EOL; } - $filePath = '/vendor/' . Strings::after($realPath, 'vendor/'); + $vendorFilePath = Strings::after($realPath, 'vendor/'); + $filePath = $vendorPath . $vendorFilePath; // PHPStan Turbo declares its native NodeTraverser before this file runs - if ($filePath === '/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php') { + if ($vendorFilePath === 'nikic/php-parser/lib/PhpParser/NodeTraverser.php') { return 'if (! class_exists(NodeTraverser::class, false)) {' . PHP_EOL . " require_once __DIR__ . '" . $filePath . "';" . PHP_EOL . '}' . PHP_EOL diff --git a/ecs.php b/ecs.php index 656c23fa9eb..45bd97c9c41 100644 --- a/ecs.php +++ b/ecs.php @@ -28,6 +28,7 @@ // avoid re-running on build __DIR__ . '/preload.php', + __DIR__ . '/preload-split-package.php', PhpdocTypesFixer::class => [ // double to Double false positive diff --git a/preload-split-package.php b/preload-split-package.php new file mode 100644 index 00000000000..51b63513cf1 --- /dev/null +++ b/preload-split-package.php @@ -0,0 +1,386 @@ + Date: Wed, 28 Oct 2026 05:37:37 +0700 Subject: [PATCH 2/2] use variable to avoid IO --- bin/rector.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/rector.php b/bin/rector.php index 2b6c158fffb..bb3825b0b93 100755 --- a/bin/rector.php +++ b/bin/rector.php @@ -34,7 +34,8 @@ // rector-src on split packages: Turbo resolves php-parser classes through the project autoloader, // as preload-split-package.php declares them only after Turbo is activated -if (file_exists(__DIR__ . '/../preload-split-package.php') && is_dir(__DIR__ . '/../../../../vendor')) { +$isSplitPackage = file_exists(__DIR__ . '/../preload-split-package.php') && is_dir(__DIR__ . '/../../../../vendor'); +if ($isSplitPackage) { $autoloadIncluder->autoloadProjectAutoloaderFile(); } @@ -147,10 +148,12 @@ public function loadIfExistsAndNotLoadedYet(string $filePath): void } // require rector-src on split packages -if (file_exists(__DIR__ . '/../preload-split-package.php') && is_dir(__DIR__ . '/../../../../vendor')) { +if ($isSplitPackage) { require_once __DIR__ . '/../preload-split-package.php'; } +unset($isSplitPackage); + $autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php'); $autoloadIncluder->autoloadProjectAutoloaderFile(); $autoloadIncluder->autoloadRectorInstalledAsGlobalDependency();