toString())->toBase58(); } public static function fromBase58(string $base58): UuidInterface { $base58 = trim($base58); if ($base58 === '' || ! preg_match('/^[1-9A-HJ-NP-Za-km-z]{21,22}$/', $base58)) { throw InvalidUuidException::forValue($base58); } try { return Uuid::fromString(SymfonyUuid::fromBase58($base58)->toString()); } catch (\Throwable) { throw InvalidUuidException::forValue($base58); } } public static function isV4(UuidInterface $uuid): bool { return self::version($uuid) === 4; } public static function isV7(UuidInterface $uuid): bool { return self::version($uuid) === 7; } public static function version(UuidInterface $uuid): int { $fields = $uuid->getFields(); if ($fields instanceof Rfc4122FieldsInterface) { $version = $fields->getVersion(); if ($version !== null) { return $version; } } $uuidString = $uuid->toString(); if (strlen($uuidString) >= 15) { return (int) hexdec($uuidString[14]); } return 0; } public static function validateUuid(UuidInterface $uuid): void { if (! Uuid::isValid($uuid->toString())) { throw InvalidUuidException::forValue($uuid->toString()); } } public static function toDateTime(UuidInterface $uuid): \DateTimeImmutable { if (! self::isV7($uuid)) { throw new InvalidUuidException("Cannot extract datetime from non-V7 UUID: {$uuid->toString()}"); } $fields = $uuid->getFields(); if ($fields instanceof Rfc4122FieldsInterface) { $timeMsHex = $fields->getTimeLow()->toString() . $fields->getTimeMid()->toString(); } else { $hex = str_replace('-', '', $uuid->toString()); $timeMsHex = substr($hex, 0, 12); } $timeMs = (int) hexdec($timeMsHex); $sec = intdiv($timeMs, 1000); $ms = $timeMs % 1000; $dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%d.%06d', $sec, $ms * 1000)); if ($dateTime === false) { throw new InvalidUuidException("Failed to extract datetime from UUID: {$uuid->toString()}"); } return $dateTime->setTimezone(new \DateTimeZone('UTC')); } }