diff --git a/conf/config.neon b/conf/config.neon
index c85cce945d..46f65b6fbb 100644
--- a/conf/config.neon
+++ b/conf/config.neon
@@ -2137,6 +2137,7 @@ services:
class: PHPStan\Diagnose\PHPStanDiagnoseExtension
arguments:
composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths%
+ allConfigFiles: %allConfigFiles%
autowired: false
# Error formatters
diff --git a/src/Diagnose/PHPStanDiagnoseExtension.php b/src/Diagnose/PHPStanDiagnoseExtension.php
index 0873b4f802..8a0834d278 100644
--- a/src/Diagnose/PHPStanDiagnoseExtension.php
+++ b/src/Diagnose/PHPStanDiagnoseExtension.php
@@ -5,13 +5,21 @@
use Phar;
use PHPStan\Command\Output;
use PHPStan\ExtensionInstaller\GeneratedConfig;
+use PHPStan\File\FileHelper;
use PHPStan\Internal\ComposerHelper;
use PHPStan\Php\PhpVersion;
+use function array_key_exists;
+use function array_slice;
use function class_exists;
use function count;
use function dirname;
+use function explode;
+use function implode;
use function is_file;
use function sprintf;
+use function str_starts_with;
+use function strlen;
+use function substr;
use const PHP_VERSION_ID;
class PHPStanDiagnoseExtension implements DiagnoseExtension
@@ -19,10 +27,13 @@ class PHPStanDiagnoseExtension implements DiagnoseExtension
/**
* @param string[] $composerAutoloaderProjectPaths
+ * @param string [] $allConfigFiles
*/
public function __construct(
private PhpVersion $phpVersion,
+ private FileHelper $fileHelper,
private array $composerAutoloaderProjectPaths,
+ private array $allConfigFiles,
)
{
}
@@ -71,6 +82,51 @@ public function print(Output $output): void
}
$output->writeLineFormatted('');
+ $thirdPartyIncludedConfigs = [];
+ foreach ($this->allConfigFiles as $configFile) {
+ $configFile = $this->fileHelper->normalizePath($configFile, '/');
+ foreach ($this->composerAutoloaderProjectPaths as $composerAutoloaderProjectPath) {
+ $composerConfig = ComposerHelper::getComposerConfig($composerAutoloaderProjectPath);
+ if ($composerConfig === null) {
+ continue;
+ }
+ $vendorDir = $this->fileHelper->normalizePath(ComposerHelper::getVendorDirFromComposerConfig($composerAutoloaderProjectPath, $composerConfig), '/');
+ if (!str_starts_with($configFile, $vendorDir)) {
+ continue;
+ }
+
+ $installedPath = $vendorDir . '/composer/installed.php';
+ if (!is_file($installedPath)) {
+ continue;
+ }
+
+ $installed = require $installedPath;
+
+ $trimmed = substr($configFile, strlen($vendorDir) + 1);
+ $parts = explode('/', $trimmed);
+ $package = implode('/', array_slice($parts, 0, 2));
+ $configPath = implode('/', array_slice($parts, 2));
+ if (!array_key_exists($package, $installed['versions'])) {
+ continue;
+ }
+
+ $packageVersion = $installed['versions'][$package]['pretty_version'] ?? null;
+ if ($packageVersion === null) {
+ continue;
+ }
+
+ $thirdPartyIncludedConfigs[] = [$package, $packageVersion, $configPath];
+ }
+ }
+
+ if (count($thirdPartyIncludedConfigs) > 0) {
+ $output->writeLineFormatted('Included configs from Composer packages:');
+ foreach ($thirdPartyIncludedConfigs as [$package, $packageVersion, $configPath]) {
+ $output->writeLineFormatted(sprintf('%s (%s): %s', $package, $configPath, $packageVersion));
+ }
+ $output->writeLineFormatted('');
+ }
+
$composerAutoloaderProjectPathsCount = count($this->composerAutoloaderProjectPaths);
$output->writeLineFormatted(sprintf(
'Discovered Composer project %s:',