Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"psr/container": "^1.1.1 || ^2.0.1",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
"ramsey/uuid": "^4.7",
"symfony/console": "^7.4.0 || ^8.0.0",
"symfony/error-handler": "^7.4.0 || ^8.0.0",
"symfony/finder": "^7.4.0 || ^8.0.0",
Expand Down
1 change: 0 additions & 1 deletion src/Illuminate/Queue/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"illuminate/pipeline": "^13.0",
"illuminate/support": "^13.0",
"laravel/serializable-closure": "^2.0.10",
"ramsey/uuid": "^4.7",
"symfony/process": "^7.4.5 || ^8.0.5"
},
"suggest": {
Expand Down
13 changes: 6 additions & 7 deletions src/Illuminate/Support/BinaryCodec.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
namespace Illuminate\Support;

use InvalidArgumentException;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid;

class BinaryCodec
{
/** @var array<string, array{encode: callable(UuidInterface|Ulid|string|null): ?string, decode: callable(?string): ?string}> */
/** @var array<string, array{encode: callable(Uuid|Ulid|string|null): ?string, decode: callable(?string): ?string}> */
protected static array $customCodecs = [];

/**
Expand All @@ -28,7 +27,7 @@ public static function register(string $name, callable $encode, callable $decode
*
* @throws \InvalidArgumentException
*/
public static function encode(UuidInterface|Ulid|string|null $value, string $format): ?string
public static function encode(Uuid|Ulid|string|null $value, string $format): ?string
{
if (blank($value)) {
return null;
Expand All @@ -40,9 +39,9 @@ public static function encode(UuidInterface|Ulid|string|null $value, string $for

return match ($format) {
'uuid' => match (true) {
$value instanceof UuidInterface => $value->getBytes(),
$value instanceof Uuid => $value->toBinary(),
self::isBinary($value) => $value,
default => Uuid::fromString($value)->getBytes(),
default => Uuid::fromString($value)->toBinary(),
},
'ulid' => match (true) {
$value instanceof Ulid => $value->toBinary(),
Expand All @@ -69,7 +68,7 @@ public static function decode(?string $value, string $format): ?string
}

return match ($format) {
'uuid' => (self::isBinary($value) ? Uuid::fromBytes($value) : Uuid::fromString($value))->toString(),
'uuid' => (string) (self::isBinary($value) ? Uuid::fromBinary($value) : Uuid::fromString($value)),
'ulid' => (self::isBinary($value) ? Ulid::fromBinary($value) : Ulid::fromString($value))->toString(),
default => throw new InvalidArgumentException("Format [$format] is invalid."),
};
Expand Down
15 changes: 14 additions & 1 deletion src/Illuminate/Support/Carbon.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

use Carbon\Carbon as BaseCarbon;
use Carbon\CarbonImmutable as BaseCarbonImmutable;
use Exception;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Dumpable;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Uid\BinaryUtil;
use Symfony\Component\Uid\TimeBasedUidInterface;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid;

class Carbon extends BaseCarbon
{
Expand All @@ -31,6 +34,16 @@ public static function createFromId(Uuid|Ulid|string $id): static
$id = Ulid::isValid($id) ? Ulid::fromString($id) : Uuid::fromString($id);
}

if (Str::isUuid($uuid = $id->toString(), 2)) {
return static::createFromInterface(
BinaryUtil::hexToDateTime('0'.substr($uuid, 15, 3).substr($uuid, 9, 4).'00000000')
);
}

if (! $id instanceof TimeBasedUidInterface) {
throw new Exception("Not a time-based UUID or ULID: [{$id->toString()}].");
}

return static::createFromInterface($id->getDateTime());
}

Expand Down
70 changes: 25 additions & 45 deletions src/Illuminate/Support/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
use League\CommonMark\Extension\InlinesOnly\InlinesOnlyExtension;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use League\CommonMark\MarkdownConverter;
use Ramsey\Uuid\Codec\TimestampFirstCombCodec;
use Ramsey\Uuid\Exception\InvalidUuidStringException;
use Ramsey\Uuid\Generator\CombGenerator;
use Ramsey\Uuid\Rfc4122\FieldsInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidFactory;
use Symfony\Component\Uid\Exception\InvalidArgumentException;
use Symfony\Component\Uid\MaxUuid;
use Symfony\Component\Uid\NilUuid;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Uid\UuidV7;
use Throwable;
use Traversable;
use voku\helper\ASCII;
Expand Down Expand Up @@ -55,7 +54,7 @@ class Str
/**
* The callback that should be used to generate UUIDs.
*
* @var (callable(): \Ramsey\Uuid\UuidInterface)|null
* @var (callable(): \Symfony\Component\Uid\Uuid)|null
*/
protected static $uuidFactory;

Expand Down Expand Up @@ -652,33 +651,25 @@ public static function isUuid($value, $version = null)
return false;
}

if ($version === null) {
return preg_match('/^[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}$/D', $value) > 0;
if (is_null($version)) {
return Uuid::isValid($value);
}

$factory = new UuidFactory;

try {
$factoryUuid = $factory->fromString($value);
} catch (InvalidUuidStringException) {
return false;
}

$fields = $factoryUuid->getFields();

if (! ($fields instanceof FieldsInterface)) {
$uuid = Uuid::fromString($value);
} catch (InvalidArgumentException) {
return false;
}

if ($version === 0 || $version === 'nil') {
return $fields->isNil();
if ($version === 'nil' || $version === 0) {
return $uuid instanceof NilUuid;
}

if ($version === 'max') {
return $fields->isMax();
return $uuid instanceof MaxUuid;
}

return $fields->getVersion() === $version;
return $version === (int) $uuid->toString()[14];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is an implementation detail—can't we abstract that away?

Copy link
Copy Markdown
Contributor Author

@lukaskleinschmidt lukaskleinschmidt Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do get back the specific class implementations like UuidV1 or UuidV7.
So doing something like this would also work:

return match ($version) {
    'nil', 0 => $uuid instanceof NilUuid,
    'max' => $uuid instanceof MaxUuid,
    1 => $uuid instanceof Uuid1,
    //...
    7 => $uuid instanceof Uuid7,
    default => false,
}

But will fail for v2 uuids.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that if not for v2—maybe, we would need a wrapper for this? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have something specific in mind.
I can't think of a different way to check for a v2 version right now, other than checking the string directly like above.

}

/**
Expand Down Expand Up @@ -1922,57 +1913,46 @@ public static function wordWrap($string, $characters = 75, $break = "\n", $cutLo
/**
* Generate a UUID (version 4).
*
* @return \Ramsey\Uuid\UuidInterface
* @return \Symfony\Component\Uid\Uuid
*/
public static function uuid()
{
return static::$uuidFactory
? call_user_func(static::$uuidFactory)
: Uuid::uuid4();
: Uuid::v4();
}

/**
* Generate a UUID (version 7).
*
* @param \DateTimeInterface|null $time
* @return \Ramsey\Uuid\UuidInterface
* @return \Symfony\Component\Uid\Uuid
*/
public static function uuid7($time = null)
{
return static::$uuidFactory
? call_user_func(static::$uuidFactory)
: Uuid::uuid7($time);
: new UuidV7(UuidV7::generate($time));
}

/**
* Generate a time-ordered UUID.
*
* @return \Ramsey\Uuid\UuidInterface
* @return \Symfony\Component\Uid\Uuid
*/
public static function orderedUuid()
{
if (static::$uuidFactory) {
return call_user_func(static::$uuidFactory);
}

$factory = new UuidFactory;

$factory->setRandomGenerator(new CombGenerator(
$factory->getRandomGenerator(),
$factory->getNumberConverter()
));

$factory->setCodec(new TimestampFirstCombCodec(
$factory->getUuidBuilder()
));

return $factory->uuid4();
return static::uuid7();
}

/**
* Set the callable that will be used to generate UUIDs.
*
* @param (callable(): \Ramsey\Uuid\UuidInterface)|null $factory
* @param (callable(): \Symfony\Component\Uid\Uuid)|null $factory
* @return void
*/
public static function createUuidsUsing(?callable $factory = null)
Expand All @@ -1983,8 +1963,8 @@ public static function createUuidsUsing(?callable $factory = null)
/**
* Set the sequence that will be used to generate UUIDs.
*
* @param \Ramsey\Uuid\UuidInterface[] $sequence
* @param (callable(): \Ramsey\Uuid\UuidInterface)|null $whenMissing
* @param \Symfony\Component\Uid\Uuid[] $sequence
* @param (callable(): \Symfony\Component\Uid\Uuid)|null $whenMissing
* @return void
*/
public static function createUuidsUsingSequence(array $sequence, $whenMissing = null)
Expand Down Expand Up @@ -2017,8 +1997,8 @@ public static function createUuidsUsingSequence(array $sequence, $whenMissing =
/**
* Always return the same UUID when generating new UUIDs.
*
* @param (\Closure(\Ramsey\Uuid\UuidInterface): mixed)|null $callback
* @return \Ramsey\Uuid\UuidInterface
* @param (\Closure(\Symfony\Component\Uid\Uuid): mixed)|null $callback
* @return \Symfony\Component\Uid\Uuid
*/
public static function freezeUuids(?Closure $callback = null)
{
Expand Down
3 changes: 1 addition & 2 deletions src/Illuminate/Support/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@
"laravel/serializable-closure": "Required to use the once function (^2.0.10).",
"league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.7).",
"league/uri": "Required to use the Uri class (^7.5.1).",
"ramsey/uuid": "Required to use Str::uuid() (^4.7).",
"symfony/process": "Required to use the Composer class (^7.4 || ^8.0).",
"symfony/uid": "Required to use Str::ulid() (^7.4 || ^8.0).",
"symfony/uid": "Required to use Str::uuid() or Str::ulid() (^7.4 || ^8.0).",
"symfony/var-dumper": "Required to use the dd function (^7.4 || ^8.0).",
"vlucas/phpdotenv": "Required to use the Env class and env helper (^5.6.1)."
},
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Validation/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"suggest": {
"illuminate/database": "Required to use the database presence verifier (^13.0).",
"ramsey/uuid": "Required to use Validator::validateUuid() (^4.7)."
"symfony/uid": "Required to use Validator::validateUuid() or Validator::validateUlid() (^7.4 || ^8.0."
},
"minimum-stability": "dev",
"autoload": {
Expand Down
6 changes: 3 additions & 3 deletions tests/Database/DatabaseEloquentAsBinaryCastTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use Illuminate\Support\BinaryCodec;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid;

class DatabaseEloquentAsBinaryCastTest extends TestCase
{
Expand Down Expand Up @@ -45,7 +45,7 @@ public function testGetDecodesUuidFromBinary()
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$model = new AsBinaryTestModel;
$model->setRawAttributes(['uuid' => Uuid::fromString($uuid)->getBytes()]);
$model->setRawAttributes(['uuid' => Uuid::fromString($uuid)->toBinary()]);

$this->assertSame($uuid, $model->uuid);
}
Expand All @@ -56,7 +56,7 @@ public function testSetEncodesUuidToBinary()
$model = new AsBinaryTestModel;
$model->uuid = $uuid;

$this->assertSame(Uuid::fromString($uuid)->getBytes(), $model->getAttributes()['uuid']);
$this->assertSame(Uuid::fromString($uuid)->toBinary(), $model->getAttributes()['uuid']);
}

public function testGetDecodesUlidFromBinary()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ public function testItWrapsThrownExceptions()
} catch (MathException $e) {
$this->assertSame('Unable to cast value to a decimal.', $e->getMessage());
$this->assertInstanceOf(NumberFormatException::class, $e->getPrevious());
$this->assertSame('The given value "foo" does not represent a valid number.', $e->getPrevious()->getMessage());
$this->assertTrue(in_array($e->getPrevious()->getMessage(), [
'The given value "foo" does not represent a valid number.',
'Value "foo" does not represent a valid number.',
], true));
}
}

Expand Down
10 changes: 5 additions & 5 deletions tests/Support/SupportBinaryCodecTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid;

class SupportBinaryCodecTest extends TestCase
{
Expand Down Expand Up @@ -85,12 +85,12 @@ public function testUuidEncodeFromString()
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';

$this->assertSame(Uuid::fromString($uuid)->getBytes(), BinaryCodec::encode($uuid, 'uuid'));
$this->assertSame(Uuid::fromString($uuid)->toBinary(), BinaryCodec::encode($uuid, 'uuid'));
}

public function testUuidEncodeFromBinary()
{
$bytes = Uuid::fromString('550e8400-e29b-41d4-a716-446655440000')->getBytes();
$bytes = Uuid::fromString('550e8400-e29b-41d4-a716-446655440000')->toBinary();

$this->assertSame($bytes, BinaryCodec::encode($bytes, 'uuid'));
}
Expand All @@ -99,13 +99,13 @@ public function testUuidEncodeFromInstance()
{
$uuid = Uuid::fromString('550e8400-e29b-41d4-a716-446655440000');

$this->assertSame($uuid->getBytes(), BinaryCodec::encode($uuid, 'uuid'));
$this->assertSame($uuid->toBinary(), BinaryCodec::encode($uuid, 'uuid'));
}

public function testUuidDecodeFromBinary()
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$bytes = Uuid::fromString($uuid)->getBytes();
$bytes = Uuid::fromString($uuid)->toBinary();

$this->assertSame($uuid, BinaryCodec::decode($bytes, 'uuid'));
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Support/SupportCarbonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public function testCreateFromUid()
$this->assertEquals('2023-05-12 03:23:43.347428', $uuidv6->toDateTimeString('microsecond'));

$uuidv7 = Carbon::createFromId('01880dfa-2825-72e4-acbb-b1e4981cf8af');
$this->assertEquals('2023-05-12 03:21:18.117000', $uuidv7->toDateTimeString('microsecond'));
$this->assertEquals('2023-05-12 03:21:18.117185', $uuidv7->toDateTimeString('microsecond'));
}

public function testPlus(): void
Expand Down
8 changes: 4 additions & 4 deletions tests/Support/SupportStrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use Illuminate\Tests\Support\Fixtures\StringableObjectStub;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\UuidInterface;
use ReflectionClass;
use Symfony\Component\Uid\Uuid;
use ValueError;

class SupportStrTest extends TestCase
Expand Down Expand Up @@ -1366,9 +1366,9 @@ public function testUcsplit()

public function testUuid()
{
$this->assertInstanceOf(UuidInterface::class, Str::uuid());
$this->assertInstanceOf(UuidInterface::class, Str::orderedUuid());
$this->assertInstanceOf(UuidInterface::class, Str::uuid7());
$this->assertInstanceOf(Uuid::class, Str::uuid());
$this->assertInstanceOf(Uuid::class, Str::orderedUuid());
$this->assertInstanceOf(Uuid::class, Str::uuid7());
}

public function testAsciiNull()
Expand Down
Loading