diff --git a/.gitignore b/.gitignore index abf0e33..279b871 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ composer.phar -composer.lock +/composer.lock /vendor/ .idea diff --git a/README.md b/README.md index 92f50a8..780be9b 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,24 @@ This library allows to easily detect the PHP stack (Wordpress, Laravel, Symfony Supported Stacks for now: -- Wordpress +- Bolt CMS +- Cakephp +- Codeigniter +- Craft CMS +- Drupal +- Grav CMS - Laravel -- Symfony +- Leaf PHP +- Lunar +- October CMS +- Shopware - Statamic -- Craft CMS +- Symfony +- Tempest +- Twill +- Typo3 CMS +- Winter CMS +- Wordpress ## Install @@ -32,7 +45,17 @@ use Einenlum\PhpStackDetector\StackType; $factory = new FilesystemDetectorFactory(); $detector = $factory->create(); -$stack = $detector->getStack('/path/to/a/symfony/directory'); +$config = $detector->getFullConfiguration('/path/to/a/symfony/directory'); + +$phpConfiguration = $config->phpConfiguration; + +// value from config platform php +$phpConfiguration->phpVersion->version; // 8.3 +// value from require php +$phpConfiguration->phpVersion->requirements; // ^7.2|^8.0 +$phpConfiguration->requiredExtensions; // ['intl', 'curl'] + +$stack = $config->stack; $stack->type === StackType::SYMFONY; $stack->version; // 5.4 @@ -49,7 +72,16 @@ $stack; // null $factory = new GithubDetectorFactory(); $detector = $factory->create(); -$stack = $detector->getStack('symfony/demo'); +$config = $detector->getFullConfiguration('/path/to/a/symfony/directory'); + +$phpConfiguration = $config->phpConfiguration; +// value from config platform php +$phpConfiguration->phpVersion->version; // 8.3 +// value from require php +$phpConfiguration->phpVersion->requirements; // ^7.2|^8.0 +$phpConfiguration->requiredExtensions; // ['intl', 'curl'] + +$stack = $config->stack; $stack->type === StackType::SYMFONY; $stack->version; // 6.3.0 @@ -59,13 +91,10 @@ $client = new \Github\Client(); $client->authenticate('some_access_token', null, \Github\AuthMethod::ACCESS_TOKEN); $detector = $factory->create($client); -$stack = $detector->getStack('einenlum/private-repo'); +$config = $detector->getFullConfiguration('einenlum/private-repo'); // optionally: detect the stack on a specific branch -$stack = $detector->getStack('einenlum/private-repo:branch-name'); - -$stack->type === StackType::SYMFONY; -$stack->version; // 6.3.0 +$config = $detector->getFullConfiguration('einenlum/private-repo:branch-name'); ``` You can also use the CLI to test it. diff --git a/bin/detect-github.php b/bin/detect-github.php index bb00235..2eb25d4 100644 --- a/bin/detect-github.php +++ b/bin/detect-github.php @@ -1,8 +1,7 @@ authenticate($accessToken, null, AuthMethod::ACCESS_TOKEN); $detector = $factory->create($client); } else { @@ -19,18 +18,25 @@ $directory = $argv[1] ?? null; if (null === $directory) { - echo 'Please provide a directory to scan' . "\n"; + echo 'Please provide a directory to scan'."\n"; exit(1); } $subDirectory = $argv[2] ?? null; -$stack = $detector->getStack($directory, $subDirectory); +$config = $detector->getFullConfiguration($directory, $subDirectory); + +$phpConfig = $config->phpConfiguration; +echo 'Detected PHP version: '.($phpConfig->phpVersion?->version ?: 'Unknown version')."\n"; +echo 'Detected PHP requirements: '.($phpConfig->phpVersion?->requirements ?: 'Unknown version')."\n"; +echo 'Required extensions: '.(empty($phpConfig->requiredExtensions) ? 'None' : implode(', ', $phpConfig->requiredExtensions))."\n"; + +$stack = $config->stack; if (null === $stack) { - echo 'No stack detected' . "\n"; + echo 'No stack detected'."\n"; exit(0); } -echo 'Detected stack: ' . $stack->type->value . "\n"; -echo 'Version: ' . ($stack->version ?: 'Unknown version') . "\n"; +echo 'Detected stack: '.$stack->type->value."\n"; +echo 'Version: '.($stack->version ?: 'Unknown version')."\n"; diff --git a/bin/detect-local.php b/bin/detect-local.php index 0f81317..26ac107 100644 --- a/bin/detect-local.php +++ b/bin/detect-local.php @@ -1,6 +1,6 @@ getStack($directory, $subDirectory); +$config = $detector->getFullConfiguration($directory, $subDirectory); + +$phpConfig = $config->phpConfiguration; +echo 'Detected PHP version: '.($phpConfig->phpVersion?->version ?: 'Unknown version')."\n"; +echo 'Detected PHP requirements: '.($phpConfig->phpVersion?->requirements ?: 'Unknown version')."\n"; +echo 'Required extensions: '.(empty($phpConfig->requiredExtensions) ? 'None' : implode(', ', $phpConfig->requiredExtensions))."\n"; + +$stack = $config->stack; if (null === $stack) { - echo 'No stack detected' . "\n"; + echo 'No stack detected'."\n"; exit(0); } -echo 'Detected stack: ' . $stack->type->value . "\n"; -echo 'Version: ' . ($stack->version ?: 'Unknown version') . "\n"; +echo 'Detected stack: '.$stack->type->value."\n"; +echo 'Version: '.($stack->version ?: 'Unknown version')."\n"; diff --git a/composer.json b/composer.json index d10b5cc..095ccb9 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "nyholm/psr7": "^1.8" }, "scripts": { - "unit-test": "phpunit tests", + "unit-test": "phpunit tests --display-warnings", "static-analysis": "phpstan", "test-cs": "./vendor/bin/php-cs-fixer fix --dry-run --diff -v", "fix-cs": "./vendor/bin/php-cs-fixer fix --diff -v", diff --git a/lib/Composer/ComposerConfigProvider.php b/lib/Composer/ComposerConfigProvider.php index c733bb4..1c4a22d 100644 --- a/lib/Composer/ComposerConfigProvider.php +++ b/lib/Composer/ComposerConfigProvider.php @@ -8,6 +8,10 @@ use Einenlum\PhpStackDetector\Exception\CacheMissException; use Einenlum\PhpStackDetector\Exception\ResourceNotFoundException; +/** + * We use an array cache so that we don't make expensive calls to the adapter + * each time. This provider is used in multiple places during a single run. + */ class ComposerConfigProvider { /** @var array */ @@ -18,56 +22,41 @@ public function __construct( ) { } + /** + * This returns the content of the composer lock file if it exists, + * otherwise the composer json file if it exists, otherwise null. + */ public function getComposerConfig( + ComposerConfigType $type, string $baseUri, ?string $subDirectory, ): ?ComposerConfig { try { - return $this->getFromCache($baseUri, $subDirectory); + return $this->getFromCache($type, $baseUri, $subDirectory); } catch (CacheMissException) { } - $lockContent = $this->getConfig( + $content = $this->getFileContent( + $type, $baseUri, $subDirectory, - 'composer.lock' ); - if (null !== $lockContent) { - $config = new ComposerConfig( - ComposerConfigType::LOCK, - $lockContent - ); - $this->setToCache($baseUri, $subDirectory, $config); - - return $config; - } - - $jsonContent = $this->getConfig( - $baseUri, - $subDirectory, - 'composer.json' - ); - - if (null !== $jsonContent) { - $config = new ComposerConfig( - ComposerConfigType::JSON, - $jsonContent - ); - - $this->setToCache($baseUri, $subDirectory, $config); - - return $config; - } + $config = $content ? new ComposerConfig($type, $content) : null; - $this->setToCache($baseUri, $subDirectory, null); + $this->setToCache($type, $baseUri, $subDirectory, $config); - return null; + return $config; } /** @return array|null */ - private function getConfig(string $baseUri, ?string $subDirectory, string $filename): ?array - { + private function getFileContent( + ComposerConfigType $type, + string $baseUri, + ?string $subDirectory, + ): ?array { + $filename = ComposerConfigType::LOCK === $type ? 'composer.lock' : 'composer.json'; + if (!$this->adapter->directoryExists($baseUri, $subDirectory)) { return null; } @@ -91,11 +80,12 @@ private function getConfig(string $baseUri, ?string $subDirectory, string $filen } private function setToCache( + ComposerConfigType $type, string $baseUri, ?string $subDirectory, ?ComposerConfig $config, ): void { - $cacheKey = $this->getCacheKey($baseUri, $subDirectory); + $cacheKey = $this->getCacheKey($type, $baseUri, $subDirectory); $this->cache[$cacheKey] = $config; } @@ -103,9 +93,12 @@ private function setToCache( /** * @throws CacheMissException */ - private function getFromCache(string $baseUri, ?string $subDirectory): ?ComposerConfig - { - $cacheKey = $this->getCacheKey($baseUri, $subDirectory); + private function getFromCache( + ComposerConfigType $type, + string $baseUri, + ?string $subDirectory, + ): ?ComposerConfig { + $cacheKey = $this->getCacheKey($type, $baseUri, $subDirectory); if (!array_key_exists($cacheKey, $this->cache)) { throw new CacheMissException(); @@ -114,8 +107,11 @@ private function getFromCache(string $baseUri, ?string $subDirectory): ?Composer return $this->cache[$cacheKey]; } - private function getCacheKey(string $baseUri, ?string $subDirectory): string - { - return $baseUri.$subDirectory; + private function getCacheKey( + ComposerConfigType $type, + string $baseUri, + ?string $subDirectory, + ): string { + return $baseUri.$subDirectory.':'.$type->value; } } diff --git a/lib/Composer/ComposerConfigType.php b/lib/Composer/ComposerConfigType.php index 9a932ab..79d3b2a 100644 --- a/lib/Composer/ComposerConfigType.php +++ b/lib/Composer/ComposerConfigType.php @@ -4,8 +4,8 @@ namespace Einenlum\PhpStackDetector\Composer; -enum ComposerConfigType +enum ComposerConfigType: string { - case JSON; - case LOCK; + case JSON = 'json'; + case LOCK = 'lock'; } diff --git a/lib/Composer/PackageVersionProvider.php b/lib/Composer/PackageVersionProvider.php index 1a59b71..b886897 100644 --- a/lib/Composer/PackageVersionProvider.php +++ b/lib/Composer/PackageVersionProvider.php @@ -18,14 +18,15 @@ public function getVersionForPackage( ?string $subDirectory, array $packageNames, ): ?PackageVersion { - $config = $this->configProvider->getComposerConfig($baseUri, $subDirectory); - if (null === $config) { - return null; - } + $lock = $this->configProvider->getComposerConfig( + ComposerConfigType::LOCK, + $baseUri, + $subDirectory + ); - if (ComposerConfigType::LOCK === $config->type) { + if ($lock) { foreach ($packageNames as $packageName) { - foreach ($config->content['packages'] as $package) { + foreach ($lock->content['packages'] as $package) { if ($package['name'] === $packageName) { return new PackageVersion( null, @@ -38,6 +39,16 @@ public function getVersionForPackage( return null; } + $config = $this->configProvider->getComposerConfig( + ComposerConfigType::JSON, + $baseUri, + $subDirectory + ); + + if (null === $config || !isset($config->content['require'])) { + return null; + } + foreach ($packageNames as $name) { foreach ($config->content['require'] as $packageName => $requirement) { if ($packageName === $name) { diff --git a/lib/Detector.php b/lib/Detector.php index 9efa1a7..c794f04 100644 --- a/lib/Detector.php +++ b/lib/Detector.php @@ -7,8 +7,28 @@ readonly class Detector { /** @param StackDetectorInterface[] $stackDetectors */ - public function __construct(private array $stackDetectors) + public function __construct( + private PhpConfigurationDetector $phpConfigurationDetector, + private array $stackDetectors, + ) { + } + + public function getFullConfiguration(string $baseUri, ?string $subFolder = null): FullConfiguration + { + $subFolder = $this->cleanSubFolder($subFolder); + + $phpConfiguration = $this->getPhpConfiguration($baseUri, $subFolder); + $stack = $this->getStack($baseUri, $subFolder); + + return new FullConfiguration( + $phpConfiguration, + $stack, + ); + } + + private function getPhpConfiguration(string $baseUri, ?string $subFolder = null): PhpConfiguration { + return $this->phpConfigurationDetector->getPhpConfiguration($baseUri, $subFolder); } /** @@ -19,10 +39,8 @@ public function __construct(private array $stackDetectors) * or * symfony/demo:v1.1 for a remote Github repository with a reference */ - public function getStack(string $baseUri, ?string $subFolder = null): ?Stack + private function getStack(string $baseUri, ?string $subFolder = null): ?Stack { - $subFolder = $this->cleanSubFolder($subFolder); - foreach ($this->stackDetectors as $stackDetector) { $stack = $stackDetector->getStack($baseUri, $subFolder); diff --git a/lib/Factory/FilesystemDetectorFactory.php b/lib/Factory/FilesystemDetectorFactory.php index aed5b87..18d92ce 100644 --- a/lib/Factory/FilesystemDetectorFactory.php +++ b/lib/Factory/FilesystemDetectorFactory.php @@ -4,8 +4,10 @@ namespace Einenlum\PhpStackDetector\Factory; +use Einenlum\PhpStackDetector\Composer\ComposerConfigProvider; use Einenlum\PhpStackDetector\Detector; use Einenlum\PhpStackDetector\DirectoryCrawler\FilesystemAdapter; +use Einenlum\PhpStackDetector\PhpConfigurationDetector; class FilesystemDetectorFactory { @@ -13,8 +15,12 @@ class FilesystemDetectorFactory public function create(): Detector { - $stackDetectors = $this->getStackDetectors(new FilesystemAdapter()); + $adapter = new FilesystemAdapter(); + $composerConfigProvider = new ComposerConfigProvider($adapter); - return new Detector($stackDetectors); + $phpConfigurationDetector = new PhpConfigurationDetector($composerConfigProvider); + $stackDetectors = $this->getStackDetectors($composerConfigProvider, $adapter); + + return new Detector($phpConfigurationDetector, $stackDetectors); } } diff --git a/lib/Factory/GithubDetectorFactory.php b/lib/Factory/GithubDetectorFactory.php index 12cfdf6..ba4e8b4 100644 --- a/lib/Factory/GithubDetectorFactory.php +++ b/lib/Factory/GithubDetectorFactory.php @@ -4,8 +4,10 @@ namespace Einenlum\PhpStackDetector\Factory; +use Einenlum\PhpStackDetector\Composer\ComposerConfigProvider; use Einenlum\PhpStackDetector\Detector; use Einenlum\PhpStackDetector\DirectoryCrawler\GithubAdapter; +use Einenlum\PhpStackDetector\PhpConfigurationDetector; use Github\Client; class GithubDetectorFactory @@ -16,9 +18,11 @@ public function create(?Client $client = null): Detector { $client = $client ?: new Client(); $adapter = new GithubAdapter($client); + $composerConfigProvider = new ComposerConfigProvider($adapter); + $phpConfigurationDetector = new PhpConfigurationDetector($composerConfigProvider); - $stackDetectors = $this->getStackDetectors($adapter); + $stackDetectors = $this->getStackDetectors($composerConfigProvider, $adapter); - return new Detector($stackDetectors); + return new Detector($phpConfigurationDetector, $stackDetectors); } } diff --git a/lib/Factory/HasStackDetectors.php b/lib/Factory/HasStackDetectors.php index ef442dc..d360b17 100644 --- a/lib/Factory/HasStackDetectors.php +++ b/lib/Factory/HasStackDetectors.php @@ -30,8 +30,10 @@ trait HasStackDetectors { /** @return StackDetectorInterface[] */ - private function getStackDetectors(AdapterInterface $adapter): array - { + private function getStackDetectors( + ComposerConfigProvider $composerConfigProvider, + AdapterInterface $adapter, + ): array { $composerConfigProvider = new ComposerConfigProvider($adapter); $packageVersionProvider = new PackageVersionProvider($composerConfigProvider); diff --git a/lib/FullConfiguration.php b/lib/FullConfiguration.php new file mode 100644 index 0000000..575e3e2 --- /dev/null +++ b/lib/FullConfiguration.php @@ -0,0 +1,14 @@ +composerConfigProvider->getComposerConfig( + ComposerConfigType::JSON, + $baseUri, + $subFolder + ); + + if (null === $composerConfig) { + return new PhpConfiguration(null, []); + } + + $phpVersion = $this->getPhpVersion($composerConfig); + $requiredExtensions = $this->getRequiredExtensions($composerConfig); + + return new PhpConfiguration($phpVersion, $requiredExtensions); + } + + private function getPhpVersion(ComposerConfig $config): ?PhpVersion + { + $content = $config->content; + if (isset($content['config']['platform']['php'])) { + $configPlatformVersion = $content['config']['platform']['php']; + } + + if (isset($content['require']['php'])) { + $phpVersionRequirements = $content['require']['php']; + } + + if (!isset($configPlatformVersion) && !isset($phpVersionRequirements)) { + return null; + } + + return new PhpVersion( + $configPlatformVersion ?? null, + $phpVersionRequirements ?? null + ); + } + + /** @return string[] */ + private function getRequiredExtensions(ComposerConfig $config): array + { + $content = $config->content; + if (!isset($content['require'])) { + return []; + } + + $extensions = []; + foreach ($content['require'] as $package => $version) { + if (str_starts_with($package, 'ext-')) { + $extensions[] = substr($package, 4); + } + } + + return $extensions; + } +} diff --git a/lib/PhpVersion.php b/lib/PhpVersion.php new file mode 100644 index 0000000..a8e0ded --- /dev/null +++ b/lib/PhpVersion.php @@ -0,0 +1,14 @@ +sut->getComposerConfig( + ComposerConfigType::LOCK, __DIR__, - '../../fixtures/composer-lock', + '../../fixtures/composer-config/composer-lock', ); $this->assertInstanceOf(ComposerConfig::class, $config); @@ -40,11 +41,24 @@ public function it_returns_a_lock_config_if_present(): void } /** @test */ - public function it_returns_a_json_config_if_no_lock_is_present(): void + public function it_returns_null_if_no_lock_config_if_present(): void { $config = $this->sut->getComposerConfig( + ComposerConfigType::LOCK, __DIR__, - '../../fixtures/composer-json', + '../../fixtures/composer-config/composer-json', + ); + + $this->assertNull($config); + } + + /** @test */ + public function it_returns_a_json_config_if_present(): void + { + $config = $this->sut->getComposerConfig( + ComposerConfigType::JSON, + __DIR__, + '../../fixtures/composer-config/composer-json', ); $this->assertInstanceOf(ComposerConfig::class, $config); @@ -54,4 +68,16 @@ public function it_returns_a_json_config_if_no_lock_is_present(): void $this->assertArrayHasKey('license', $config->content); $this->assertArrayHasKey('require', $config->content); } + + /** @test */ + public function it_returns_null_if_no_json_config_if_present(): void + { + $config = $this->sut->getComposerConfig( + ComposerConfigType::JSON, + __DIR__, + '../../fixtures/composer-config/composer-lock', + ); + + $this->assertNull($config); + } } diff --git a/tests/Unit/DetectorTest.php b/tests/Unit/DetectorTest.php index e0b60bb..280b739 100644 --- a/tests/Unit/DetectorTest.php +++ b/tests/Unit/DetectorTest.php @@ -26,13 +26,124 @@ public function setUp(): void */ public function it_detects_stack(string $fixtureFolder, ?string $expectedVersion, StackType $expectedType): void { - $stack = $this->sut->getStack(sprintf('%s/../fixtures/%s', __DIR__, $fixtureFolder)); + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, $fixtureFolder) + ); + $this->assertNotNull($fullConfig); + + $stack = $fullConfig->stack; $this->assertNotNull($stack); $this->assertSame($expectedType, $stack->type); $this->assertSame($expectedVersion, $stack->version); } + /** @test */ + public function it_detects_config_platform_php_version() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/platform-config') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame('8.4.3', $fullConfig->phpConfiguration->phpVersion->version); + } + + /** @test */ + public function it_detects_no_php_version_if_config_platform_is_not_present() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/php-requirements') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame(null, $fullConfig->phpConfiguration->phpVersion->version); + } + + /** @test */ + public function it_detects_php_requirements() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/php-requirements') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame( + '>=7.4|^8.0|^8.1|^8.2', + $fullConfig->phpConfiguration->phpVersion->requirements + ); + } + + /** @test */ + public function it_detects_no_php_requirements_if_not_present() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/platform-config') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame(null, $fullConfig->phpConfiguration->phpVersion->requirements); + } + + /** @test */ + public function it_detects_both_config_platform_version_and_requirements() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/php-requirements-and-platform-config') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame('8.4.3', $fullConfig->phpConfiguration->phpVersion->version); + $this->assertSame( + '>=7.4|^8.0|^8.1|^8.2', + $fullConfig->phpConfiguration->phpVersion->requirements + ); + } + + /** @test */ + public function it_returns_null_for_php_version_dto_if_no_information_is_available() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/empty') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertNull($fullConfig->phpConfiguration->phpVersion); + } + + /** @test */ + public function it_detects_extensions_if_in_require() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/php-extensions') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame( + ['json', 'mbstring', 'curl'], + $fullConfig->phpConfiguration->requiredExtensions + ); + } + + /** @test */ + public function it_detects_no_extension_if_not_in_require() + { + $fullConfig = $this->sut->getFullConfiguration( + sprintf('%s/../fixtures/%s', __DIR__, 'php-config/detector/no-platform-config') + ); + + $this->assertNotNull($fullConfig); + $this->assertNotNull($fullConfig->phpConfiguration); + $this->assertSame([], $fullConfig->phpConfiguration->requiredExtensions); + } + public static function packagesDataProvider(): array { return [ diff --git a/tests/fixtures/composer-config/composer-json/composer.json b/tests/fixtures/composer-config/composer-json/composer.json new file mode 100644 index 0000000..0ac81ac --- /dev/null +++ b/tests/fixtures/composer-config/composer-json/composer.json @@ -0,0 +1,66 @@ +{ + "type": "project", + "license": "proprietary", + "minimum-stability": "stable", + "prefer-stable": true, + "require": { + "symfony/framework-bundle": "6.3.*", + "symfony/runtime": "6.3.*", + "symfony/yaml": "6.3.*", + "lorem/ipsum": "2.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/browser-kit": "6.3.*", + "symfony/css-selector": "6.3.*", + "symfony/phpunit-bridge": "^6.3" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "App\\Tests\\": "tests/" + } + }, + "replace": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*" + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" + }, + "post-install-cmd": [ + "@auto-scripts" + ], + "post-update-cmd": [ + "@auto-scripts" + ] + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "6.3.*" + } + } +} diff --git a/tests/fixtures/composer-config/composer-lock/composer.lock b/tests/fixtures/composer-config/composer-lock/composer.lock new file mode 100644 index 0000000..4b3dcbf --- /dev/null +++ b/tests/fixtures/composer-config/composer-lock/composer.lock @@ -0,0 +1,316 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "0ff5bf63dfe00c079888b47286b794c9", + "packages": [ + { + "name": "someframework/foo", + "version": "v2.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-19T20:22:16+00:00" + }, + { + "name": "symfony/config", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-19T20:22:16+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "567cafcfc08e3076b47290a7558b0ca17a98b0ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/567cafcfc08e3076b47290a7558b0ca17a98b0ce", + "reference": "567cafcfc08e3076b47290a7558b0ca17a98b0ce", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.1", + "symfony/cache": "^5.4|^6.0", + "symfony/config": "^6.1", + "symfony/dependency-injection": "^6.3.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.1", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-foundation": "^6.3", + "symfony/http-kernel": "^6.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^5.4|^6.0" + }, + "conflict": { + "doctrine/annotations": "<1.13.1", + "doctrine/persistence": "<1.3", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/asset": "<5.4", + "symfony/clock": "<6.3", + "symfony/console": "<5.4", + "symfony/dom-crawler": "<6.3", + "symfony/dotenv": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<6.3", + "symfony/lock": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<6.3", + "symfony/mime": "<6.2", + "symfony/property-access": "<5.4", + "symfony/property-info": "<5.4", + "symfony/security-core": "<5.4", + "symfony/security-csrf": "<5.4", + "symfony/serializer": "<6.3", + "symfony/stopwatch": "<5.4", + "symfony/translation": "<6.2.8", + "symfony/twig-bridge": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/validator": "<6.3", + "symfony/web-profiler-bundle": "<5.4", + "symfony/workflow": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.13.1|^2", + "doctrine/persistence": "^1.3|^2|^3", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^5.4|^6.0", + "symfony/asset-mapper": "^6.3", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/clock": "^6.2", + "symfony/console": "^5.4.9|^6.0.9", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dom-crawler": "^6.3", + "symfony/dotenv": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/html-sanitizer": "^6.1", + "symfony/http-client": "^6.3", + "symfony/lock": "^5.4|^6.0", + "symfony/mailer": "^5.4|^6.0", + "symfony/messenger": "^6.3", + "symfony/mime": "^6.2", + "symfony/notifier": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/rate-limiter": "^5.4|^6.0", + "symfony/scheduler": "^6.3", + "symfony/security-bundle": "^5.4|^6.0", + "symfony/semaphore": "^5.4|^6.0", + "symfony/serializer": "^6.3", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/string": "^5.4|^6.0", + "symfony/translation": "^6.2.8", + "symfony/twig-bundle": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^6.3", + "symfony/web-link": "^5.4|^6.0", + "symfony/workflow": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0", + "twig/twig": "^2.10|^3.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/framework-bundle/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-29T10:45:15+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.1", + "ext-ctype": "*", + "ext-iconv": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/tests/fixtures/php-config/detector/empty/composer.json b/tests/fixtures/php-config/detector/empty/composer.json new file mode 100644 index 0000000..8ee44ea --- /dev/null +++ b/tests/fixtures/php-config/detector/empty/composer.json @@ -0,0 +1,3 @@ +{ + "name": "cool/package" +} diff --git a/tests/fixtures/php-config/detector/php-extensions/composer.json b/tests/fixtures/php-config/detector/php-extensions/composer.json new file mode 100644 index 0000000..2d3cd92 --- /dev/null +++ b/tests/fixtures/php-config/detector/php-extensions/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-curl": "*" + } +} diff --git a/tests/fixtures/php-config/detector/php-requirements-and-platform-config/composer.json b/tests/fixtures/php-config/detector/php-requirements-and-platform-config/composer.json new file mode 100644 index 0000000..569997a --- /dev/null +++ b/tests/fixtures/php-config/detector/php-requirements-and-platform-config/composer.json @@ -0,0 +1,10 @@ +{ + "require": { + "php": ">=7.4|^8.0|^8.1|^8.2" + }, + "config": { + "platform": { + "php": "8.4.3" + } + } +} diff --git a/tests/fixtures/php-config/detector/php-requirements/composer.json b/tests/fixtures/php-config/detector/php-requirements/composer.json new file mode 100644 index 0000000..c84c078 --- /dev/null +++ b/tests/fixtures/php-config/detector/php-requirements/composer.json @@ -0,0 +1,6 @@ +{ + "name": "cool/package", + "require": { + "php": ">=7.4|^8.0|^8.1|^8.2" + } +} diff --git a/tests/fixtures/php-config/detector/platform-config/composer.json b/tests/fixtures/php-config/detector/platform-config/composer.json new file mode 100644 index 0000000..cd1c87a --- /dev/null +++ b/tests/fixtures/php-config/detector/platform-config/composer.json @@ -0,0 +1,7 @@ +{ + "config": { + "platform": { + "php": "8.4.3" + } + } +}