diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index fe887ca9c97bd..65004b9e9fa74 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -19,6 +19,7 @@ CHANGELOG * Made `BrowserKitAssertionsTrait` report the original error message in case of a failure * Added ability for `config:dump-reference` and `debug:config` to dump and debug kernel container extension configuration. * Deprecated `session.attribute_bag` service and `session.flash_bag` service. + * The `uid:generate` command from the Uid Component is now registered. 5.0.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ce65523745bd5..d4cafc773ea3f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -124,6 +124,7 @@ use Symfony\Component\String\Slugger\SluggerInterface; use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand; use Symfony\Component\Translation\Translator; +use Symfony\Component\Uid\Command\UidGenerateCommand; use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader; use Symfony\Component\Validator\ObjectInitializerInterface; @@ -187,6 +188,9 @@ public function load(array $configs, ContainerBuilder $container) if (!class_exists(BaseYamlLintCommand::class)) { $container->removeDefinition('console.command.yaml_lint'); } + if (!class_exists(UidGenerateCommand::class)) { + $container->removeDefinition('console.command.uid_generate'); + } } // Load Cache configuration first as it is used by other components diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml index 3ef3108b45d49..833b34f1eaadf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.xml @@ -174,6 +174,10 @@ + + + + diff --git a/src/Symfony/Component/Uid/CHANGELOG.md b/src/Symfony/Component/Uid/CHANGELOG.md index 70a35a92916ff..3ff8db2f91969 100644 --- a/src/Symfony/Component/Uid/CHANGELOG.md +++ b/src/Symfony/Component/Uid/CHANGELOG.md @@ -7,3 +7,4 @@ CHANGELOG * added support for UUID * added support for ULID * added the component + * added `uid:generate` command to generate UUIDs and ULIDs diff --git a/src/Symfony/Component/Uid/Command/UidGenerateCommand.php b/src/Symfony/Component/Uid/Command/UidGenerateCommand.php new file mode 100644 index 0000000000000..4986e51dc65dc --- /dev/null +++ b/src/Symfony/Component/Uid/Command/UidGenerateCommand.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Uid\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Uid\Ulid; +use Symfony\Component\Uid\Uuid; + +class UidGenerateCommand extends Command +{ + protected static $defaultName = 'uid:generate'; + + private const UUID_1 = 'uuid-1'; + private const UUID_3 = 'uuid-3'; + private const UUID_4 = 'uuid-4'; + private const UUID_5 = 'uuid-5'; + private const UUID_6 = 'uuid-6'; + private const ULID = 'ulid'; + + private const TYPES = [ + self::UUID_1, + self::UUID_3, + self::UUID_4, + self::UUID_5, + self::UUID_6, + self::ULID, + ]; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $typesAsString = implode(', ', self::TYPES); + + $this + ->setDefinition([ + new InputArgument('type', InputArgument::REQUIRED, 'The type/version of the generated UID.'), + new InputArgument('namespace', InputArgument::OPTIONAL, 'Namespace for UUID V3 and V5 versions.', ''), + new InputArgument('name', InputArgument::OPTIONAL, 'Name for UUID V3 and V5 versions.', ''), + new InputOption('base32', null, InputOption::VALUE_NONE, 'Use this option to represent the generated UUID/ULID in base 32.'), + new InputOption('base58', null, InputOption::VALUE_NONE, 'Use this option to represent the generated UUID/ULID in base 58.'), + ]) + ->setDescription('Generates a UID, that can be either a ULID or a UUID in a given version.') + ->setHelp(<<%command.name% generates UID. This can be a ULID or a UUID +in a given version. Available types are $typesAsString. +Examples: + + php %command.full_name% ulid for generating a ULID. + php %command.full_name% uuid-1 for generating a UUID in version 1. + php %command.full_name% uuid-3 9b7541de-6f87-11ea-ab3c-9da9a81562fc foo for generating a UUID in version 3. + php %command.full_name% uuid-4 --base32 for generating a UUID in version 4 represented in base 32. + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $type = $input->getArgument('type'); + $namespace = $input->getArgument('namespace'); + $name = $input->getArgument('name'); + + if (\in_array($type, [self::UUID_3, self::UUID_5], true) && !Uuid::isValid($namespace)) { + throw new InvalidArgumentException('You must specify a valid namespace as a second argument.'); + } + + switch ($type) { + case self::UUID_1: + $uid = Uuid::v1(); + break; + case self::UUID_3: + $uid = Uuid::v3(Uuid::fromString($namespace), $name); + break; + case self::UUID_4: + $uid = Uuid::v4(); + break; + case self::UUID_5: + $uid = Uuid::v5(Uuid::fromString($namespace), $name); + break; + case self::UUID_6: + $uid = Uuid::v6(); + break; + case self::ULID: + $uid = new Ulid(); + break; + default: + throw new InvalidArgumentException('Invalid UID type. Available values are '.implode(', ', self::TYPES).'.'); + } + + if ($input->getOption('base32')) { + $uid = $uid->toBase32(); + } elseif ($input->getOption('base58')) { + $uid = $uid->toBase58(); + } + + $output->writeln($uid); + + return 0; + } +} diff --git a/src/Symfony/Component/Uid/Tests/Command/UidGenerateCommandTest.php b/src/Symfony/Component/Uid/Tests/Command/UidGenerateCommandTest.php new file mode 100644 index 0000000000000..47415e8079aaf --- /dev/null +++ b/src/Symfony/Component/Uid/Tests/Command/UidGenerateCommandTest.php @@ -0,0 +1,49 @@ +getTester(); + + $tester->execute(['type' => 'uuid-1']); + $this->assertRegExp('/[a-f\d]{8}\-[a-f\d]{4}\-1[a-f\d]{3}\-[a-f\d]{4}\-[a-f\d]{8}/i', $tester->getDisplay()); + + $tester->execute(['type' => 'uuid-3', 'namespace' => 'a1dc606e-741b-11ea-aa36-99e245e7882b', 'name' => 'foo']); + $this->assertStringContainsString('ad4ab486-b67f-3d46-881f-21f03d27a68b', $tester->getDisplay()); + + $tester->execute(['type' => 'uuid-4']); + $this->assertRegExp('/[a-f\d]{8}\-[a-f\d]{4}\-4[a-f\d]{3}\-[a-f\d]{4}\-[a-f\d]{8}/i', $tester->getDisplay()); + + $tester->execute(['type' => 'uuid-5', 'namespace' => 'a1dc606e-741b-11ea-aa36-99e245e7882b', 'name' => 'foo']); + $this->assertStringContainsString('d87f160a-3cc6-520e-845f-112865bed05c', $tester->getDisplay()); + + $tester->execute(['type' => 'uuid-6']); + $this->assertRegExp('/[a-f\d]{8}\-[a-f\d]{4}\-6[a-f\d]{3}\-[a-f\d]{4}\-[a-f\d]{8}/i', $tester->getDisplay()); + + $tester->execute(['type' => 'ulid']); + $this->assertRegExp('/[0-9A-HJKMNP-TV-Z]{26}/i', $tester->getDisplay()); + + $tester->execute(['type' => 'uuid-1', '--base32' => true]); + $this->assertRegExp('/[0-9A-HJKMNP-TV-Z]{26}/i', $tester->getDisplay()); + + $tester->execute(['type' => 'uuid-1', '--base58' => true]); + $this->assertRegExp('/[1-9A-HJ-NP-Za-km-z]{22}/i', $tester->getDisplay()); + } + + public function getTester(): CommandTester + { + $application = new BaseApplication(); + $application->add(new UidGenerateCommand()); + $command = $application->find('uid:generate'); + + return new CommandTester($command); + } +}