Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 325d898

Browse filesBrowse files
committed
[Uid] Add Generate and Inspect commands
1 parent 6ae59a9 commit 325d898
Copy full SHA for 325d898

10 files changed

+1009
-0
lines changed

‎src/Symfony/Component/Uid/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/CHANGELOG.md
+1Lines 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
-----
+118Lines 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+
}
+193Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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\UuidFactory;
21+
use Symfony\Component\Uid\Uuid;
22+
23+
class GenerateUuidCommand extends Command
24+
{
25+
protected static $defaultName = 'uuid:generate';
26+
27+
private $factory;
28+
29+
public function __construct(UuidFactory $factory = null)
30+
{
31+
$this->factory = $factory ?? new UuidFactory();
32+
33+
parent::__construct();
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
protected function configure(): void
40+
{
41+
$this
42+
->setDefinition([
43+
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)'),
44+
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)"),
45+
new InputOption('name-based', null, InputOption::VALUE_REQUIRED, 'The name, to generate a name based UUID'),
46+
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)"),
47+
new InputOption('random-based', null, InputOption::VALUE_NONE, 'To generate a random based UUID'),
48+
new InputOption('count', 'c', InputOption::VALUE_REQUIRED, 'The number of UUID to generate', 1),
49+
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'The UUID output format: canonical, base_58 or base_32', 'canonical'),
50+
])
51+
->setDescription('Generates a UUID')
52+
->setHelp(<<<'EOF'
53+
The <info>%command.name%</info> generates a UUID.
54+
55+
<info>php %command.full_name%</info>
56+
57+
To generate a time based UUID:
58+
59+
<info>php %command.full_name% --time-based=now</info>
60+
61+
To specify a time based UUID's node:
62+
63+
<info>php %command.full_name% --time-based=@1613480254 --node=fb3502dc-137e-4849-8886-ac90d07f64a7</info>
64+
65+
To generate a name based UUID:
66+
67+
<info>php %command.full_name% --name-based=foo</info>
68+
69+
To specify a name based UUID's namespace:
70+
71+
<info>php %command.full_name% --name-based=bar --namespace=fb3502dc-137e-4849-8886-ac90d07f64a7</info>
72+
73+
To generate a random based UUID:
74+
75+
<info>php %command.full_name% --random-based</info>
76+
77+
To generate several UUIDs:
78+
79+
<info>php %command.full_name% --count=10</info>
80+
81+
To output a specific format:
82+
83+
<info>php %command.full_name% --format=base_58</info>
84+
EOF
85+
)
86+
;
87+
}
88+
89+
/**
90+
* {@inheritdoc}
91+
*/
92+
protected function execute(InputInterface $input, OutputInterface $output)
93+
{
94+
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
95+
96+
$time = $input->getOption('time-based');
97+
$node = $input->getOption('node');
98+
$name = $input->getOption('name-based');
99+
$namespace = $input->getOption('namespace');
100+
$random = $input->getOption('random-based');
101+
102+
switch (true) {
103+
case !$time && !$node && !$name && !$namespace && !$random:
104+
$create = [$this->factory, 'create'];
105+
106+
break;
107+
case $time && !$name && !$namespace && !$random:
108+
if ($node) {
109+
try {
110+
$node = Uuid::fromString($node);
111+
} catch (\InvalidArgumentException $e) {
112+
$io->error(sprintf('Invalid node "%s". It cannot be parsed.', $node));
113+
114+
return 1;
115+
}
116+
}
117+
118+
try {
119+
$time = new \DateTimeImmutable($time);
120+
} catch (\Exception $e) {
121+
$io->error(sprintf('Invalid timestamp "%s". It cannot be parsed.', $input->getOption('time-based')));
122+
123+
return 2;
124+
}
125+
126+
if ($time < new \DateTimeImmutable('@-12219292800')) {
127+
$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')));
128+
129+
return 3;
130+
}
131+
132+
$create = function () use ($node, $time): Uuid { return $this->factory->timeBased($node)->create($time); };
133+
134+
break;
135+
case $name && !$time && !$node && !$random:
136+
if ($namespace) {
137+
try {
138+
$namespace = Uuid::fromString($namespace);
139+
} catch (\InvalidArgumentException $e) {
140+
$io->error(sprintf('Invalid namespace "%s". It cannot be parsed.', $namespace));
141+
142+
return 4;
143+
}
144+
} else {
145+
$refl = new \ReflectionProperty($this->factory, 'nameBasedNamespace');
146+
$refl->setAccessible(true);
147+
if (null === $refl->getValue($this->factory)) {
148+
$io->error('Missing namespace. Use the "--namespace" option or configure a default namespace in the underlying factory.');
149+
150+
return 5;
151+
}
152+
}
153+
154+
$create = function () use ($namespace, $name): Uuid { return $this->factory->nameBased($namespace)->create($name); };
155+
156+
break;
157+
case $random && !$time && !$node && !$name && !$namespace:
158+
$create = [$this->factory->randomBased(), 'create'];
159+
160+
break;
161+
default:
162+
$io->error('Invalid combination of options.');
163+
164+
return 6;
165+
}
166+
167+
switch ($input->getOption('format')) {
168+
case 'canonical':
169+
$format = 'strval';
170+
171+
break;
172+
case 'base_58':
173+
$format = static function (Uuid $uuid): string { return $uuid->toBase58(); };
174+
175+
break;
176+
case 'base_32':
177+
$format = static function (Uuid $uuid): string { return $uuid->toBase32(); };
178+
179+
break;
180+
default:
181+
$io->error(sprintf('Invalid format "%s". Supported formats are canonical, base_58 and base_32.', $input->getOption('format')));
182+
183+
return 7;
184+
}
185+
186+
$count = (int) $input->getOption('count');
187+
for ($i = 0; $i < $count; ++$i) {
188+
$io->writeln($format($create()));
189+
}
190+
191+
return 0;
192+
}
193+
}
+74Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.