8000 [Uid] Add Generate and Inspect commands · symfony/symfony@836350b · GitHub
[go: up one dir, main page]

Skip to content

Commit 836350b

Browse files
committed
[Uid] Add Generate and Inspect commands
1 parent 6ae59a9 commit 836350b

10 files changed

+1011
-0
lines changed

src/Symfony/Component/Uid/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* [BC BREAK] Replace `UuidV1::getTime()`, `UuidV6::getTime()` and `Ulid::getTime()` by `UuidV1::getDateTime()`, `UuidV6::getDateTime()` and `Ulid::getDateTime()`
1010
* Add `Uuid::NAMESPACE_*` constants from RFC4122
1111
* Add `UlidFactory`, `UuidFactory`, `RandomBasedUuidFactory`, `TimeBasedUuidFactory` and `NameBasedUuidFactory`
12+
* Add commands to generate and inspect UUIDs and ULIDs
1213

1314
5.2.0
1415
-----
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Uid\Command;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Input\InputOption;
17+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Style\SymfonyStyle;
20+
use Symfony\Component\Uid\Factory\UlidFactory;
21+
use Symfony\Component\Uid\Ulid;
22+
23+
class GenerateUlidCommand extends Command
24+
{
25+
protected static $defaultName = 'ulid:generate';
26+
27+
private $factory;
28+
29+
public function __construct(UlidFactory $factory = null)
30+
{
31+
$this->factory = $factory ?? new UlidFactory();
32+
33+
parent::__construct();
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
protected function configure(): void
40+
{
41+
$this
42+
->setDefinition([
43+
new InputOption('timestamp', null, InputOption::VALUE_REQUIRED, 'The ULID timestamp: a parsable date/time string by the \DateTimeImmutable constructor. It must be greater than or equals to the UNIX epoch (1970-01-01 00:00:00)'),
44+
new InputOption('count', 'c', InputOption::VALUE_REQUIRED, 'The number of ULID to generate', 1),
45+
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'The ULID output format: canonical, base_58 or rfc_4122', 'canonical'),
46+
])
47+
->setDescription('Generates a ULID')
48+
->setHelp(<<<'EOF'
49+
The <info>%command.name%</info> command generates a ULID.
50+
51+
<info>php %command.full_name%</info>
52+
53+
To specify the timestamp:
54+
55+
<info>php %command.full_name% --timestamp="2021-02-16 14:09:08"</info>
56+
57+
To generate several ULIDs:
58+
59+
<info>php %command.full_name% --count=10</info>
60+
61+
To output a specific format:
62+
63+
<info>php %command.full_name% --format=rfc_4122</info>
64+
EOF
65+
)
66+
;
67+
}
68+
69+
/**
70+
* {@inheritdoc}
71+
*/
72+
protected function execute(InputInterface $input, OutputInterface $output)
73+
{
74+
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
75+
76+
if (null !== $time = $input->getOption('timestamp')) {
77+
try {
78+
$time = new \DateTimeImmutable($time);
79+
} catch (\Exception $e) {
80+
$io->error(sprintf('Invalid timestamp "%s". It cannot be parsed.', $time));
81+
82+
return 1;
83+
}
84+
85+
if (0 > $time->format('U')) {
86+
$io->error(sprintf('Invalid timestamp "%s". It must be greater than or equals to the UNIX epoch (1970-01-01 00:00:00).', $input->getOption('timestamp')));
87+
88+
return 2;
89+
}
90+
}
91+
92+
switch ($input->getOption('format')) {
93+
case 'canonical':
94+
$format = 'strval';
95+
96+
break;
97+
case 'base_58':
98+
$format = static function (Ulid $ulid): string { return $ulid->toBase58(); };
99+
100+
break;
101+
case 'rfc_4122':
102+
$format = static function (Ulid $ulid): string { return $ulid->toRfc4122(); };
103+
104+
break;
105+
default:
106+
$io->error(sprintf('Invalid format "%s". Supported formats are canonical, base_58 and rfc_4122.', $input->getOption('format')));
107+
108+
return 3;
109+
}
110+
111+
$count = (int) $input->getOption('count');
112+
for ($i = 0; $i < $count; ++$i) {
113+
$output->writeln($format($this->factory->create($time)));
114+
}
115+
116+
return 0;
117+
}
118+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Uid\Command;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Input\InputArgument;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
19+
use Symfony\Component\Console\Output\OutputInterface;
20+
use Symfony\Component\Console\Style\SymfonyStyle;
21+
use Symfony\Component\Uid\Factory\UuidFactory;
22+
use Symfony\Component\Uid\Ulid;
23+
use Symfony\Component\Uid\Uuid;
24+
25+
class GenerateUuidCommand extends Command
26+
{
27+
protected static $defaultName = 'uuid:generate';
28+
29+
private $factory;
30+
31+
public function __construct(UuidFactory $factory = null)
32+
{
33+
$this->factory = $factory ?? new UuidFactory();
34+
35+
parent::__construct();
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
protected function configure(): void
42+
{
43+
$this
44+
->setDefinition([
45+
new InputOption('time-based', null, InputOption::VALUE_REQUIRED, 'The timestamp, to generate a time based UUID: a parsable date/time string by the \DateTimeImmutable constructor. It must be greater than or equals to the UUID epoch (1582-10-15 00:00:00)'),
46+
new InputOption('node', null, InputOption::VALUE_REQUIRED, "The time based UUID's node: a UUID, in any of the supported formats (base 58, base 32 or RFC 4122)"),
47+
new InputOption('name-based', null, InputOption::VALUE_REQUIRED, 'The name, to generate a name based UUID'),
48+
new InputOption('namespace', null, InputOption::VALUE_REQUIRED, "The name based UUID's namespace: a UUID, in any of the supported formats (base 58, base 32 or RFC 4122)"),
49+
new InputOption('random-based', null, InputOption::VALUE_NONE, 'To generate a random based UUID'),
50+
new InputOption('count', 'c', InputOption::VALUE_REQUIRED, 'The number of UUID to generate', 1),
51+
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'The UUID output format: canonical, base_58 or base_32', 'canonical'),
52+
])
53+
->setDescription('Generates a UUID')
54+
->setHelp(<<<'EOF'
55+
The <info>%command.name%</info> generates a UUID.
56+
57+
<info>php %command.full_name%</info>
58+
59+
To generate a time based UUID:
60+
61+
<info>php %command.full_name% --time-based=now</info>
62+
63+
To specify a time based UUID's node:
64+
65+
<info>php %command.full_name% --time-based=@1613480254 --node=fb3502dc-137e-4849-8886-ac90d07f64a7</info>
66+
67+
To generate a name based UUID:
68+
69+
<info>php %command.full_name% --name-based=foo</info>
70+
71+
To specify a name based UUID's namespace:
72+
73+
<info>php %command.full_name% --name-based=bar --namespace=fb3502dc-137e-4849-8886-ac90d07f64a7</info>
74+
75+
To generate a random based UUID:
76+
77+
<info>php %command.full_name% --random-based</info>
78+
79+
To generate several UUIDs:
80+
81+
<info>php %command.full_name% --count=10</info>
82+
83+
To output a specific format:
84+
85+
<info>php %command.full_name% --format=base_58</info>
86+
EOF
87+
)
88+
;
89+
}
90+
91+
/**
92+
* {@inheritdoc}
93+
*/
94+
protected function execute(InputInterface $input, OutputInterface $output)
95+
{
96+
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
97+
98+
$time = $input->getOption('time-based');
99+
$node = $input->getOption('node');
100+
$name = $input->getOption('name-based');
101+
$namespace = $input->getOption('namespace');
102+
$random = $input->getOption('random-based');
103+
104+
switch (true) {
105+
case !$time && !$node && !$name && !$namespace && !$random:
106+
$create = [$this->factory, 'create'];
107+
108+
break;
109+
case $time && !$name && !$namespace && !$random:
110+
if ($node) {
111+
try {
112+
$node = Uuid::fromString($node);
113+
} catch (\InvalidArgumentException $e) {
114+
$io->error(sprintf('Invalid node "%s". It cannot be parsed.', $node));
115+
116+
return 1;
117+
}
118+
}
119+
120+
try {
121+
$time = new \DateTimeImmutable($time);
122+
} catch (\Exception $e) {
123+
$io->error(sprintf('Invalid timestamp "%s". It cannot be parsed.', $input->getOption('time-based')));
124+
125+
return 2;
126+
}
127+
128+
if ($time < new \DateTimeImmutable('@-12219292800')) {
129+
$io->error(sprintf('Invalid timestamp "%s". It must be greater than or equals to the UUID epoch (1582-10-15 00:00:00).', $input->getOption('time-based')));
130+
131+
return 3;
132+
}
133+
134+
$create = function () use ($node, $time): Uuid { return $this->factory->timeBased($node)->create($time); };
135+
136+
break;
137+
case $name && !$time && !$node && !$random:
138+
if ($namespace) {
139+
try {
140+
$namespace = Uuid::fromString($namespace);
141+
} catch (\InvalidArgumentException $e) {
142+
$io->error(sprintf('Invalid namespace "%s". It cannot be parsed.', $namespace));
143+
144+
return 4;
145+
}
146+
} else {
147+
$refl = new \ReflectionProperty($this->factory, 'nameBasedNamespace');
148+
$refl->setAccessible(true);
149+
if (null === $refl->getValue($this->factory)) {
150+
$io->error('Missing namespace. Use the "--namespace" option or configure a default namespace in the underlying factory.');
151+
152+
return 5;
153+
}
154+
}
155+
156+
$create = function() use ($namespace, $name): Uuid { return $this->factory->nameBased($namespace)->create($name); };
157+
158+
break;
159+
case $random && !$time && !$node && !$name && !$namespace:
160+
$create = [$this->factory->randomBased(), 'create'];
161+
162+
break;
163+
default:
164+
$io->error('Invalid combination of options.');
165+
166+
return 6;
167+
}
168+
169+
switch ($input->getOption('format')) {
170+
case 'canonical':
171+
$format = 'strval';
172+
173+
break;
174+
case 'base_58':
175+
$format = static function (Uuid $uuid): string { return $uuid->toBase58(); };
176+
177+
break;
178+
case 'base_32':
179+
$format = static function (Uuid $uuid): string { return $uuid->toBase32(); };
180+
181+
break;
182+
default:
183+
$io->error(sprintf('Invalid format "%s". Supported formats are canonical, base_58 and base_32.', $input->getOption('format')));
184+
185+
return 7;
186+
}
187+
188+
$count = (int) $input->getOption('count');
189+
for ($i = 0; $i < $count; ++$i) {
190+
$io->writeln($format($create()));
191+
}
192+
193+
return 0;
194+
}
195+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Uid\Command;
13+
14+
use Symfony\Component\Console\Command\Command;
15+
use Symfony\Component\Console\Helper\TableSeparator;
16+
use Symfony\Component\Console\Input\InputArgument;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
19+
use Symfony\Component\Console\Output\OutputInterface;
20+
use Symfony\Component\Console\Style\SymfonyStyle;
21+
use Symfony\Component\Uid\Ulid;
22+
23+
class InspectUlidCommand extends Command
24+
{
25+
protected static $defaultName = 'ulid:inspect';
26+
27+
/**
28+
* {@inheritdoc}
29+
*/
30+
protected function configure(): void
31+
{
32+
$this
33+
->setDefinition([
34+
new InputArgument('ulid', InputArgument::REQUIRED, 'The ULID to inspect, in any of the supported formats (base 58, base 32 or RFC 4122)'),
35+
])
36+
->setDescription('Inspects a ULID')
37+
->setHelp(<<<'EOF'
38+
The <info>%command.name%</info> display information about a ULID.
39+
40+
<info>php %command.full_name% 01EWAKBCMWQ2C94EXNN60ZBS0Q</info>
41+
<info>php %command.full_name% 1BVdfLn3ERmbjYBLCdaaLW</info>
42+
<info>php %command.full_name% 01771535-b29c-b898-923b-b5a981f5e417</info>
43+
EOF
44+
)
45+
;
46+
}
47+
48+
/**
49+
* {@inheritdoc}
50+
*/
51+
protected function execute(InputInterface $input, OutputInterface $output)
52+
{
53+
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
54+
55+
try {
56+
/** @var Ulid $ulid */
57+
$ulid = Ulid::fromString($input->getArgument('ulid'));
58+
} catch (\InvalidArgumentException $e) {
59+
$io->error(sprintf('Invalid ULID "%s".', $input->getArgument('ulid')));
60+
61+
return 1;
62+
}
63+
64+
$io->table(['Label', 'Value'], [
65+
['Canonical (Base 32)', (string) $ulid],
66+
['Base 58', $ulid->toBase58()],
67+
['RFC 4122', $ulid->toRfc4122()],
68+
new TableSeparator(),
69+
['Timestamp', ($ulid->getDateTime())->format('Y-m-d H:i:s.v')],
70+
]);
71+
72+
return 0;
73+
}
74+
}

0 commit comments

Comments
 (0)
0