diff --git a/composer.json b/composer.json index 4c6382c6d..df5ba318e 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "symfony/config": "^3.4|^4.0|^5.0", "symfony/console": "^3.4|^4.0|^5.0", "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/deprecation-contracts": "^2.3", "symfony/filesystem": "^3.4|^4.0|^5.0", "symfony/finder": "^3.4|^4.0|^5.0", "symfony/framework-bundle": "^3.4|^4.0|^5.0", diff --git a/src/Generator.php b/src/Generator.php index b56377639..83dee909f 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -14,6 +14,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; use Symfony\Bundle\MakerBundle\Util\ClassNameDetails; +use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; /** * @author Javier Eguiluz @@ -25,12 +26,21 @@ class Generator private $twigHelper; private $pendingOperations = []; private $namespacePrefix; + private $phpCompatUtil; - public function __construct(FileManager $fileManager, string $namespacePrefix) + public function __construct(FileManager $fileManager, string $namespacePrefix, PhpCompatUtil $phpCompatUtil = null) { $this->fileManager = $fileManager; $this->twigHelper = new GeneratorTwigHelper($fileManager); $this->namespacePrefix = trim($namespacePrefix, '\\'); + + if (null === $phpCompatUtil) { + $phpCompatUtil = new PhpCompatUtil($fileManager); + + trigger_deprecation('symfony/maker-bundle', '1.25', 'Initializing Generator without providing an instance of PhpCompatUtil is deprecated.'); + } + + $this->phpCompatUtil = $phpCompatUtil; } /** @@ -157,6 +167,7 @@ private function addOperation(string $targetPath, string $templateName, array $v } $variables['relative_path'] = $this->fileManager->relativizePath($targetPath); + $variables['use_attributes'] = $this->phpCompatUtil->canUseAttributes(); $templatePath = $templateName; if (!file_exists($templatePath)) { diff --git a/src/Maker/AbstractMaker.php b/src/Maker/AbstractMaker.php index 8017aa012..3d5a84841 100644 --- a/src/Maker/AbstractMaker.php +++ b/src/Maker/AbstractMaker.php @@ -48,9 +48,4 @@ protected function addDependencies(array $dependencies, string $message = null): $message ); } - - final protected function useAttributes(): bool - { - return \PHP_VERSION_ID >= 80000; - } } diff --git a/src/Maker/MakeController.php b/src/Maker/MakeController.php index fc769f635..be4ef1c2e 100644 --- a/src/Maker/MakeController.php +++ b/src/Maker/MakeController.php @@ -58,7 +58,6 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $controllerClassNameDetails->getFullName(), 'controller/Controller.tpl.php', [ - 'use_attributes' => $this->useAttributes(), 'route_path' => Str::asRoutePath($controllerClassNameDetails->getRelativeNameWithoutSuffix()), 'route_name' => Str::asRouteName($controllerClassNameDetails->getRelativeNameWithoutSuffix()), 'with_template' => $this->isTwigInstalled() && !$noTemplate, diff --git a/src/Maker/MakeCrud.php b/src/Maker/MakeCrud.php index de4b9a89a..be56c00a6 100644 --- a/src/Maker/MakeCrud.php +++ b/src/Maker/MakeCrud.php @@ -143,7 +143,6 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $controllerClassDetails->getFullName(), 'crud/controller/Controller.tpl.php', array_merge([ - 'use_attributes' => $this->useAttributes(), 'entity_full_class_name' => $entityClassDetails->getFullName(), 'entity_class_name' => $entityClassDetails->getShortName(), 'form_full_class_name' => $formClassDetails->getFullName(), diff --git a/src/Maker/MakeRegistrationForm.php b/src/Maker/MakeRegistrationForm.php index 84791959f..17d1a7cb0 100644 --- a/src/Maker/MakeRegistrationForm.php +++ b/src/Maker/MakeRegistrationForm.php @@ -258,7 +258,6 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $controllerClassNameDetails->getFullName(), 'registration/RegistrationController.tpl.php', [ - 'use_attributes' => $this->useAttributes(), 'route_path' => '/register', 'route_name' => 'app_register', 'form_class_name' => $formClassDetails->getShortName(), diff --git a/src/Maker/MakeResetPassword.php b/src/Maker/MakeResetPassword.php index 8a8df6800..b3de79c45 100644 --- a/src/Maker/MakeResetPassword.php +++ b/src/Maker/MakeResetPassword.php @@ -197,7 +197,6 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $controllerClassNameDetails->getFullName(), 'resetPassword/ResetPasswordController.tpl.php', [ - 'use_attributes' => $this->useAttributes(), 'user_full_class_name' => $userClassNameDetails->getFullName(), 'user_class_name' => $userClassNameDetails->getShortName(), 'request_form_type_full_class_name' => $requestFormTypeClassNameDetails->getFullName(), diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 872dc8027..3937fcf28 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -49,6 +49,7 @@ + @@ -63,5 +64,9 @@ + + + + diff --git a/src/Util/PhpCompatUtil.php b/src/Util/PhpCompatUtil.php new file mode 100644 index 000000000..bd221839a --- /dev/null +++ b/src/Util/PhpCompatUtil.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Util; + +use Symfony\Bundle\MakerBundle\FileManager; + +/** + * @author Jesse Rushlow + * + * @internal + */ +class PhpCompatUtil +{ + /** @var FileManager */ + private $fileManager; + + public function __construct(FileManager $fileManager) + { + $this->fileManager = $fileManager; + } + + public function canUseAttributes(): bool + { + $version = $this->getPhpVersion(); + + return version_compare($version, '8alpha', '>='); + } + + protected function getPhpVersion(): string + { + $rootDirectory = $this->fileManager->getRootDirectory(); + + $composerLockPath = sprintf('%s/composer.lock', $rootDirectory); + + if (!$this->fileManager->fileExists($composerLockPath)) { + return PHP_VERSION; + } + + $lockFileContents = json_decode($this->fileManager->getFileContents($composerLockPath), true); + + if (empty($lockFileContents['platform-overrides']) || empty($lockFileContents['platform-overrides']['php'])) { + return PHP_VERSION; + } + + return $lockFileContents['platform-overrides']['php']; + } +} diff --git a/tests/Command/MakerCommandTest.php b/tests/Command/MakerCommandTest.php index e1a1b3934..47ca2f362 100644 --- a/tests/Command/MakerCommandTest.php +++ b/tests/Command/MakerCommandTest.php @@ -17,6 +17,7 @@ use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\MakerInterface; +use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; use Symfony\Component\Console\Tester\CommandTester; class MakerCommandTest extends TestCase @@ -35,7 +36,9 @@ public function testExceptionOnMissingDependencies() $fileManager = $this->createMock(FileManager::class); - $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'App')); + $mockPhpCompatUtil = $this->createMock(PhpCompatUtil::class); + + $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'App', $mockPhpCompatUtil)); // needed because it's normally set by the Application $command->setName('make:foo'); $tester = new CommandTester($command); @@ -48,7 +51,9 @@ public function testExceptionOnUnknownRootNamespace() $fileManager = $this->createMock(FileManager::class); - $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'Unknown')); + $mockPhpCompatUtil = $this->createMock(PhpCompatUtil::class); + + $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'Unknown', $mockPhpCompatUtil)); // needed because it's normally set by the Application $command->setName('make:foo'); $tester = new CommandTester($command); diff --git a/tests/Doctrine/EntityRegeneratorTest.php b/tests/Doctrine/EntityRegeneratorTest.php index 3941264ef..247746a71 100644 --- a/tests/Doctrine/EntityRegeneratorTest.php +++ b/tests/Doctrine/EntityRegeneratorTest.php @@ -23,6 +23,7 @@ use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\Util\AutoloaderUtil; use Symfony\Bundle\MakerBundle\Util\MakerFileLinkFormatter; +use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Filesystem\Filesystem; @@ -113,7 +114,8 @@ private function doTestRegeneration(string $sourceDir, Kernel $kernel, string $n $fileManager = new FileManager($fs, $autoloaderUtil, new MakerFileLinkFormatter(null), $tmpDir); $doctrineHelper = new DoctrineHelper('App\\Entity', $container->get('doctrine')); - $generator = new Generator($fileManager, 'App\\'); + $phpCompatUtil = new PhpCompatUtil($fileManager); + $generator = new Generator($fileManager, 'App\\', $phpCompatUtil); $entityClassGenerator = new EntityClassGenerator($generator, $doctrineHelper); $entityClassGenerator->setMangerRegistryClassName(ManagerRegistry::class); $regenerator = new EntityRegenerator( diff --git a/tests/GeneratorTest.php b/tests/GeneratorTest.php index 9027aab8d..5a7d0882d 100644 --- a/tests/GeneratorTest.php +++ b/tests/GeneratorTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Generator; +use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; class GeneratorTest extends TestCase { @@ -26,7 +27,11 @@ public function testCreateClassNameDetails(string $name, string $prefix, string $fileManager->expects($this->any()) ->method('getNamespacePrefixForClass') ->willReturn('Foo'); - $generator = new Generator($fileManager, 'App\\'); + + $mockPhpCompatUtil = $this->createMock(PhpCompatUtil::class); + + $generator = new Generator($fileManager, 'App\\', $mockPhpCompatUtil); + $classNameDetails = $generator->createClassNameDetails($name, $prefix, $suffix); $this->assertSame($expectedFullClassName, $classNameDetails->getFullName()); diff --git a/tests/Util/PhpVersionTest.php b/tests/Util/PhpVersionTest.php new file mode 100644 index 000000000..84756cf4f --- /dev/null +++ b/tests/Util/PhpVersionTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Tests\Util; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\MakerBundle\FileManager; +use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; + +/** + * @author Jesse Rushlow + */ +class PhpVersionTest extends TestCase +{ + /** + * @dataProvider phpVersionDataProvider + */ + public function testUsesPhpPlatformFromComposerJsonFile(string $version, bool $expectedResult): void + { + $json = sprintf('{"platform-overrides": {"php": "%s"}}', $version); + + $mockFileManager = $this->createMock(FileManager::class); + $mockFileManager + ->expects(self::once()) + ->method('getRootDirectory') + ->willReturn('/test') + ; + + $mockFileManager + ->expects(self::once()) + ->method('fileExists') + ->with('/test/composer.lock') + ->willReturn(true) + ; + + $mockFileManager + ->expects(self::once()) + ->method('getFileContents') + ->with('/test/composer.lock') + ->willReturn($json) + ; + + $version = new PhpCompatUtil($mockFileManager); + + $result = $version->canUseAttributes(); + + self::assertSame($expectedResult, $result); + } + + public function phpVersionDataProvider(): \Generator + { + yield ['8', true]; + yield ['8.0', true]; + yield ['8.0.1', true]; + yield ['8RC1', true]; + yield ['8.1alpha1', true]; + yield ['8.0.0beta2', true]; + yield ['8beta', true]; + yield ['7', false]; + yield ['7.0', false]; + yield ['7.1.1', false]; + yield ['7.0.0RC3', false]; + } + + public function testFallBackToPhpVersionWithoutLockFile(): void + { + $mockFileManager = $this->createMock(FileManager::class); + $mockFileManager + ->expects(self::once()) + ->method('getRootDirectory') + ->willReturn('/test') + ; + + $mockFileManager + ->expects(self::once()) + ->method('fileExists') + ->with('/test/composer.lock') + ->willReturn(false) + ; + + $mockFileManager + ->expects(self::never()) + ->method('getFileContents') + ; + + $util = new PhpCompatUtilTestFixture($mockFileManager); + + $result = $util->getVersionForTest(); + + self::assertSame(PHP_VERSION, $result); + } + + public function testWithoutPlatformVersionSet(): void + { + $json = '{"platform-overrides": {}}'; + + $mockFileManager = $this->createMock(FileManager::class); + $mockFileManager + ->expects(self::once()) + ->method('getRootDirectory') + ->willReturn('/test') + ; + + $mockFileManager + ->expects(self::once()) + ->method('fileExists') + ->with('/test/composer.lock') + ->willReturn(true) + ; + + $mockFileManager + ->expects(self::once()) + ->method('getFileContents') + ->with('/test/composer.lock') + ->willReturn($json) + ; + + $util = new PhpCompatUtilTestFixture($mockFileManager); + + $result = $util->getVersionForTest(); + + self::assertSame(PHP_VERSION, $result); + } +} + +class PhpCompatUtilTestFixture extends PhpCompatUtil +{ + public function getVersionForTest(): string + { + return $this->getPhpVersion(); + } +}