Aggiornato Composer

This commit is contained in:
Paolo A
2024-05-17 12:24:19 +00:00
parent 4ac62108b5
commit ec201d75b2
2238 changed files with 38684 additions and 59785 deletions

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015-2021 Ben Ramsey <ben@benramsey.com>
Copyright (c) 2015-2022 Ben Ramsey <ben@benramsey.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -9,7 +9,7 @@
<a href="https://packagist.org/packages/ramsey/collection"><img src="https://img.shields.io/packagist/v/ramsey/collection.svg?style=flat-square&label=release" alt="Download Package"></a>
<a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/collection.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a>
<a href="https://github.com/ramsey/collection/blob/master/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/collection.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a>
<a href="https://github.com/ramsey/collection/actions?query=workflow%3ACI"><img src="https://img.shields.io/github/workflow/status/ramsey/collection/CI?label=CI&logo=github&style=flat-square" alt="Build Status"></a>
<a href="https://github.com/ramsey/collection/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/collection/continuous-integration.yml?branch=main&logo=github&style=flat-square" alt="Build Status"></a>
<a href="https://codecov.io/gh/ramsey/collection"><img src="https://img.shields.io/codecov/c/gh/ramsey/collection?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a>
<a href="https://shepherd.dev/github/ramsey/collection"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fshepherd.dev%2Fgithub%2Framsey%2Fcollection%2Fcoverage" alt="Psalm Type Coverage"></a>
</p>
@@ -34,25 +34,13 @@ composer require ramsey/collection
## Usage
Examples of how to use this framework can be found in the
Examples of how to use this library may be found in the
[Wiki pages](https://github.com/ramsey/collection/wiki/Examples).
## Contributing
Contributions are welcome! Before contributing to this project, familiarize
yourself with [CONTRIBUTING.md](CONTRIBUTING.md).
To develop this project, you will need [PHP](https://www.php.net) 7.3 or greater
and [Composer](https://getcomposer.org).
After cloning this repository locally, execute the following commands:
``` bash
cd /path/to/repository
composer install
```
Now, you are ready to develop!
Contributions are welcome! To contribute, please familiarize yourself with
[CONTRIBUTING.md](CONTRIBUTING.md).
## Coordinated Disclosure
@@ -79,4 +67,4 @@ MIT License (MIT). Please see [LICENSE](LICENSE) for more information.
[java]: http://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html
[security.md]: https://github.com/ramsey/collection/blob/master/SECURITY.md
[security.md]: https://github.com/ramsey/collection/blob/main/SECURITY.md

View File

@@ -1,29 +1,59 @@
<!--
This policy was created using the HackerOne Policy Builder:
https://hackerone.com/policy-builder/
This policy template was created using the HackerOne Policy Builder [1],
with guidance from the National Telecommunications and Information
Administration Coordinated Vulnerability Disclosure Template [2].
-->
# Vulnerability Disclosure Policy
# Vulnerability Disclosure Policy (VDP)
## Brand Promise
<!--
This is your brand promise. Its objective is to "demonstrate a clear, good
faith commitment to customers and other stakeholders potentially impacted by
security vulnerabilities" [2].
-->
Keeping user information safe and secure is a top priority, and we welcome the
contribution of external security researchers.
## Scope
<!--
This is your initial scope. It tells vulnerability finders and reporters
"which systems and capabilities are 'fair game' versus 'off limits'" [2].
For software packages, this is often a list of currently maintained versions
of the package.
-->
If you believe you've found a security issue in software that is maintained in
this repository, we encourage you to notify us.
| Version | In scope | Source code |
| :-----: | :------: | :---------- |
| ------- | :------: | ----------- |
| latest | ✅ | https://github.com/ramsey/collection |
## How to Submit a Report
To submit a vulnerability report, please contact us at <security@ramsey.dev>.
<!--
This is your communication process. It tells security researchers how to
contact you to report a vulnerability. It may be a link to a web form that
uses HTTPS for secure communication, or it may be an email address.
Optionally, you may choose to include a PGP public key, so that researchers
may send you encrypted messages.
-->
To submit a vulnerability report, please contact us at security@ramsey.dev.
Your submission will be reviewed and validated by a member of our team.
## Safe Harbor
<!--
This section assures vulnerability finders and reporters that they will
receive good faith responses to their good faith acts. In other words,
"we will not take legal action if..." [2].
-->
We support safe harbor for security researchers who:
* Make a good faith effort to avoid privacy violations, destruction of data, and
@@ -33,7 +63,7 @@ We support safe harbor for security researchers who:
us immediately, do not proceed with access, and immediately purge any local
information.
* Provide us with a reasonable amount of time to resolve vulnerabilities prior
to any disclosure to the public or a third-party.
to any disclosure to the public or a third party.
We will consider activities conducted consistent with this policy to constitute
"authorized" conduct and will not pursue civil action or initiate a complaint to
@@ -45,15 +75,41 @@ with or unaddressed by this policy.
## Preferences
<!--
The preferences section sets expectations based on priority and submission
volume, rather than legal objection or restriction [2].
According to the NTIA [2]:
This section is a living document that sets expectations for preferences
and priorities, typically maintained by the support and engineering
team. This can outline classes of vulnerabilities, reporting style
(crash dumps, CVSS scoring, proof-of-concept, etc.), tools, etc. Too
many preferences can set the wrong tone or make reporting findings
difficult to navigate. This section also sets expectations to the
researcher community for what types of issues are considered important
or not.
-->
* Please provide detailed reports with reproducible steps and a clearly defined
impact.
* Include the version number of the vulnerable package in your report
* Social engineering (e.g. phishing, vishing, smishing) is prohibited.
<!--
References
[1] HackerOne. Policy builder. Retrieved from https://hackerone.com/policy-builder/
[2] NTIA Safety Working Group. 2016. "Early stage" coordinated vulnerability
disclosure template: Version 1.1. (15 December 2016). Retrieved from
https://www.ntia.doc.gov/files/ntia/publications/ntia_vuln_disclosure_early_stage_template.pdf
-->
## Encryption Key for security@ramsey.dev
For increased privacy when reporting sensitive issues, you may encrypt your
messages using the following key:
message using the following public key:
```
-----BEGIN PGP PUBLIC KEY BLOCK-----

View File

@@ -1,7 +1,8 @@
{
"name": "ramsey/collection",
"type": "library",
"description": "A PHP library for representing and manipulating collections.",
"license": "MIT",
"type": "library",
"keywords": [
"array",
"collection",
@@ -10,7 +11,6 @@
"queue",
"set"
],
"license": "MIT",
"authors": [
{
"name": "Ben Ramsey",
@@ -19,31 +19,33 @@
}
],
"require": {
"php": "^7.3 || ^8",
"php": "^7.4 || ^8.0",
"symfony/polyfill-php81": "^1.23"
},
"require-dev": {
"captainhook/captainhook": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"ergebnis/composer-normalize": "^2.6",
"fakerphp/faker": "^1.5",
"hamcrest/hamcrest-php": "^2",
"jangregor/phpstan-prophecy": "^0.8",
"mockery/mockery": "^1.3",
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1",
"phpstan/phpstan": "^0.12.32",
"phpstan/phpstan-mockery": "^0.12.5",
"phpstan/phpstan-phpunit": "^0.12.11",
"phpunit/phpunit": "^8.5 || ^9",
"psy/psysh": "^0.10.4",
"slevomat/coding-standard": "^6.3",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.4"
},
"config": {
"sort-packages": true
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
},
"minimum-stability": "RC",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Ramsey\\Collection\\": "src/"
@@ -51,7 +53,6 @@
},
"autoload-dev": {
"psr-4": {
"Ramsey\\Console\\": "resources/console/",
"Ramsey\\Collection\\Test\\": "tests/",
"Ramsey\\Test\\Generics\\": "tests/generics/"
},
@@ -59,44 +60,61 @@
"vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php"
]
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"ergebnis/composer-normalize": true,
"phpstan/extension-installer": true,
"captainhook/plugin-composer": true
},
"sort-packages": true
},
"extra": {
"captainhook": {
"force-install": true
},
"ramsey/conventional-commits": {
"configFile": "conventional-commits.json"
}
},
"scripts": {
"post-autoload-dump": "captainhook install --ansi -f -s",
"dev:analyze": [
"@dev:analyze:phpstan",
"@dev:analyze:psalm"
],
"dev:analyze:phpstan": "phpstan --memory-limit=1G analyse",
"dev:analyze:psalm": "psalm --diff --config=psalm.xml",
"dev:build:clean": "git clean -fX build/.",
"dev:build:clear-cache": "git clean -fX build/cache/.",
"dev:lint": "phpcs --cache=build/cache/phpcs.cache",
"dev:lint:fix": "./bin/lint-fix.sh",
"dev:repl": [
"echo ; echo 'Type ./bin/repl to start the REPL.'"
"dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G",
"dev:analyze:psalm": "psalm",
"dev:build:clean": "git clean -fX build/",
"dev:lint": [
"@dev:lint:syntax",
"@dev:lint:style"
],
"dev:test": "phpunit",
"dev:test:all": [
"dev:lint:fix": "phpcbf",
"dev:lint:style": "phpcs --colors",
"dev:lint:syntax": "parallel-lint --colors src/ tests/",
"dev:test": [
"@dev:lint",
"@dev:analyze",
"@dev:test"
"@dev:test:unit"
],
"dev:test:coverage:ci": "phpunit --coverage-clover build/logs/clover.xml",
"dev:test:coverage:html": "phpunit --coverage-html build/coverage",
"test": "@dev:test:all"
"dev:test:coverage:ci": "phpunit --colors=always --coverage-text --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml",
"dev:test:coverage:html": "phpunit --colors=always --coverage-html build/coverage/coverage-html/",
"dev:test:unit": "phpunit --colors=always",
"test": "@dev:test"
},
"scripts-descriptions": {
"dev:analyze": "Performs static analysis on the code base.",
"dev:analyze": "Runs all static analysis checks.",
"dev:analyze:phpstan": "Runs the PHPStan static analyzer.",
"dev:analyze:psalm": "Runs the Psalm static analyzer.",
"dev:build:clean": "Removes everything not under version control from the build directory.",
"dev:build:clear-cache": "Removes everything not under version control from build/cache/.",
"dev:lint": "Checks all source code for coding standards issues.",
"dev:lint:fix": "Checks source code for coding standards issues and fixes them, if possible.",
"dev:repl": "Note: Use ./bin/repl to run the REPL.",
"dev:test": "Runs the full unit test suite.",
"dev:test:all": "Runs linting, static analysis, and unit tests.",
"dev:test:coverage:ci": "Runs the unit test suite and generates a Clover coverage report.",
"dev:test:coverage:html": "Runs the unit tests suite and generates an HTML coverage report.",
"test": "Shortcut to run the full test suite."
"dev:build:clean": "Cleans the build/ directory.",
"dev:lint": "Runs all linting checks.",
"dev:lint:fix": "Auto-fixes coding standards issues, if possible.",
"dev:lint:style": "Checks for coding standards issues.",
"dev:lint:syntax": "Checks for syntax errors.",
"dev:test": "Runs linting, static analysis, and unit tests.",
"dev:test:coverage:ci": "Runs unit tests and generates CI coverage reports.",
"dev:test:coverage:html": "Runs unit tests and generates HTML coverage report.",
"dev:test:unit": "Runs unit tests.",
"test": "Runs linting, static analysis, and unit tests."
}
}

View File

@@ -17,6 +17,7 @@ namespace Ramsey\Collection;
use ArrayIterator;
use Traversable;
use function count;
use function serialize;
use function unserialize;
@@ -34,7 +35,7 @@ abstract class AbstractArray implements ArrayInterface
*
* @var array<array-key, T>
*/
protected $data = [];
protected array $data = [];
/**
* Constructs a new array object.
@@ -83,8 +84,6 @@ abstract class AbstractArray implements ArrayInterface
*
* @return T|null the value stored at the offset, or null if the offset
* does not exist.
*
* @psalm-suppress InvalidAttribute
*/
#[\ReturnTypeWillChange] // phpcs:ignore
public function offsetGet($offset)

View File

@@ -33,7 +33,9 @@ use function current;
use function end;
use function in_array;
use function is_int;
use function is_object;
use function reset;
use function spl_object_id;
use function sprintf;
use function unserialize;
use function usort;
@@ -78,7 +80,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
if ($this->checkType($this->getType(), $value) === false) {
throw new InvalidArgumentException(
'Value must be of type ' . $this->getType() . '; value is '
. $this->toolValueToString($value)
. $this->toolValueToString($value),
);
}
@@ -95,7 +97,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
public function remove($element): bool
{
if (($position = array_search($element, $this->data, true)) !== false) {
unset($this->data[$position]);
unset($this[$position]);
return true;
}
@@ -176,7 +178,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
$bValue = $this->extractValue($b, $propertyOrMethod);
return ($aValue <=> $bValue) * ($order === self::SORT_DESC ? -1 : 1);
}
},
);
return $collection;
@@ -244,15 +246,19 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
foreach ($collections as $index => $collection) {
if (!$collection instanceof static) {
throw new CollectionMismatchException(
sprintf('Collection with index %d must be of type %s', $index, static::class)
sprintf('Collection with index %d must be of type %s', $index, static::class),
);
}
// When using generics (Collection.php, Set.php, etc),
// we also need to make sure that the internal types match each other
if ($collection->getType() !== $this->getType()) {
if ($this->getUniformType($collection) !== $this->getUniformType($this)) {
throw new CollectionMismatchException(
sprintf('Collection items in collection with index %d must be of type %s', $index, $this->getType())
sprintf(
'Collection items in collection with index %d must be of type %s',
$index,
$this->getType(),
),
);
}
@@ -290,7 +296,7 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
// When using generics (Collection.php, Set.php, etc),
// we also need to make sure that the internal types match each other
if ($other->getType() !== $this->getType()) {
if ($this->getUniformType($other) !== $this->getUniformType($this)) {
throw new CollectionMismatchException('Collection items must be of type ' . $this->getType());
}
}
@@ -315,4 +321,21 @@ abstract class AbstractCollection extends AbstractArray implements CollectionInt
return $a === $b ? 0 : ($a < $b ? 1 : -1);
};
}
/**
* @param CollectionInterface<mixed> $collection
*/
private function getUniformType(CollectionInterface $collection): string
{
switch ($collection->getType()) {
case 'integer':
return 'int';
case 'boolean':
return 'bool';
case 'double':
return 'float';
default:
return $collection->getType();
}
}
}

View File

@@ -80,10 +80,8 @@ class Collection extends AbstractCollection
*
* A collection's type is immutable once it is set. For this reason, this
* property is set private.
*
* @var string
*/
private $collectionType;
private string $collectionType;
/**
* Constructs a collection object of the specified type, optionally with the

View File

@@ -151,6 +151,7 @@ interface CollectionInterface extends ArrayInterface
*
* @return CollectionInterface<T>
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
public function where(string $propertyOrMethod, $value): self;
/**

View File

@@ -29,10 +29,8 @@ class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
{
/**
* Index of the last element in the queue.
*
* @var int
*/
private $tail = -1;
private int $tail = -1;
/**
* @inheritDoc
@@ -42,7 +40,7 @@ class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
if ($this->checkType($this->getType(), $value) === false) {
throw new InvalidArgumentException(
'Value must be of type ' . $this->getType() . '; value is '
. $this->toolValueToString($value)
. $this->toolValueToString($value),
);
}
@@ -52,6 +50,8 @@ class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
}
/**
* @throws InvalidArgumentException if $element is of the wrong type
*
* @inheritDoc
*/
public function addFirst($element): bool
@@ -59,7 +59,7 @@ class DoubleEndedQueue extends Queue implements DoubleEndedQueueInterface
if ($this->checkType($this->getType(), $element) === false) {
throw new InvalidArgumentException(
'Value must be of type ' . $this->getType() . '; value is '
. $this->toolValueToString($element)
. $this->toolValueToString($element),
);
}

View File

@@ -15,6 +15,7 @@ declare(strict_types=1);
namespace Ramsey\Collection;
use Ramsey\Collection\Exception\NoSuchElementException;
use RuntimeException;
/**
* A linear collection that supports element insertion and removal at both ends.
@@ -175,7 +176,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @return bool `true` if this queue changed as a result of the call.
*
* @throws \RuntimeException if a queue refuses to add a particular element
* @throws RuntimeException if a queue refuses to add a particular element
* for any reason other than that it already contains the element.
* Implementations should use a more-specific exception that extends
* `\RuntimeException`.
@@ -196,7 +197,7 @@ interface DoubleEndedQueueInterface extends QueueInterface
*
* @return bool `true` if this queue changed as a result of the call.
*
* @throws \RuntimeException if a queue refuses to add a particular element
* @throws RuntimeException if a queue refuses to add a particular element
* for any reason other than that it already contains the element.
* Implementations should use a more-specific exception that extends
* `\RuntimeException`.

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown when attempting to operate on collections of differing types.
*/
class CollectionMismatchException extends \RuntimeException
class CollectionMismatchException extends RuntimeException
{
}

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown when attempting to use a sort order that is not recognized.
*/
class InvalidSortOrderException extends \RuntimeException
class InvalidSortOrderException extends RuntimeException
{
}

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown when attempting to access an element that does not exist.
*/
class NoSuchElementException extends \RuntimeException
class NoSuchElementException extends RuntimeException
{
}

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown to indicate that the requested operation is not supported.
*/
class UnsupportedOperationException extends \RuntimeException
class UnsupportedOperationException extends RuntimeException
{
}

View File

@@ -14,9 +14,11 @@ declare(strict_types=1);
namespace Ramsey\Collection\Exception;
use RuntimeException;
/**
* Thrown when attempting to extract a value for a method or property that does not exist.
*/
class ValueExtractionException extends \RuntimeException
class ValueExtractionException extends RuntimeException
{
}

View File

@@ -20,6 +20,7 @@ use Ramsey\Collection\Exception\InvalidArgumentException;
use function array_key_exists;
use function array_keys;
use function in_array;
use function var_export;
/**
* This class provides a basic implementation of `MapInterface`, to minimize the
@@ -39,7 +40,7 @@ abstract class AbstractMap extends AbstractArray implements MapInterface
if ($offset === null) {
throw new InvalidArgumentException(
'Map elements are key/value pairs; a key must be provided for '
. 'value ' . var_export($value, true)
. 'value ' . var_export($value, true),
);
}

View File

@@ -18,11 +18,13 @@ use Ramsey\Collection\Exception\InvalidArgumentException;
use Ramsey\Collection\Tool\TypeTrait;
use Ramsey\Collection\Tool\ValueToStringTrait;
use function var_export;
/**
* This class provides a basic implementation of `TypedMapInterface`, to
* minimize the effort required to implement this interface.
*
* @template K
* @template K of array-key
* @template T
* @extends AbstractMap<T>
* @implements TypedMapInterface<T>
@@ -37,33 +39,30 @@ abstract class AbstractTypedMap extends AbstractMap implements TypedMapInterface
* @param T $value
*
* @inheritDoc
*
* @psalm-suppress MoreSpecificImplementedParamType
*/
public function offsetSet($offset, $value): void
{
if ($offset === null) {
throw new InvalidArgumentException(
'Map elements are key/value pairs; a key must be provided for '
. 'value ' . var_export($value, true)
. 'value ' . var_export($value, true),
);
}
if ($this->checkType($this->getKeyType(), $offset) === false) {
throw new InvalidArgumentException(
'Key must be of type ' . $this->getKeyType() . '; key is '
. $this->toolValueToString($offset)
. $this->toolValueToString($offset),
);
}
if ($this->checkType($this->getValueType(), $value) === false) {
throw new InvalidArgumentException(
'Value must be of type ' . $this->getValueType() . '; value is '
. $this->toolValueToString($value)
. $this->toolValueToString($value),
);
}
/** @psalm-suppress MixedArgumentTypeCoercion */
parent::offsetSet($offset, $value);
}
}

View File

@@ -21,6 +21,7 @@ use Ramsey\Collection\Tool\ValueToStringTrait;
use function array_combine;
use function array_key_exists;
use function is_int;
use function var_export;
/**
* `NamedParameterMap` represents a mapping of values to a set of named keys
@@ -38,7 +39,7 @@ class NamedParameterMap extends AbstractMap
*
* @var array<string, string>
*/
protected $namedParameters;
protected array $namedParameters;
/**
* Constructs a new `NamedParameterMap`.
@@ -70,14 +71,14 @@ class NamedParameterMap extends AbstractMap
if ($offset === null) {
throw new InvalidArgumentException(
'Map elements are key/value pairs; a key must be provided for '
. 'value ' . var_export($value, true)
. 'value ' . var_export($value, true),
);
}
if (!array_key_exists($offset, $this->namedParameters)) {
throw new InvalidArgumentException(
'Attempting to set value for unconfigured parameter \''
. $offset . '\''
. $offset . '\'',
);
}
@@ -85,7 +86,7 @@ class NamedParameterMap extends AbstractMap
throw new InvalidArgumentException(
'Value for \'' . $offset . '\' must be of type '
. $this->namedParameters[$offset] . '; value is '
. $this->toolValueToString($value)
. $this->toolValueToString($value),
);
}

View File

@@ -20,7 +20,7 @@ use Ramsey\Collection\Tool\TypeTrait;
* A `TypedMap` represents a map of elements where key and value are typed.
*
* Each element is identified by a key with defined type and a value of defined
* type. The keys of the map must be unique. The values on the map can be=
* type. The keys of the map must be unique. The values on the map can be
* repeated but each with its own different key.
*
* The most common case is to use a string type key, but it's not limited to
@@ -80,7 +80,7 @@ use Ramsey\Collection\Tool\TypeTrait;
* }
* ```
*
* @template K
* @template K of array-key
* @template T
* @extends AbstractTypedMap<K, T>
*/
@@ -93,20 +93,16 @@ class TypedMap extends AbstractTypedMap
*
* A map key's type is immutable once it is set. For this reason, this
* property is set private.
*
* @var string data type of the map key.
*/
private $keyType;
private string $keyType;
/**
* The data type of values stored in this collection.
*
* A map value's type is immutable once it is set. For this reason, this
* property is set private.
*
* @var string data type of the map value.
*/
private $valueType;
private string $valueType;
/**
* Constructs a map object of the specified key and value types,
@@ -121,7 +117,6 @@ class TypedMap extends AbstractTypedMap
$this->keyType = $keyType;
$this->valueType = $valueType;
/** @psalm-suppress MixedArgumentTypeCoercion */
parent::__construct($data);
}

View File

@@ -37,17 +37,13 @@ class Queue extends AbstractArray implements QueueInterface
*
* A queue's type is immutable once it is set. For this reason, this
* property is set private.
*
* @var string
*/
private $queueType;
private string $queueType;
/**
* The index of the head of the queue.
*
* @var int
*/
protected $index = 0;
protected int $index = 0;
/**
* Constructs a queue object of the specified type, optionally with the
@@ -68,13 +64,15 @@ class Queue extends AbstractArray implements QueueInterface
* Since arbitrary offsets may not be manipulated in a queue, this method
* serves only to fulfill the `ArrayAccess` interface requirements. It is
* invoked by other operations when adding values to the queue.
*
* @throws InvalidArgumentException if $value is of the wrong type
*/
public function offsetSet($offset, $value): void
{
if ($this->checkType($this->getType(), $value) === false) {
throw new InvalidArgumentException(
'Value must be of type ' . $this->getType() . '; value is '
. $this->toolValueToString($value)
. $this->toolValueToString($value),
);
}
@@ -82,6 +80,8 @@ class Queue extends AbstractArray implements QueueInterface
}
/**
* @throws InvalidArgumentException if $value is of the wrong type
*
* @inheritDoc
*/
public function add($element): bool
@@ -100,7 +100,7 @@ class Queue extends AbstractArray implements QueueInterface
if ($element === null) {
throw new NoSuchElementException(
'Can\'t return element from Queue. Queue is empty.'
'Can\'t return element from Queue. Queue is empty.',
);
}

View File

@@ -15,6 +15,7 @@ declare(strict_types=1);
namespace Ramsey\Collection;
use Ramsey\Collection\Exception\NoSuchElementException;
use RuntimeException;
/**
* A queue is a collection in which the entities in the collection are kept in
@@ -123,7 +124,7 @@ interface QueueInterface extends ArrayInterface
*
* @return bool `true` if this queue changed as a result of the call.
*
* @throws \RuntimeException if a queue refuses to add a particular element
* @throws RuntimeException if a queue refuses to add a particular element
* for any reason other than that it already contains the element.
* Implementations should use a more-specific exception that extends
* `\RuntimeException`.

View File

@@ -44,10 +44,8 @@ class Set extends AbstractSet
* The type of elements stored in this set
*
* A set's type is immutable. For this reason, this property is private.
*
* @var string
*/
private $setType;
private string $setType;
/**
* Constructs a set object of the specified type, optionally with the

View File

@@ -36,6 +36,7 @@ trait TypeTrait
* @param string $type The type to check the value against.
* @param mixed $value The value to check.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
protected function checkType(string $type, $value): bool
{
switch ($type) {

View File

@@ -17,6 +17,7 @@ namespace Ramsey\Collection\Tool;
use Ramsey\Collection\Exception\ValueExtractionException;
use function get_class;
use function is_object;
use function method_exists;
use function property_exists;
use function sprintf;
@@ -37,6 +38,7 @@ trait ValueExtractorTrait
*
* @throws ValueExtractionException if the method or property is not defined.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
protected function extractValue($object, string $propertyOrMethod)
{
if (!is_object($object)) {
@@ -52,7 +54,8 @@ trait ValueExtractorTrait
}
throw new ValueExtractionException(
sprintf('Method or property "%s" not defined in %s', $propertyOrMethod, get_class($object))
// phpcs:ignore SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall
sprintf('Method or property "%s" not defined in %s', $propertyOrMethod, get_class($object)),
);
}
}

View File

@@ -21,8 +21,10 @@ use function get_resource_type;
use function is_array;
use function is_bool;
use function is_callable;
use function is_object;
use function is_resource;
use function is_scalar;
use function var_export;
/**
* Provides functionality to express a value as string
@@ -44,6 +46,7 @@ trait ValueToStringTrait
*
* @param mixed $value the value to return as a string.
*/
// phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
protected function toolValueToString($value): string
{
// null
@@ -89,6 +92,7 @@ trait ValueToStringTrait
}
// unknown type
// phpcs:ignore SlevomatCodingStandard.Classes.ModernClassNameReference.ClassNameReferencedViaFunctionCall
return '(' . get_class($value) . ' Object)';
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2012-2022 Ben Ramsey <ben@benramsey.com>
Copyright (c) 2012-2023 Ben Ramsey <ben@benramsey.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -8,9 +8,9 @@
<a href="https://github.com/ramsey/uuid"><img src="http://img.shields.io/badge/source-ramsey/uuid-blue.svg?style=flat-square" alt="Source Code"></a>
<a href="https://packagist.org/packages/ramsey/uuid"><img src="https://img.shields.io/packagist/v/ramsey/uuid.svg?style=flat-square&label=release" alt="Download Package"></a>
<a href="https://php.net"><img src="https://img.shields.io/packagist/php-v/ramsey/uuid.svg?style=flat-square&colorB=%238892BF" alt="PHP Programming Language"></a>
<a href="https://github.com/ramsey/uuid/blob/main/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/uuid.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a>
<a href="https://github.com/ramsey/uuid/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/workflow/status/ramsey/uuid/build/main?logo=github&style=flat-square" alt="Build Status"></a>
<a href="https://codecov.io/gh/ramsey/uuid"><img src="https://img.shields.io/codecov/c/gh/ramsey/uuid?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a>
<a href="https://github.com/ramsey/uuid/blob/4.x/LICENSE"><img src="https://img.shields.io/packagist/l/ramsey/uuid.svg?style=flat-square&colorB=darkcyan" alt="Read License"></a>
<a href="https://github.com/ramsey/uuid/actions/workflows/continuous-integration.yml"><img src="https://img.shields.io/github/actions/workflow/status/ramsey/uuid/continuous-integration.yml?branch=4.x&logo=github&style=flat-square" alt="Build Status"></a>
<a href="https://app.codecov.io/gh/ramsey/uuid/branch/4.x"><img src="https://img.shields.io/codecov/c/github/ramsey/uuid/4.x?label=codecov&logo=codecov&style=flat-square" alt="Codecov Code Coverage"></a>
<a href="https://shepherd.dev/github/ramsey/uuid"><img src="https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fshepherd.dev%2Fgithub%2Framsey%2Fuuid%2Fcoverage" alt="Psalm Type Coverage"></a>
</p>
@@ -38,7 +38,7 @@ composer require ramsey/uuid
See the documentation for a thorough upgrade guide:
* [Upgrading ramsey/uuid Version 3 to 4](https://uuid.ramsey.dev/en/latest/upgrading/3-to-4.html)
* [Upgrading ramsey/uuid Version 3 to 4](https://uuid.ramsey.dev/en/stable/upgrading/3-to-4.html)
## Documentation
@@ -74,10 +74,10 @@ licensed for use under the MIT License (MIT). Please see [LICENSE][] for more
information.
[rfc4122]: http://tools.ietf.org/html/rfc4122
[conduct]: https://github.com/ramsey/uuid/blob/main/CODE_OF_CONDUCT.md
[conduct]: https://github.com/ramsey/uuid/blob/4.x/CODE_OF_CONDUCT.md
[javauuid]: http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html
[pyuuid]: http://docs.python.org/3/library/uuid.html
[composer]: http://getcomposer.org/
[contributing.md]: https://github.com/ramsey/uuid/blob/main/CONTRIBUTING.md
[security.md]: https://github.com/ramsey/uuid/blob/main/SECURITY.md
[license]: https://github.com/ramsey/uuid/blob/main/LICENSE
[contributing.md]: https://github.com/ramsey/uuid/blob/4.x/CONTRIBUTING.md
[security.md]: https://github.com/ramsey/uuid/blob/4.x/SECURITY.md
[license]: https://github.com/ramsey/uuid/blob/4.x/LICENSE

View File

@@ -10,10 +10,9 @@
],
"require": {
"php": "^8.0",
"ext-ctype": "*",
"ext-json": "*",
"brick/math": "^0.8 || ^0.9",
"ramsey/collection": "^1.0"
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
"ramsey/collection": "^1.2 || ^2.0"
},
"require-dev": {
"captainhook/captainhook": "^5.10",
@@ -22,18 +21,18 @@
"doctrine/annotations": "^1.8",
"ergebnis/composer-normalize": "^2.15",
"mockery/mockery": "^1.3",
"moontoast/math": "^1.1",
"paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2",
"php-mock/php-mock-mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.1",
"phpbench/phpbench": "^1.0",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-mockery": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9",
"slevomat/coding-standard": "^7.0",
"ramsey/composer-repl": "^1.4",
"slevomat/coding-standard": "^8.4",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.9"
},
@@ -42,7 +41,6 @@
},
"suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
"ext-ctype": "Enables faster processing of character classification using ctype functions.",
"ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.",
"ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.",
"paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter",
@@ -70,7 +68,8 @@
"captainhook/plugin-composer": true,
"ergebnis/composer-normalize": true,
"phpstan/extension-installer": true,
"dealerdirect/phpcodesniffer-composer-installer": true
"dealerdirect/phpcodesniffer-composer-installer": true,
"ramsey/composer-repl": true
},
"sort-packages": true
},
@@ -91,8 +90,8 @@
"phpcbf": "phpcbf -vpw --cache=build/cache/phpcs.cache",
"phpcs": "phpcs --cache=build/cache/phpcs.cache",
"phpstan": [
"phpstan analyse --no-progress",
"phpstan analyse -c phpstan-tests.neon --no-progress"
"phpstan analyse --no-progress --memory-limit=1G",
"phpstan analyse -c phpstan-tests.neon --no-progress --memory-limit=1G"
],
"phpunit": "phpunit --verbose --colors=always",
"phpunit-coverage": "phpunit --verbose --colors=always --coverage-html build/coverage",

View File

@@ -30,15 +30,7 @@ use Ramsey\Uuid\UuidInterface;
*/
class DegradedUuidBuilder implements UuidBuilderInterface
{
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
private TimeConverterInterface $timeConverter;
/**
* @param NumberConverterInterface $numberConverter The number converter to
@@ -47,10 +39,9 @@ class DegradedUuidBuilder implements UuidBuilderInterface
* for converting timestamps extracted from a UUID to Unix timestamps
*/
public function __construct(
NumberConverterInterface $numberConverter,
private NumberConverterInterface $numberConverter,
?TimeConverterInterface $timeConverter = null
) {
$this->numberConverter = $numberConverter;
$this->timeConverter = $timeConverter ?: new DegradedTimeConverter();
}

View File

@@ -27,17 +27,11 @@ use Ramsey\Uuid\UuidInterface;
*/
class FallbackBuilder implements UuidBuilderInterface
{
/**
* @var iterable<UuidBuilderInterface>
*/
private $builders;
/**
* @param iterable<UuidBuilderInterface> $builders An array of UUID builders
*/
public function __construct(iterable $builders)
public function __construct(private iterable $builders)
{
$this->builders = $builders;
}
/**

View File

@@ -18,6 +18,7 @@ use Ramsey\Uuid\Guid\Guid;
use Ramsey\Uuid\UuidInterface;
use function bin2hex;
use function sprintf;
use function substr;
/**
@@ -29,6 +30,26 @@ use function substr;
*/
class GuidStringCodec extends StringCodec
{
public function encode(UuidInterface $uuid): string
{
$hex = bin2hex($uuid->getFields()->getBytes());
/** @var non-empty-string */
return sprintf(
'%02s%02s%02s%02s-%02s%02s-%02s%02s-%04s-%012s',
substr($hex, 6, 2),
substr($hex, 4, 2),
substr($hex, 2, 2),
substr($hex, 0, 2),
substr($hex, 10, 2),
substr($hex, 8, 2),
substr($hex, 14, 2),
substr($hex, 12, 2),
substr($hex, 16, 4),
substr($hex, 20),
);
}
public function decode(string $encodedUuid): UuidInterface
{
$bytes = $this->getBytes($encodedUuid);

View File

@@ -17,12 +17,13 @@ namespace Ramsey\Uuid\Codec;
use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Exception\InvalidUuidStringException;
use Ramsey\Uuid\Rfc4122\FieldsInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use function bin2hex;
use function hex2bin;
use function implode;
use function sprintf;
use function str_replace;
use function strlen;
use function substr;
@@ -36,36 +37,28 @@ use function substr;
*/
class StringCodec implements CodecInterface
{
/**
* @var UuidBuilderInterface
*/
private $builder;
/**
* Constructs a StringCodec
*
* @param UuidBuilderInterface $builder The builder to use when encoding UUIDs
*/
public function __construct(UuidBuilderInterface $builder)
public function __construct(private UuidBuilderInterface $builder)
{
$this->builder = $builder;
}
public function encode(UuidInterface $uuid): string
{
/** @var FieldsInterface $fields */
$fields = $uuid->getFields();
$hex = bin2hex($uuid->getFields()->getBytes());
return $fields->getTimeLow()->toString()
. '-'
. $fields->getTimeMid()->toString()
. '-'
. $fields->getTimeHiAndVersion()->toString()
. '-'
. $fields->getClockSeqHiAndReserved()->toString()
. $fields->getClockSeqLow()->toString()
. '-'
. $fields->getNode()->toString();
/** @var non-empty-string */
return sprintf(
'%08s-%04s-%04s-%04s-%012s',
substr($hex, 0, 8),
substr($hex, 8, 4),
substr($hex, 12, 4),
substr($hex, 16, 4),
substr($hex, 20),
);
}
/**

View File

@@ -27,10 +27,7 @@ use Ramsey\Uuid\Math\BrickMathCalculator;
*/
class BigNumberConverter implements NumberConverterInterface
{
/**
* @var NumberConverterInterface
*/
private $converter;
private NumberConverterInterface $converter;
public function __construct()
{

View File

@@ -26,14 +26,8 @@ use Ramsey\Uuid\Type\Integer as IntegerObject;
*/
class GenericNumberConverter implements NumberConverterInterface
{
/**
* @var CalculatorInterface
*/
private $calculator;
public function __construct(CalculatorInterface $calculator)
public function __construct(private CalculatorInterface $calculator)
{
$this->calculator = $calculator;
}
/**

View File

@@ -29,10 +29,7 @@ use Ramsey\Uuid\Type\Time;
*/
class BigNumberTimeConverter implements TimeConverterInterface
{
/**
* @var TimeConverterInterface
*/
private $converter;
private TimeConverterInterface $converter;
public function __construct()
{

View File

@@ -50,14 +50,8 @@ class GenericTimeConverter implements TimeConverterInterface
*/
private const MICROSECOND_INTERVALS = '10';
/**
* @var CalculatorInterface
*/
private $calculator;
public function __construct(CalculatorInterface $calculator)
public function __construct(private CalculatorInterface $calculator)
{
$this->calculator = $calculator;
}
public function calculateTime(string $seconds, string $microseconds): Hexadecimal

View File

@@ -58,20 +58,9 @@ class PhpTimeConverter implements TimeConverterInterface
*/
private const MICROSECOND_INTERVALS = 10;
/**
* @var CalculatorInterface
*/
private $calculator;
/**
* @var TimeConverterInterface
*/
private $fallbackConverter;
/**
* @var int
*/
private $phpPrecision;
private int $phpPrecision;
private CalculatorInterface $calculator;
private TimeConverterInterface $fallbackConverter;
public function __construct(
?CalculatorInterface $calculator = null,
@@ -132,11 +121,11 @@ class PhpTimeConverter implements TimeConverterInterface
}
/**
* @param int|float $time The time to split into seconds and microseconds
* @param float|int $time The time to split into seconds and microseconds
*
* @return string[]
*/
private function splitTime($time): array
private function splitTime(float | int $time): array
{
$split = explode('.', (string) $time, 2);

View File

@@ -18,8 +18,7 @@ use DateTimeInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
/**
* This interface encapsulates deprecated methods for ramsey/uuid; this
* interface and its methods will be removed in ramsey/uuid 5.0.0.
* This interface encapsulates deprecated methods for ramsey/uuid
*
* @psalm-immutable
*/
@@ -123,12 +122,6 @@ interface DeprecatedUuidInterface
*/
public function getTimestampHex(): string;
/**
* @deprecated In ramsey/uuid version 5.0.0, this will be removed from this
* interface. It has moved to {@see \Ramsey\Uuid\Rfc4122\UuidInterface::getUrn()}.
*/
public function getUrn(): string;
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a

View File

@@ -17,10 +17,8 @@ namespace Ramsey\Uuid;
use DateTimeImmutable;
use DateTimeInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\DateTimeException;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Throwable;
use function str_pad;
@@ -32,29 +30,17 @@ use const STR_PAD_LEFT;
* This trait encapsulates deprecated methods for ramsey/uuid; this trait and
* its methods will be removed in ramsey/uuid 5.0.0.
*
* @deprecated This trait and its methods will be removed in ramsey/uuid 5.0.0.
*
* @psalm-immutable
*/
trait DeprecatedUuidMethodsTrait
{
/**
* @var Rfc4122FieldsInterface
*/
protected $fields;
/**
* @var NumberConverterInterface
*/
protected $numberConverter;
/**
* @var TimeConverterInterface
*/
protected $timeConverter;
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getClockSeqHiAndReserved()}
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
*/
@@ -65,8 +51,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getClockSeqHiAndReserved()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqHiAndReserved()}.
*/
public function getClockSeqHiAndReservedHex(): string
{
@@ -75,8 +62,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getClockSeqLow()}
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
*/
@@ -87,8 +75,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getClockSeqLow()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeqLow()}.
*/
public function getClockSeqLowHex(): string
{
@@ -97,8 +86,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getClockSeq()}
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
*/
@@ -109,8 +99,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getClockSeq()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getClockSeq()}.
*/
public function getClockSequenceHex(): string
{
@@ -157,7 +148,7 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance.
*
* @return string[]
*/
@@ -219,10 +210,11 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getNode()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()} and use the
* arbitrary-precision math library of your choice to convert it to a
* string integer.
*/
public function getNode(): string
{
@@ -231,8 +223,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getNode()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getNode()}.
*/
public function getNodeHex(): string
{
@@ -241,8 +234,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimeHiAndVersion()}
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
*/
@@ -253,8 +247,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimeHiAndVersion()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeHiAndVersion()}.
*/
public function getTimeHiAndVersionHex(): string
{
@@ -263,10 +258,11 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimeLow()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()} and use the
* arbitrary-precision math library of your choice to convert it to a
* string integer.
*/
public function getTimeLow(): string
{
@@ -275,8 +271,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimeLow()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeLow()}.
*/
public function getTimeLowHex(): string
{
@@ -285,10 +282,11 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimeMid()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()} and use the
* arbitrary-precision math library of your choice to convert it to a
* string integer.
*/
public function getTimeMid(): string
{
@@ -297,8 +295,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimeMid()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimeMid()}.
*/
public function getTimeMidHex(): string
{
@@ -307,10 +306,11 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimestamp()}
* and use the arbitrary-precision math library of your choice to
* convert it to a string integer.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()} and use
* the arbitrary-precision math library of your choice to convert it to
* a string integer.
*/
public function getTimestamp(): string
{
@@ -323,8 +323,9 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a {@see Rfc4122FieldsInterface}
* instance, you may call {@see Rfc4122FieldsInterface::getTimestamp()}.
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getTimestamp()}.
*/
public function getTimestampHex(): string
{
@@ -335,20 +336,9 @@ trait DeprecatedUuidMethodsTrait
return $this->fields->getTimestamp()->toString();
}
/**
* @deprecated This has moved to {@see Rfc4122FieldsInterface::getUrn()} and
* is available on {@see \Ramsey\Uuid\Rfc4122\UuidV1},
* {@see \Ramsey\Uuid\Rfc4122\UuidV3}, {@see \Ramsey\Uuid\Rfc4122\UuidV4},
* and {@see \Ramsey\Uuid\Rfc4122\UuidV5}.
*/
public function getUrn(): string
{
return 'urn:uuid:' . $this->toString();
}
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVariant()}.
*/
@@ -359,7 +349,7 @@ trait DeprecatedUuidMethodsTrait
/**
* @deprecated Use {@see UuidInterface::getFields()} to get a
* {@see FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Fields\FieldsInterface} instance. If it is a
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface} instance, you may call
* {@see \Ramsey\Uuid\Rfc4122\FieldsInterface::getVersion()}.
*/

View File

@@ -35,6 +35,7 @@ use Ramsey\Uuid\Generator\RandomGeneratorFactory;
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
use Ramsey\Uuid\Generator\TimeGeneratorFactory;
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
use Ramsey\Uuid\Generator\UnixTimeGenerator;
use Ramsey\Uuid\Guid\GuidBuilder;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Math\CalculatorInterface;
@@ -61,92 +62,25 @@ use const PHP_INT_SIZE;
*/
class FeatureSet
{
/**
* @var bool
*/
private $disableBigNumber = false;
/**
* @var bool
*/
private $disable64Bit = false;
/**
* @var bool
*/
private $ignoreSystemNode = false;
/**
* @var bool
*/
private $enablePecl = false;
/**
* @var UuidBuilderInterface
*/
private $builder;
/**
* @var CodecInterface
*/
private $codec;
/**
* @var DceSecurityGeneratorInterface
*/
private $dceSecurityGenerator;
/**
* @var NameGeneratorInterface
*/
private $nameGenerator;
/**
* @var NodeProviderInterface
*/
private $nodeProvider;
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
/**
* @var RandomGeneratorInterface
*/
private $randomGenerator;
/**
* @var TimeGeneratorInterface
*/
private $timeGenerator;
/**
* @var TimeProviderInterface
*/
private $timeProvider;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* @var CalculatorInterface
*/
private $calculator;
private ?TimeProviderInterface $timeProvider = null;
private CalculatorInterface $calculator;
private CodecInterface $codec;
private DceSecurityGeneratorInterface $dceSecurityGenerator;
private NameGeneratorInterface $nameGenerator;
private NodeProviderInterface $nodeProvider;
private NumberConverterInterface $numberConverter;
private RandomGeneratorInterface $randomGenerator;
private TimeConverterInterface $timeConverter;
private TimeGeneratorInterface $timeGenerator;
private TimeGeneratorInterface $unixTimeGenerator;
private UuidBuilderInterface $builder;
private ValidatorInterface $validator;
/**
* @param bool $useGuids True build UUIDs using the GuidStringCodec
* @param bool $force32Bit True to force the use of 32-bit functionality
* (primarily for testing purposes)
* @param bool $forceNoBigNumber True to disable the use of moontoast/math
* (primarily for testing purposes)
* @param bool $forceNoBigNumber (obsolete)
* @param bool $ignoreSystemNode True to disable attempts to check for the
* system node ID (primarily for testing purposes)
* @param bool $enablePecl True to enable the use of the PeclUuidTimeGenerator
@@ -154,25 +88,23 @@ class FeatureSet
*/
public function __construct(
bool $useGuids = false,
bool $force32Bit = false,
private bool $force32Bit = false,
bool $forceNoBigNumber = false,
bool $ignoreSystemNode = false,
bool $enablePecl = false
private bool $ignoreSystemNode = false,
private bool $enablePecl = false
) {
$this->disableBigNumber = $forceNoBigNumber;
$this->disable64Bit = $force32Bit;
$this->ignoreSystemNode = $ignoreSystemNode;
$this->enablePecl = $enablePecl;
$this->randomGenerator = $this->buildRandomGenerator();
$this->setCalculator(new BrickMathCalculator());
$this->builder = $this->buildUuidBuilder($useGuids);
$this->codec = $this->buildCodec($useGuids);
$this->nodeProvider = $this->buildNodeProvider();
$this->nameGenerator = $this->buildNameGenerator();
$this->randomGenerator = $this->buildRandomGenerator();
$this->setTimeProvider(new SystemTimeProvider());
$this->setDceSecurityProvider(new SystemDceSecurityProvider());
$this->validator = new GenericValidator();
assert($this->timeProvider !== null);
$this->unixTimeGenerator = $this->buildUnixTimeGenerator();
}
/**
@@ -255,6 +187,14 @@ class FeatureSet
return $this->timeGenerator;
}
/**
* Returns the Unix Epoch time generator configured for this environment
*/
public function getUnixTimeGenerator(): TimeGeneratorInterface
{
return $this->unixTimeGenerator;
}
/**
* Returns the validator configured for this environment
*/
@@ -292,7 +232,10 @@ class FeatureSet
public function setNodeProvider(NodeProviderInterface $nodeProvider): void
{
$this->nodeProvider = $nodeProvider;
$this->timeGenerator = $this->buildTimeGenerator($this->timeProvider);
if (isset($this->timeProvider)) {
$this->timeGenerator = $this->buildTimeGenerator($this->timeProvider);
}
}
/**
@@ -393,6 +336,14 @@ class FeatureSet
))->getGenerator();
}
/**
* Returns a Unix Epoch time generator configured for this environment
*/
private function buildUnixTimeGenerator(): TimeGeneratorInterface
{
return new UnixTimeGenerator($this->randomGenerator);
}
/**
* Returns a name generator configured for this environment
*/
@@ -441,6 +392,6 @@ class FeatureSet
*/
private function is64BitSystem(): bool
{
return PHP_INT_SIZE === 8 && !$this->disable64Bit;
return PHP_INT_SIZE === 8 && !$this->force32Bit;
}
}

View File

@@ -56,22 +56,23 @@ trait SerializableFieldsTrait
/**
* Constructs the object from a serialized string representation
*
* @param string $serialized The serialized string representation of the object
* @param string $data The serialized string representation of the object
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @psalm-suppress UnusedMethodCall
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
if (strlen($serialized) === 16) {
$this->__construct($serialized);
if (strlen($data) === 16) {
$this->__construct($data);
} else {
$this->__construct(base64_decode($serialized));
$this->__construct(base64_decode($data));
}
}
/**
* @param array{bytes: string} $data
* @param array{bytes?: string} $data
*
* @psalm-suppress UnusedMethodCall
*/
public function __unserialize(array $data): void
{

View File

@@ -61,22 +61,10 @@ class CombGenerator implements RandomGeneratorInterface
{
public const TIMESTAMP_BYTES = 6;
/**
* @var RandomGeneratorInterface
*/
private $randomGenerator;
/**
* @var NumberConverterInterface
*/
private $converter;
public function __construct(
RandomGeneratorInterface $generator,
NumberConverterInterface $numberConverter
private RandomGeneratorInterface $generator,
private NumberConverterInterface $numberConverter
) {
$this->converter = $numberConverter;
$this->randomGenerator = $generator;
}
/**
@@ -95,11 +83,11 @@ class CombGenerator implements RandomGeneratorInterface
$hash = '';
if (self::TIMESTAMP_BYTES > 0 && $length > self::TIMESTAMP_BYTES) {
$hash = $this->randomGenerator->generate($length - self::TIMESTAMP_BYTES);
$hash = $this->generator->generate($length - self::TIMESTAMP_BYTES);
}
$lsbTime = str_pad(
$this->converter->toHex($this->timestamp()),
$this->numberConverter->toHex($this->timestamp()),
self::TIMESTAMP_BYTES * 2,
'0',
STR_PAD_LEFT

View File

@@ -52,29 +52,11 @@ class DceSecurityGenerator implements DceSecurityGeneratorInterface
*/
private const CLOCK_SEQ_LOW = 0;
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var TimeGeneratorInterface
*/
private $timeGenerator;
/**
* @var DceSecurityProviderInterface
*/
private $dceSecurityProvider;
public function __construct(
NumberConverterInterface $numberConverter,
TimeGeneratorInterface $timeGenerator,
DceSecurityProviderInterface $dceSecurityProvider
private NumberConverterInterface $numberConverter,
private TimeGeneratorInterface $timeGenerator,
private DceSecurityProviderInterface $dceSecurityProvider
) {
$this->numberConverter = $numberConverter;
$this->timeGenerator = $timeGenerator;
$this->dceSecurityProvider = $dceSecurityProvider;
}
public function generate(
@@ -153,8 +135,7 @@ class DceSecurityGenerator implements DceSecurityGeneratorInterface
// Replace bytes in the time-based UUID with DCE Security values.
$bytes = substr_replace($bytes, $identifierBytes, 0, 4);
$bytes = substr_replace($bytes, $domainByte, 9, 1);
return $bytes;
return substr_replace($bytes, $domainByte, 9, 1);
}
}

View File

@@ -23,11 +23,11 @@ use Ramsey\Uuid\Provider\TimeProviderInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Throwable;
use function ctype_xdigit;
use function dechex;
use function hex2bin;
use function is_int;
use function pack;
use function preg_match;
use function sprintf;
use function str_pad;
use function strlen;
@@ -40,29 +40,11 @@ use const STR_PAD_LEFT;
*/
class DefaultTimeGenerator implements TimeGeneratorInterface
{
/**
* @var NodeProviderInterface
*/
private $nodeProvider;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
/**
* @var TimeProviderInterface
*/
private $timeProvider;
public function __construct(
NodeProviderInterface $nodeProvider,
TimeConverterInterface $timeConverter,
TimeProviderInterface $timeProvider
private NodeProviderInterface $nodeProvider,
private TimeConverterInterface $timeConverter,
private TimeProviderInterface $timeProvider
) {
$this->nodeProvider = $nodeProvider;
$this->timeConverter = $timeConverter;
$this->timeProvider = $timeProvider;
}
/**
@@ -121,13 +103,13 @@ class DefaultTimeGenerator implements TimeGeneratorInterface
* Uses the node provider given when constructing this instance to get
* the node ID (usually a MAC address)
*
* @param string|int|null $node A node value that may be used to override the node provider
* @param int|string|null $node A node value that may be used to override the node provider
*
* @return string 6-byte binary string representation of the node
*
* @throws InvalidArgumentException
*/
private function getValidNode($node): string
private function getValidNode(int | string | null $node): string
{
if ($node === null) {
$node = $this->nodeProvider->getNode();
@@ -138,7 +120,7 @@ class DefaultTimeGenerator implements TimeGeneratorInterface
$node = dechex($node);
}
if (!ctype_xdigit((string) $node) || strlen((string) $node) > 12) {
if (!preg_match('/^[A-Fa-f0-9]+$/', (string) $node) || strlen((string) $node) > 12) {
throw new InvalidArgumentException('Invalid node value');
}

View File

@@ -33,21 +33,16 @@ class PeclUuidNameGenerator implements NameGeneratorInterface
/** @psalm-pure */
public function generate(UuidInterface $ns, string $name, string $hashAlgorithm): string
{
switch ($hashAlgorithm) {
case 'md5':
$uuid = uuid_generate_md5($ns->toString(), $name);
break;
case 'sha1':
$uuid = uuid_generate_sha1($ns->toString(), $name);
break;
default:
throw new NameException(sprintf(
$uuid = match ($hashAlgorithm) {
'md5' => uuid_generate_md5($ns->toString(), $name),
'sha1' => uuid_generate_sha1($ns->toString(), $name),
default => throw new NameException(
sprintf(
'Unable to hash namespace and name with algorithm \'%s\'',
$hashAlgorithm
));
}
)
),
};
return uuid_parse($uuid);
}

View File

@@ -22,7 +22,7 @@ interface RandomGeneratorInterface
/**
* Generates a string of randomized binary data
*
* @param int $length The number of bytes of random binary data to generate
* @param int<1, max> $length The number of bytes of random binary data to generate
*
* @return string A binary string
*/

View File

@@ -29,10 +29,7 @@ use RandomLib\Generator;
*/
class RandomLibAdapter implements RandomGeneratorInterface
{
/**
* @var Generator
*/
private $generator;
private Generator $generator;
/**
* Constructs a RandomLibAdapter

View File

@@ -24,29 +24,11 @@ use Ramsey\Uuid\Provider\TimeProviderInterface;
*/
class TimeGeneratorFactory
{
/**
* @var NodeProviderInterface
*/
private $nodeProvider;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
/**
* @var TimeProviderInterface
*/
private $timeProvider;
public function __construct(
NodeProviderInterface $nodeProvider,
TimeConverterInterface $timeConverter,
TimeProviderInterface $timeProvider
private NodeProviderInterface $nodeProvider,
private TimeConverterInterface $timeConverter,
private TimeProviderInterface $timeProvider
) {
$this->nodeProvider = $nodeProvider;
$this->timeConverter = $timeConverter;
$this->timeProvider = $timeProvider;
}
/**

View File

@@ -17,6 +17,7 @@ namespace Ramsey\Uuid\Guid;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Fields\SerializableFieldsTrait;
use Ramsey\Uuid\Rfc4122\FieldsInterface;
use Ramsey\Uuid\Rfc4122\MaxTrait;
use Ramsey\Uuid\Rfc4122\NilTrait;
use Ramsey\Uuid\Rfc4122\VariantTrait;
use Ramsey\Uuid\Rfc4122\VersionTrait;
@@ -44,16 +45,12 @@ use const STR_PAD_LEFT;
*/
final class Fields implements FieldsInterface
{
use MaxTrait;
use NilTrait;
use SerializableFieldsTrait;
use VariantTrait;
use VersionTrait;
/**
* @var string
*/
private $bytes;
/**
* @param string $bytes A 16-byte binary string representation of a UUID
*
@@ -61,17 +58,15 @@ final class Fields implements FieldsInterface
* @throws InvalidArgumentException if the byte string does not represent a GUID
* @throws InvalidArgumentException if the byte string does not contain a valid version
*/
public function __construct(string $bytes)
public function __construct(private string $bytes)
{
if (strlen($bytes) !== 16) {
if (strlen($this->bytes) !== 16) {
throw new InvalidArgumentException(
'The byte string must be 16 bytes long; '
. 'received ' . strlen($bytes) . ' bytes'
. 'received ' . strlen($this->bytes) . ' bytes'
);
}
$this->bytes = $bytes;
if (!$this->isCorrectVariant()) {
throw new InvalidArgumentException(
'The byte string received does not conform to the RFC '
@@ -149,7 +144,13 @@ final class Fields implements FieldsInterface
public function getClockSeq(): Hexadecimal
{
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
if ($this->isMax()) {
$clockSeq = 0xffff;
} elseif ($this->isNil()) {
$clockSeq = 0x0000;
} else {
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
}
return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
}
@@ -171,7 +172,7 @@ final class Fields implements FieldsInterface
public function getVersion(): ?int
{
if ($this->isNil()) {
if ($this->isNil() || $this->isMax()) {
return null;
}
@@ -183,7 +184,7 @@ final class Fields implements FieldsInterface
private function isCorrectVariant(): bool
{
if ($this->isNil()) {
if ($this->isNil() || $this->isMax()) {
return true;
}

View File

@@ -31,16 +31,6 @@ use Throwable;
*/
class GuidBuilder implements UuidBuilderInterface
{
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
/**
* @param NumberConverterInterface $numberConverter The number converter to
* use when constructing the Guid
@@ -48,11 +38,9 @@ class GuidBuilder implements UuidBuilderInterface
* for converting timestamps extracted from a UUID to Unix timestamps
*/
public function __construct(
NumberConverterInterface $numberConverter,
TimeConverterInterface $timeConverter
private NumberConverterInterface $numberConverter,
private TimeConverterInterface $timeConverter
) {
$this->numberConverter = $numberConverter;
$this->timeConverter = $timeConverter;
}
/**

View File

@@ -18,8 +18,8 @@ use DateTimeInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Fields\FieldsInterface;
use Ramsey\Uuid\Nonstandard\UuidV6;
use Ramsey\Uuid\Rfc4122\UuidV1;
use Ramsey\Uuid\Rfc4122\UuidV6;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\UuidFactory;
@@ -55,18 +55,14 @@ use function substr;
final class LazyUuidFromString implements UuidInterface
{
public const VALID_REGEX = '/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/ms';
/**
* @var string
* @psalm-var non-empty-string
*/
private $uuid;
/** @var UuidInterface|null */
private $unwrapped;
/** @psalm-param non-empty-string $uuid */
public function __construct(string $uuid)
private ?UuidInterface $unwrapped = null;
/**
* @psalm-param non-empty-string $uuid
*/
public function __construct(private string $uuid)
{
$this->uuid = $uuid;
}
/** @psalm-pure */
@@ -105,19 +101,20 @@ final class LazyUuidFromString implements UuidInterface
/**
* {@inheritDoc}
*
* @param string $serialized
* @param string $data
*
* @psalm-param non-empty-string $serialized
* @psalm-param non-empty-string $data
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
$this->uuid = $serialized;
$this->uuid = $data;
}
/**
* @param array{string: string} $data
* @param array{string?: string} $data
*
* @psalm-param array{string: non-empty-string} $data
* @psalm-param array{string?: non-empty-string} $data
* @psalm-suppress UnusedMethodCall
*/
public function __unserialize(array $data): void
{

View File

@@ -136,9 +136,11 @@ final class BrickMathCalculator implements CalculatorInterface
/**
* Maps ramsey/uuid rounding modes to those used by brick/math
*
* @return BrickMathRounding::*
*/
private function getBrickRoundingMode(int $roundingMode): int
private function getBrickRoundingMode(int $roundingMode)
{
return self::ROUNDING_MODE_MAP[$roundingMode] ?? 0;
return self::ROUNDING_MODE_MAP[$roundingMode] ?? BrickMathRounding::UNNECESSARY;
}
}

View File

@@ -47,26 +47,19 @@ final class Fields implements FieldsInterface
use SerializableFieldsTrait;
use VariantTrait;
/**
* @var string
*/
private $bytes;
/**
* @param string $bytes A 16-byte binary string representation of a UUID
*
* @throws InvalidArgumentException if the byte string is not exactly 16 bytes
*/
public function __construct(string $bytes)
public function __construct(private string $bytes)
{
if (strlen($bytes) !== 16) {
if (strlen($this->bytes) !== 16) {
throw new InvalidArgumentException(
'The byte string must be 16 bytes long; '
. 'received ' . strlen($bytes) . ' bytes'
. 'received ' . strlen($this->bytes) . ' bytes'
);
}
$this->bytes = $bytes;
}
public function getBytes(): string
@@ -130,4 +123,9 @@ final class Fields implements FieldsInterface
{
return false;
}
public function isMax(): bool
{
return false;
}
}

View File

@@ -29,16 +29,6 @@ use Throwable;
*/
class UuidBuilder implements UuidBuilderInterface
{
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
/**
* @param NumberConverterInterface $numberConverter The number converter to
* use when constructing the Nonstandard\Uuid
@@ -46,11 +36,9 @@ class UuidBuilder implements UuidBuilderInterface
* for converting timestamps extracted from a UUID to Unix timestamps
*/
public function __construct(
NumberConverterInterface $numberConverter,
TimeConverterInterface $timeConverter
private NumberConverterInterface $numberConverter,
private TimeConverterInterface $timeConverter
) {
$this->numberConverter = $numberConverter;
$this->timeConverter = $timeConverter;
}
/**

View File

@@ -14,39 +14,34 @@ declare(strict_types=1);
namespace Ramsey\Uuid\Nonstandard;
use DateTimeImmutable;
use DateTimeInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\DateTimeException;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Lazy\LazyUuidFromString;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Rfc4122\TimeTrait;
use Ramsey\Uuid\Rfc4122\UuidInterface;
use Ramsey\Uuid\Rfc4122\UuidV1;
use Ramsey\Uuid\Uuid;
use Throwable;
use function hex2bin;
use function str_pad;
use function substr;
use const STR_PAD_LEFT;
use Ramsey\Uuid\Uuid as BaseUuid;
/**
* Ordered-time, or version 6, UUIDs include timestamp, clock sequence, and node
* values that are combined into a 128-bit unsigned integer
* Reordered time, or version 6, UUIDs include timestamp, clock sequence, and
* node values that are combined into a 128-bit unsigned integer
*
* @deprecated Use {@see \Ramsey\Uuid\Rfc4122\UuidV6} instead.
*
* @link https://github.com/uuid6/uuid6-ietf-draft UUID version 6 IETF draft
* @link http://gh.peabody.io/uuidv6/ "Version 6" UUIDs
*
* @psalm-immutable
*/
final class UuidV6 extends Uuid implements UuidInterface
class UuidV6 extends BaseUuid implements UuidInterface
{
use TimeTrait;
/**
* Creates a version 6 (time-based) UUID
* Creates a version 6 (reordered time) UUID
*
* @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
* @param NumberConverterInterface $numberConverter The number converter to use
@@ -62,39 +57,16 @@ final class UuidV6 extends Uuid implements UuidInterface
CodecInterface $codec,
TimeConverterInterface $timeConverter
) {
if ($fields->getVersion() !== Uuid::UUID_TYPE_PEABODY) {
if ($fields->getVersion() !== Uuid::UUID_TYPE_REORDERED_TIME) {
throw new InvalidArgumentException(
'Fields used to create a UuidV6 must represent a '
. 'version 6 (ordered-time) UUID'
. 'version 6 (reordered time) UUID'
);
}
parent::__construct($fields, $numberConverter, $codec, $timeConverter);
}
/**
* Returns a DateTimeInterface object representing the timestamp associated
* with the UUID
*
* @return DateTimeImmutable A PHP DateTimeImmutable instance representing
* the timestamp of a version 6 UUID
*/
public function getDateTime(): DateTimeInterface
{
$time = $this->timeConverter->convertTime($this->fields->getTimestamp());
try {
return new DateTimeImmutable(
'@'
. $time->getSeconds()->toString()
. '.'
. str_pad($time->getMicroseconds()->toString(), 6, '0', STR_PAD_LEFT)
);
} catch (Throwable $e) {
throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Converts this UUID into an instance of a version 1 UUID
*/
@@ -116,7 +88,7 @@ final class UuidV6 extends Uuid implements UuidInterface
/**
* Converts a version 1 UUID into an instance of a version 6 UUID
*/
public static function fromUuidV1(UuidV1 $uuidV1): UuidV6
public static function fromUuidV1(UuidV1 $uuidV1): \Ramsey\Uuid\Rfc4122\UuidV6
{
$hex = $uuidV1->getHex()->toString();
$hex = substr($hex, 13, 3)

View File

@@ -21,7 +21,6 @@ use Ramsey\Uuid\Type\Integer as IntegerObject;
use function escapeshellarg;
use function preg_split;
use function str_getcsv;
use function strpos;
use function strrpos;
use function strtolower;
use function strtoupper;
@@ -42,6 +41,7 @@ class SystemDceSecurityProvider implements DceSecurityProviderInterface
*/
public function getUid(): IntegerObject
{
/** @var int|float|string|IntegerObject|null $uid */
static $uid = null;
if ($uid instanceof IntegerObject) {
@@ -72,6 +72,7 @@ class SystemDceSecurityProvider implements DceSecurityProviderInterface
*/
public function getGid(): IntegerObject
{
/** @var int|float|string|IntegerObject|null $gid */
static $gid = null;
if ($gid instanceof IntegerObject) {
@@ -104,15 +105,10 @@ class SystemDceSecurityProvider implements DceSecurityProviderInterface
return '';
}
switch ($this->getOs()) {
case 'WIN':
return $this->getWindowsUid();
case 'DAR':
case 'FRE':
case 'LIN':
default:
return trim((string) shell_exec('id -u'));
}
return match ($this->getOs()) {
'WIN' => $this->getWindowsUid(),
default => trim((string) shell_exec('id -u')),
};
}
/**
@@ -124,15 +120,10 @@ class SystemDceSecurityProvider implements DceSecurityProviderInterface
return '';
}
switch ($this->getOs()) {
case 'WIN':
return $this->getWindowsGid();
case 'DAR':
case 'FRE':
case 'LIN':
default:
return trim((string) shell_exec('id -g'));
}
return match ($this->getOs()) {
'WIN' => $this->getWindowsGid(),
default => trim((string) shell_exec('id -g')),
};
}
/**
@@ -142,7 +133,7 @@ class SystemDceSecurityProvider implements DceSecurityProviderInterface
{
$disabledFunctions = strtolower((string) ini_get('disable_functions'));
return strpos($disabledFunctions, 'shell_exec') === false;
return !str_contains($disabledFunctions, 'shell_exec');
}
/**
@@ -150,7 +141,13 @@ class SystemDceSecurityProvider implements DceSecurityProviderInterface
*/
private function getOs(): string
{
return strtoupper(substr(constant('PHP_OS'), 0, 3));
/**
* @psalm-suppress UnnecessaryVarAnnotation
* @var string $phpOs
*/
$phpOs = constant('PHP_OS');
return strtoupper(substr($phpOs, 0, 3));
}
/**

View File

@@ -24,24 +24,18 @@ use Ramsey\Uuid\Type\Hexadecimal;
*/
class FallbackNodeProvider implements NodeProviderInterface
{
/**
* @var iterable<NodeProviderInterface>
*/
private $nodeProviders;
/**
* @param iterable<NodeProviderInterface> $providers Array of node providers
*/
public function __construct(iterable $providers)
public function __construct(private iterable $providers)
{
$this->nodeProviders = $providers;
}
public function getNode(): Hexadecimal
{
$lastProviderException = null;
foreach ($this->nodeProviders as $provider) {
foreach ($this->providers as $provider) {
try {
return $provider->getNode();
} catch (NodeException $exception) {

View File

@@ -32,10 +32,7 @@ use const STR_PAD_LEFT;
*/
class StaticNodeProvider implements NodeProviderInterface
{
/**
* @var Hexadecimal
*/
private $node;
private Hexadecimal $node;
/**
* @param Hexadecimal $node The static node value to use

View File

@@ -27,8 +27,8 @@ use function ob_start;
use function preg_match;
use function preg_match_all;
use function reset;
use function str_contains;
use function str_replace;
use function strpos;
use function strtolower;
use function strtoupper;
use function substr;
@@ -100,12 +100,18 @@ class SystemNodeProvider implements NodeProviderInterface
{
$disabledFunctions = strtolower((string) ini_get('disable_functions'));
if (strpos($disabledFunctions, 'passthru') !== false) {
if (str_contains($disabledFunctions, 'passthru')) {
return '';
}
/**
* @psalm-suppress UnnecessaryVarAnnotation
* @var string $phpOs
*/
$phpOs = constant('PHP_OS');
ob_start();
switch (strtoupper(substr(constant('PHP_OS'), 0, 3))) {
switch (strtoupper(substr($phpOs, 0, 3))) {
case 'WIN':
passthru('ipconfig /all 2>&1');
@@ -127,12 +133,15 @@ class SystemNodeProvider implements NodeProviderInterface
$ifconfig = (string) ob_get_clean();
$node = '';
if (preg_match_all(self::IFCONFIG_PATTERN, $ifconfig, $matches, PREG_PATTERN_ORDER)) {
$node = $matches[1][0] ?? '';
foreach ($matches[1] as $iface) {
if ($iface !== '00:00:00:00:00:00' && $iface !== '00-00-00-00-00-00') {
return $iface;
}
}
}
return $node;
return '';
}
/**
@@ -142,13 +151,20 @@ class SystemNodeProvider implements NodeProviderInterface
{
$mac = '';
if (strtoupper(constant('PHP_OS')) === 'LINUX') {
/**
* @psalm-suppress UnnecessaryVarAnnotation
* @var string $phpOs
*/
$phpOs = constant('PHP_OS');
if (strtoupper($phpOs) === 'LINUX') {
$addressPaths = glob('/sys/class/net/*/address', GLOB_NOSORT);
if ($addressPaths === false || count($addressPaths) === 0) {
return '';
}
/** @var array<array-key, string> $macs */
$macs = [];
array_walk($addressPaths, function (string $addressPath) use (&$macs): void {
@@ -157,7 +173,10 @@ class SystemNodeProvider implements NodeProviderInterface
}
});
$macs = array_map('trim', $macs);
/** @var callable $trim */
$trim = 'trim';
$macs = array_map($trim, $macs);
// Remove invalid entries.
$macs = array_filter($macs, function (string $address) {
@@ -165,6 +184,7 @@ class SystemNodeProvider implements NodeProviderInterface
&& preg_match(self::SYSFS_PATTERN, $address);
});
/** @var string|bool $mac */
$mac = reset($macs);
}

View File

@@ -19,21 +19,15 @@ use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Type\Time;
/**
* FixedTimeProvider uses an known time to provide the time
* FixedTimeProvider uses a known time to provide the time
*
* This provider allows the use of a previously-generated, or known, time
* when generating time-based UUIDs.
*/
class FixedTimeProvider implements TimeProviderInterface
{
/**
* @var Time
*/
private $fixedTime;
public function __construct(Time $time)
public function __construct(private Time $time)
{
$this->fixedTime = $time;
}
/**
@@ -43,7 +37,7 @@ class FixedTimeProvider implements TimeProviderInterface
*/
public function setUsec($value): void
{
$this->fixedTime = new Time($this->fixedTime->getSeconds(), $value);
$this->time = new Time($this->time->getSeconds(), $value);
}
/**
@@ -53,11 +47,11 @@ class FixedTimeProvider implements TimeProviderInterface
*/
public function setSec($value): void
{
$this->fixedTime = new Time($value, $this->fixedTime->getMicroseconds());
$this->time = new Time($value, $this->time->getMicroseconds());
}
public function getTime(): Time
{
return $this->fixedTime;
return $this->time;
}
}

View File

@@ -40,16 +40,12 @@ use const STR_PAD_LEFT;
*/
final class Fields implements FieldsInterface
{
use MaxTrait;
use NilTrait;
use SerializableFieldsTrait;
use VariantTrait;
use VersionTrait;
/**
* @var string
*/
private $bytes;
/**
* @param string $bytes A 16-byte binary string representation of a UUID
*
@@ -57,17 +53,15 @@ final class Fields implements FieldsInterface
* @throws InvalidArgumentException if the byte string does not represent an RFC 4122 UUID
* @throws InvalidArgumentException if the byte string does not contain a valid version
*/
public function __construct(string $bytes)
public function __construct(private string $bytes)
{
if (strlen($bytes) !== 16) {
if (strlen($this->bytes) !== 16) {
throw new InvalidArgumentException(
'The byte string must be 16 bytes long; '
. 'received ' . strlen($bytes) . ' bytes'
. 'received ' . strlen($this->bytes) . ' bytes'
);
}
$this->bytes = $bytes;
if (!$this->isCorrectVariant()) {
throw new InvalidArgumentException(
'The byte string received does not conform to the RFC 4122 variant'
@@ -88,7 +82,13 @@ final class Fields implements FieldsInterface
public function getClockSeq(): Hexadecimal
{
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
if ($this->isMax()) {
$clockSeq = 0xffff;
} elseif ($this->isNil()) {
$clockSeq = 0x0000;
} else {
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
}
return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
}
@@ -140,52 +140,53 @@ final class Fields implements FieldsInterface
*/
public function getTimestamp(): Hexadecimal
{
switch ($this->getVersion()) {
case Uuid::UUID_TYPE_DCE_SECURITY:
$timestamp = sprintf(
'%03x%04s%08s',
hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
$this->getTimeMid()->toString(),
''
);
break;
case Uuid::UUID_TYPE_PEABODY:
$timestamp = sprintf(
'%08s%04s%03x',
$this->getTimeLow()->toString(),
$this->getTimeMid()->toString(),
hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff
);
break;
default:
$timestamp = sprintf(
'%03x%04s%08s',
hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
$this->getTimeMid()->toString(),
$this->getTimeLow()->toString()
);
}
$timestamp = match ($this->getVersion()) {
Uuid::UUID_TYPE_DCE_SECURITY => sprintf(
'%03x%04s%08s',
hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
$this->getTimeMid()->toString(),
''
),
Uuid::UUID_TYPE_REORDERED_TIME => sprintf(
'%08s%04s%03x',
$this->getTimeLow()->toString(),
$this->getTimeMid()->toString(),
hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff
),
// The Unix timestamp in version 7 UUIDs is a 48-bit number,
// but for consistency, we will return a 60-bit number, padded
// to the left with zeros.
Uuid::UUID_TYPE_UNIX_TIME => sprintf(
'%011s%04s',
$this->getTimeLow()->toString(),
$this->getTimeMid()->toString(),
),
default => sprintf(
'%03x%04s%08s',
hexdec($this->getTimeHiAndVersion()->toString()) & 0x0fff,
$this->getTimeMid()->toString(),
$this->getTimeLow()->toString()
),
};
return new Hexadecimal($timestamp);
}
public function getVersion(): ?int
{
if ($this->isNil()) {
if ($this->isNil() || $this->isMax()) {
return null;
}
/** @var array $parts */
/** @var int[] $parts */
$parts = unpack('n*', $this->bytes);
return (int) $parts[4] >> 12;
return $parts[4] >> 12;
}
private function isCorrectVariant(): bool
{
if ($this->isNil()) {
if ($this->isNil() || $this->isMax()) {
return true;
}

View File

@@ -103,11 +103,13 @@ interface FieldsInterface extends BaseFieldsInterface
* The version number describes how the UUID was generated and has the
* following meaning:
*
* 1. Time-based UUID
* 1. Gregorian time UUID
* 2. DCE security UUID
* 3. Name-based UUID hashed with MD5
* 4. Randomly generated UUID
* 5. Name-based UUID hashed with SHA-1
* 6. Reordered time UUID
* 7. Unix Epoch time UUID
*
* This returns `null` if the UUID is not an RFC 4122 variant, since version
* is only meaningful for this variant.

View File

@@ -17,11 +17,13 @@ namespace Ramsey\Uuid\Rfc4122;
use Ramsey\Uuid\Builder\UuidBuilderInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\Time\UnixTimeConverter;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Nonstandard\UuidV6;
use Ramsey\Uuid\Math\BrickMathCalculator;
use Ramsey\Uuid\Rfc4122\UuidInterface as Rfc4122UuidInterface;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Throwable;
@@ -32,15 +34,7 @@ use Throwable;
*/
class UuidBuilder implements UuidBuilderInterface
{
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
private TimeConverterInterface $unixTimeConverter;
/**
* Constructs the DefaultUuidBuilder
@@ -48,14 +42,18 @@ class UuidBuilder implements UuidBuilderInterface
* @param NumberConverterInterface $numberConverter The number converter to
* use when constructing the Uuid
* @param TimeConverterInterface $timeConverter The time converter to use
* for converting timestamps extracted from a UUID to Unix timestamps
* for converting Gregorian time extracted from version 1, 2, and 6
* UUIDs to Unix timestamps
* @param TimeConverterInterface|null $unixTimeConverter The time converter
* to use for converter Unix Epoch time extracted from version 7 UUIDs
* to Unix timestamps
*/
public function __construct(
NumberConverterInterface $numberConverter,
TimeConverterInterface $timeConverter
private NumberConverterInterface $numberConverter,
private TimeConverterInterface $timeConverter,
?TimeConverterInterface $unixTimeConverter = null
) {
$this->numberConverter = $numberConverter;
$this->timeConverter = $timeConverter;
$this->unixTimeConverter = $unixTimeConverter ?? new UnixTimeConverter(new BrickMathCalculator());
}
/**
@@ -71,25 +69,34 @@ class UuidBuilder implements UuidBuilderInterface
public function build(CodecInterface $codec, string $bytes): UuidInterface
{
try {
/** @var Fields $fields */
$fields = $this->buildFields($bytes);
if ($fields->isNil()) {
return new NilUuid($fields, $this->numberConverter, $codec, $this->timeConverter);
}
if ($fields->isMax()) {
return new MaxUuid($fields, $this->numberConverter, $codec, $this->timeConverter);
}
switch ($fields->getVersion()) {
case 1:
case Uuid::UUID_TYPE_TIME:
return new UuidV1($fields, $this->numberConverter, $codec, $this->timeConverter);
case 2:
case Uuid::UUID_TYPE_DCE_SECURITY:
return new UuidV2($fields, $this->numberConverter, $codec, $this->timeConverter);
case 3:
case Uuid::UUID_TYPE_HASH_MD5:
return new UuidV3($fields, $this->numberConverter, $codec, $this->timeConverter);
case 4:
case Uuid::UUID_TYPE_RANDOM:
return new UuidV4($fields, $this->numberConverter, $codec, $this->timeConverter);
case 5:
case Uuid::UUID_TYPE_HASH_SHA1:
return new UuidV5($fields, $this->numberConverter, $codec, $this->timeConverter);
case 6:
case Uuid::UUID_TYPE_REORDERED_TIME:
return new UuidV6($fields, $this->numberConverter, $codec, $this->timeConverter);
case Uuid::UUID_TYPE_UNIX_TIME:
return new UuidV7($fields, $this->numberConverter, $codec, $this->unixTimeConverter);
case Uuid::UUID_TYPE_CUSTOM:
return new UuidV8($fields, $this->numberConverter, $codec, $this->timeConverter);
}
throw new UnsupportedOperationException(

View File

@@ -26,11 +26,4 @@ use Ramsey\Uuid\UuidInterface as BaseUuidInterface;
*/
interface UuidInterface extends BaseUuidInterface
{
/**
* Returns the string standard representation of the UUID as a URN
*
* @link http://en.wikipedia.org/wiki/Uniform_Resource_Name Uniform Resource Name
* @link https://tools.ietf.org/html/rfc4122#section-3 RFC 4122, § 3: Namespace Registration Template
*/
public function getUrn(): string;
}

View File

@@ -14,31 +14,25 @@ declare(strict_types=1);
namespace Ramsey\Uuid\Rfc4122;
use DateTimeImmutable;
use DateTimeInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\DateTimeException;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Uuid;
use Throwable;
use function str_pad;
use const STR_PAD_LEFT;
/**
* Time-based, or version 1, UUIDs include timestamp, clock sequence, and node
* Gregorian time, or version 1, UUIDs include timestamp, clock sequence, and node
* values that are combined into a 128-bit unsigned integer
*
* @psalm-immutable
*/
final class UuidV1 extends Uuid implements UuidInterface
{
use TimeTrait;
/**
* Creates a version 1 (time-based) UUID
* Creates a version 1 (Gregorian time) UUID
*
* @param Rfc4122FieldsInterface $fields The fields from which to construct a UUID
* @param NumberConverterInterface $numberConverter The number converter to use
@@ -63,30 +57,4 @@ final class UuidV1 extends Uuid implements UuidInterface
parent::__construct($fields, $numberConverter, $codec, $timeConverter);
}
/**
* Returns a DateTimeInterface object representing the timestamp associated
* with the UUID
*
* The timestamp value is only meaningful in a time-based UUID, which
* has version type 1.
*
* @return DateTimeImmutable A PHP DateTimeImmutable instance representing
* the timestamp of a version 1 UUID
*/
public function getDateTime(): DateTimeInterface
{
$time = $this->timeConverter->convertTime($this->fields->getTimestamp());
try {
return new DateTimeImmutable(
'@'
. $time->getSeconds()->toString()
. '.'
. str_pad($time->getMicroseconds()->toString(), 6, '0', STR_PAD_LEFT)
);
} catch (Throwable $e) {
throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
}
}
}

View File

@@ -14,28 +14,33 @@ declare(strict_types=1);
namespace Ramsey\Uuid\Rfc4122;
use DateTimeImmutable;
use DateTimeInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\DateTimeException;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Ramsey\Uuid\Uuid;
use Throwable;
use function hexdec;
use function str_pad;
use const STR_PAD_LEFT;
/**
* DCE Security version, or version 2, UUIDs include local domain identifier,
* local ID for the specified domain, and node values that are combined into a
* 128-bit unsigned integer
*
* It is important to note that a version 2 UUID suffers from some loss of
* fidelity of the timestamp, due to replacing the time_low field with the
* local identifier. When constructing the timestamp value for date
* purposes, we replace the local identifier bits with zeros. As a result,
* the timestamp can be off by a range of 0 to 429.4967295 seconds (or 7
* minutes, 9 seconds, and 496730 microseconds).
*
* Astute observers might note this value directly corresponds to 2^32 - 1,
* or 0xffffffff. The local identifier is 32-bits, and we have set each of
* these bits to 0, so the maximum range of timestamp drift is 0x00000000
* to 0xffffffff (counted in 100-nanosecond intervals).
*
* @link https://publications.opengroup.org/c311 DCE 1.1: Authentication and Security Services
* @link https://publications.opengroup.org/c706 DCE 1.1: Remote Procedure Call
* @link https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01 DCE 1.1: Auth & Sec, §5.2.1.1
@@ -47,6 +52,8 @@ use const STR_PAD_LEFT;
*/
final class UuidV2 extends Uuid implements UuidInterface
{
use TimeTrait;
/**
* Creates a version 2 (DCE Security) UUID
*
@@ -74,41 +81,6 @@ final class UuidV2 extends Uuid implements UuidInterface
parent::__construct($fields, $numberConverter, $codec, $timeConverter);
}
/**
* Returns a DateTimeInterface object representing the timestamp associated
* with the UUID
*
* It is important to note that a version 2 UUID suffers from some loss of
* fidelity of the timestamp, due to replacing the time_low field with the
* local identifier. When constructing the timestamp value for date
* purposes, we replace the local identifier bits with zeros. As a result,
* the timestamp can be off by a range of 0 to 429.4967295 seconds (or 7
* minutes, 9 seconds, and 496730 microseconds).
*
* Astute observers might note this value directly corresponds to 2^32 - 1,
* or 0xffffffff. The local identifier is 32-bits, and we have set each of
* these bits to 0, so the maximum range of timestamp drift is 0x00000000
* to 0xffffffff (counted in 100-nanosecond intervals).
*
* @return DateTimeImmutable A PHP DateTimeImmutable instance representing
* the timestamp of a version 2 UUID
*/
public function getDateTime(): DateTimeInterface
{
$time = $this->timeConverter->convertTime($this->fields->getTimestamp());
try {
return new DateTimeImmutable(
'@'
. $time->getSeconds()->toString()
. '.'
. str_pad($time->getMicroseconds()->toString(), 6, '0', STR_PAD_LEFT)
);
} catch (Throwable $e) {
throw new DateTimeException($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Returns the local domain used to create this version 2 UUID
*/

View File

@@ -28,7 +28,7 @@ use function str_replace;
final class Validator implements ValidatorInterface
{
private const VALID_PATTERN = '\A[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-'
. '[1-5]{1}[0-9A-Fa-f]{3}-[ABab89]{1}[0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z';
. '[1-8][0-9A-Fa-f]{3}-[ABab89][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}\z';
/**
* @psalm-return non-empty-string
@@ -43,7 +43,8 @@ final class Validator implements ValidatorInterface
public function validate(string $uuid): bool
{
$uuid = str_replace(['urn:', 'uuid:', 'URN:', 'UUID:', '{', '}'], '', $uuid);
$uuid = strtolower($uuid);
return $uuid === Uuid::NIL || preg_match('/' . self::VALID_PATTERN . '/Dms', $uuid);
return $uuid === Uuid::NIL || $uuid === Uuid::MAX || preg_match('/' . self::VALID_PATTERN . '/Dms', $uuid);
}
}

View File

@@ -19,8 +19,8 @@ use Ramsey\Uuid\Uuid;
use function decbin;
use function str_pad;
use function str_starts_with;
use function strlen;
use function strpos;
use function substr;
use function unpack;
@@ -58,7 +58,13 @@ trait VariantTrait
throw new InvalidBytesException('Invalid number of bytes');
}
/** @var array $parts */
if ($this->isMax() || $this->isNil()) {
// RFC 4122 defines these special types of UUID, so we will consider
// them as belonging to the RFC 4122 variant.
return Uuid::RFC_4122;
}
/** @var int[] $parts */
$parts = unpack('n*', $this->getBytes());
// $parts[5] is a 16-bit, unsigned integer containing the variant bits
@@ -67,7 +73,7 @@ trait VariantTrait
// three characters (three most-significant bits) to determine the
// variant.
$binary = str_pad(
decbin((int) $parts[5]),
decbin($parts[5]),
16,
'0',
STR_PAD_LEFT
@@ -76,15 +82,13 @@ trait VariantTrait
$msb = substr($binary, 0, 3);
if ($msb === '111') {
$variant = Uuid::RESERVED_FUTURE;
return Uuid::RESERVED_FUTURE;
} elseif ($msb === '110') {
$variant = Uuid::RESERVED_MICROSOFT;
} elseif (strpos($msb, '10') === 0) {
$variant = Uuid::RFC_4122;
} else {
$variant = Uuid::RESERVED_NCS;
return Uuid::RESERVED_MICROSOFT;
} elseif (str_starts_with($msb, '10')) {
return Uuid::RFC_4122;
}
return $variant;
return Uuid::RESERVED_NCS;
}
}

View File

@@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Ramsey\Uuid\Rfc4122;
use Ramsey\Uuid\Uuid;
/**
* Provides common functionality for handling the version, as defined by RFC 4122
*
@@ -26,6 +28,11 @@ trait VersionTrait
*/
abstract public function getVersion(): ?int;
/**
* Returns true if these fields represent a max UUID
*/
abstract public function isMax(): bool;
/**
* Returns true if these fields represent a nil UUID
*/
@@ -38,20 +45,16 @@ trait VersionTrait
*/
private function isCorrectVersion(): bool
{
if ($this->isNil()) {
if ($this->isNil() || $this->isMax()) {
return true;
}
switch ($this->getVersion()) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
return true;
}
return false;
return match ($this->getVersion()) {
Uuid::UUID_TYPE_TIME, Uuid::UUID_TYPE_DCE_SECURITY,
Uuid::UUID_TYPE_HASH_MD5, Uuid::UUID_TYPE_RANDOM,
Uuid::UUID_TYPE_HASH_SHA1, Uuid::UUID_TYPE_REORDERED_TIME,
Uuid::UUID_TYPE_UNIX_TIME, Uuid::UUID_TYPE_CUSTOM => true,
default => false,
};
}
}

View File

@@ -19,6 +19,7 @@ use ValueError;
use function is_numeric;
use function sprintf;
use function str_starts_with;
/**
* A value object representing a decimal
@@ -34,20 +35,10 @@ use function sprintf;
*/
final class Decimal implements NumberInterface
{
/**
* @var string
*/
private $value;
private string $value;
private bool $isNegative = false;
/**
* @var bool
*/
private $isNegative = false;
/**
* @param mixed $value The decimal value to store
*/
public function __construct($value)
public function __construct(float | int | string | self $value)
{
$value = (string) $value;
@@ -59,7 +50,7 @@ final class Decimal implements NumberInterface
}
// Remove the leading +-symbol.
if (strpos($value, '+') === 0) {
if (str_starts_with($value, '+')) {
$value = substr($value, 1);
}
@@ -68,7 +59,7 @@ final class Decimal implements NumberInterface
$value = '0';
}
if (strpos($value, '-') === 0) {
if (str_starts_with($value, '-')) {
$this->isNegative = true;
}
@@ -111,18 +102,19 @@ final class Decimal implements NumberInterface
/**
* Constructs the object from a serialized string representation
*
* @param string $serialized The serialized string representation of the object
* @param string $data The serialized string representation of the object
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @psalm-suppress UnusedMethodCall
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
$this->__construct($serialized);
$this->__construct($data);
}
/**
* @param array{string: string} $data
* @param array{string?: string} $data
*
* @psalm-suppress UnusedMethodCall
*/
public function __unserialize(array $data): void
{

View File

@@ -17,10 +17,8 @@ namespace Ramsey\Uuid\Type;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use ValueError;
use function ctype_xdigit;
use function preg_match;
use function sprintf;
use function strpos;
use function strtolower;
use function substr;
/**
@@ -34,29 +32,14 @@ use function substr;
*/
final class Hexadecimal implements TypeInterface
{
/**
* @var string
*/
private $value;
private string $value;
/**
* @param string $value The hexadecimal value to store
* @param self|string $value The hexadecimal value to store
*/
public function __construct(string $value)
public function __construct(self | string $value)
{
$value = strtolower($value);
if (strpos($value, '0x') === 0) {
$value = substr($value, 2);
}
if (!ctype_xdigit($value)) {
throw new InvalidArgumentException(
'Value must be a hexadecimal number'
);
}
$this->value = $value;
$this->value = $value instanceof self ? (string) $value : $this->prepareValue($value);
}
public function toString(): string
@@ -90,18 +73,17 @@ final class Hexadecimal implements TypeInterface
/**
* Constructs the object from a serialized string representation
*
* @param string $serialized The serialized string representation of the object
* @param string $data The serialized string representation of the object
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @psalm-suppress UnusedMethodCall
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
$this->__construct($serialized);
$this->__construct($data);
}
/**
* @param array{string: string} $data
* @param array{string?: string} $data
*/
public function __unserialize(array $data): void
{
@@ -113,4 +95,21 @@ final class Hexadecimal implements TypeInterface
$this->unserialize($data['string']);
}
private function prepareValue(string $value): string
{
$value = strtolower($value);
if (str_starts_with($value, '0x')) {
$value = substr($value, 2);
}
if (!preg_match('/^[A-Fa-f0-9]+$/', $value)) {
throw new InvalidArgumentException(
'Value must be a hexadecimal number'
);
}
return $value;
}
}

View File

@@ -17,10 +17,10 @@ namespace Ramsey\Uuid\Type;
use Ramsey\Uuid\Exception\InvalidArgumentException;
use ValueError;
use function ctype_digit;
use function ltrim;
use function assert;
use function is_numeric;
use function preg_match;
use function sprintf;
use function strpos;
use function substr;
/**
@@ -40,52 +40,13 @@ final class Integer implements NumberInterface
/**
* @psalm-var numeric-string
*/
private $value;
private string $value;
/**
* @var bool
*/
private $isNegative = false;
private bool $isNegative = false;
/**
* @param mixed $value The integer value to store
*/
public function __construct($value)
public function __construct(float | int | string | self $value)
{
$value = (string) $value;
$sign = '+';
// If the value contains a sign, remove it for ctype_digit() check.
if (strpos($value, '-') === 0 || strpos($value, '+') === 0) {
$sign = substr($value, 0, 1);
$value = substr($value, 1);
}
if (!ctype_digit($value)) {
throw new InvalidArgumentException(
'Value must be a signed integer or a string containing only '
. 'digits 0-9 and, optionally, a sign (+ or -)'
);
}
// Trim any leading zeros.
$value = ltrim($value, '0');
// Set to zero if the string is empty after trimming zeros.
if ($value === '') {
$value = '0';
}
// Add the negative sign back to the value.
if ($sign === '-' && $value !== '0') {
$value = $sign . $value;
$this->isNegative = true;
}
/** @psalm-var numeric-string $numericValue */
$numericValue = $value;
$this->value = $numericValue;
$this->value = $value instanceof self ? (string) $value : $this->prepareValue($value);
}
public function isNegative(): bool
@@ -101,6 +62,9 @@ final class Integer implements NumberInterface
return $this->value;
}
/**
* @psalm-return numeric-string
*/
public function __toString(): string
{
return $this->toString();
@@ -127,18 +91,17 @@ final class Integer implements NumberInterface
/**
* Constructs the object from a serialized string representation
*
* @param string $serialized The serialized string representation of the object
* @param string $data The serialized string representation of the object
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @psalm-suppress UnusedMethodCall
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
$this->__construct($serialized);
$this->__construct($data);
}
/**
* @param array{string: string} $data
* @param array{string?: string} $data
*/
public function __unserialize(array $data): void
{
@@ -150,4 +113,46 @@ final class Integer implements NumberInterface
$this->unserialize($data['string']);
}
/**
* @return numeric-string
*/
private function prepareValue(float | int | string $value): string
{
$value = (string) $value;
$sign = '+';
// If the value contains a sign, remove it for digit pattern check.
if (str_starts_with($value, '-') || str_starts_with($value, '+')) {
$sign = substr($value, 0, 1);
$value = substr($value, 1);
}
if (!preg_match('/^\d+$/', $value)) {
throw new InvalidArgumentException(
'Value must be a signed integer or a string containing only '
. 'digits 0-9 and, optionally, a sign (+ or -)'
);
}
// Trim any leading zeros.
$value = ltrim($value, '0');
// Set to zero if the string is empty after trimming zeros.
if ($value === '') {
$value = '0';
}
// Add the negative sign back to the value.
if ($sign === '-' && $value !== '0') {
$value = $sign . $value;
/** @psalm-suppress InaccessibleProperty */
$this->isNegative = true;
}
assert(is_numeric($value));
return $value;
}
}

View File

@@ -17,7 +17,6 @@ namespace Ramsey\Uuid\Type;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use ValueError;
use stdClass;
use function json_decode;
use function json_encode;
@@ -34,22 +33,13 @@ use function sprintf;
*/
final class Time implements TypeInterface
{
/**
* @var IntegerObject
*/
private $seconds;
private IntegerObject $seconds;
private IntegerObject $microseconds;
/**
* @var IntegerObject
*/
private $microseconds;
/**
* @param mixed $seconds
* @param mixed $microseconds
*/
public function __construct($seconds, $microseconds = 0)
{
public function __construct(
float | int | string | IntegerObject $seconds,
float | int | string | IntegerObject $microseconds = 0,
) {
$this->seconds = new IntegerObject($seconds);
$this->microseconds = new IntegerObject($microseconds);
}
@@ -66,7 +56,7 @@ final class Time implements TypeInterface
public function toString(): string
{
return $this->seconds->toString() . '.' . $this->microseconds->toString();
return $this->seconds->toString() . '.' . sprintf('%06s', $this->microseconds->toString());
}
public function __toString(): string
@@ -104,27 +94,26 @@ final class Time implements TypeInterface
/**
* Constructs the object from a serialized string representation
*
* @param string $serialized The serialized string representation of the object
* @param string $data The serialized string representation of the object
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @psalm-suppress UnusedMethodCall
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
/** @var stdClass $time */
$time = json_decode($serialized);
/** @var array{seconds?: int|float|string, microseconds?: int|float|string} $time */
$time = json_decode($data, true);
if (!isset($time->seconds) || !isset($time->microseconds)) {
if (!isset($time['seconds']) || !isset($time['microseconds'])) {
throw new UnsupportedOperationException(
'Attempted to unserialize an invalid value'
);
}
$this->__construct($time->seconds, $time->microseconds);
$this->__construct($time['seconds'], $time['microseconds']);
}
/**
* @param array{seconds: string, microseconds: string} $data
* @param array{seconds?: string, microseconds?: string} $data
*/
public function __unserialize(array $data): void
{

View File

@@ -14,10 +14,12 @@ declare(strict_types=1);
namespace Ramsey\Uuid;
use BadMethodCallException;
use DateTimeInterface;
use Ramsey\Uuid\Codec\CodecInterface;
use Ramsey\Uuid\Converter\NumberConverterInterface;
use Ramsey\Uuid\Converter\TimeConverterInterface;
use Ramsey\Uuid\Exception\UnsupportedOperationException;
use Ramsey\Uuid\Fields\FieldsInterface;
use Ramsey\Uuid\Lazy\LazyUuidFromString;
use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface;
@@ -27,6 +29,7 @@ use ValueError;
use function assert;
use function bin2hex;
use function method_exists;
use function preg_match;
use function sprintf;
use function str_replace;
@@ -82,6 +85,14 @@ class Uuid implements UuidInterface
*/
public const NIL = '00000000-0000-0000-0000-000000000000';
/**
* The max UUID is a special form of UUID that is specified to have all 128
* bits set to one
*
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.10 Max UUID
*/
public const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';
/**
* Variant: reserved, NCS backward compatibility
*
@@ -116,7 +127,7 @@ class Uuid implements UuidInterface
public const VALID_PATTERN = '^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$';
/**
* Version 1 (time-based) UUID
* Version 1 (Gregorian time) UUID
*
* @link https://tools.ietf.org/html/rfc4122#section-4.1.3 RFC 4122, § 4.1.3: Version
*/
@@ -156,16 +167,29 @@ class Uuid implements UuidInterface
public const UUID_TYPE_HASH_SHA1 = 5;
/**
* Version 6 (ordered-time) UUID
*
* This is named `UUID_TYPE_PEABODY`, since the specification is still in
* draft form, and the primary author/editor's name is Brad Peabody.
*
* @link https://github.com/uuid6/uuid6-ietf-draft UUID version 6 IETF draft
* @link http://gh.peabody.io/uuidv6/ "Version 6" UUIDs
* @deprecated Use {@see Uuid::UUID_TYPE_REORDERED_TIME} instead.
*/
public const UUID_TYPE_PEABODY = 6;
/**
* Version 6 (reordered time) UUID
*
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.6 UUID Version 6
*/
public const UUID_TYPE_REORDERED_TIME = 6;
/**
* Version 7 (Unix Epoch time) UUID
*
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.7 UUID Version 7
*/
public const UUID_TYPE_UNIX_TIME = 7;
/**
* @link https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis-00#section-5.8 UUID Version 8
*/
public const UUID_TYPE_CUSTOM = 8;
/**
* DCE Security principal domain
*
@@ -198,38 +222,19 @@ class Uuid implements UuidInterface
self::DCE_DOMAIN_ORG => 'org',
];
/**
* @var UuidFactoryInterface|null
*/
private static $factory = null;
private static ?UuidFactoryInterface $factory = null;
/**
* @var bool flag to detect if the UUID factory was replaced internally, which disables all optimizations
* for the default/happy path internal scenarios
* @var bool flag to detect if the UUID factory was replaced internally,
* which disables all optimizations for the default/happy path internal
* scenarios
*/
private static $factoryReplaced = false;
private static bool $factoryReplaced = false;
/**
* @var CodecInterface
*/
protected $codec;
/**
* The fields that make up this UUID
*
* @var Rfc4122FieldsInterface
*/
protected $fields;
/**
* @var NumberConverterInterface
*/
protected $numberConverter;
/**
* @var TimeConverterInterface
*/
protected $timeConverter;
protected CodecInterface $codec;
protected NumberConverterInterface $numberConverter;
protected Rfc4122FieldsInterface $fields;
protected TimeConverterInterface $timeConverter;
/**
* Creates a universally unique identifier (UUID) from an array of fields
@@ -288,7 +293,7 @@ class Uuid implements UuidInterface
*/
public function serialize(): string
{
return $this->getFields()->getBytes();
return $this->codec->encode($this);
}
/**
@@ -302,19 +307,17 @@ class Uuid implements UuidInterface
/**
* Re-constructs the object from its serialized form
*
* @param string $serialized The serialized PHP string to unserialize into
* @param string $data The serialized PHP string to unserialize into
* a UuidInterface instance
*
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*/
public function unserialize($serialized): void
public function unserialize(string $data): void
{
if (strlen($serialized) === 16) {
if (strlen($data) === 16) {
/** @var Uuid $uuid */
$uuid = self::getFactory()->fromBytes($serialized);
$uuid = self::getFactory()->fromBytes($data);
} else {
/** @var Uuid $uuid */
$uuid = self::getFactory()->fromString($serialized);
$uuid = self::getFactory()->fromString($data);
}
$this->codec = $uuid->codec;
@@ -324,7 +327,7 @@ class Uuid implements UuidInterface
}
/**
* @param array{bytes: string} $data
* @param array{bytes?: string} $data
*/
public function __unserialize(array $data): void
{
@@ -384,6 +387,11 @@ class Uuid implements UuidInterface
return new IntegerObject($this->numberConverter->fromHex($this->getHex()->toString()));
}
public function getUrn(): string
{
return 'urn:uuid:' . $this->toString();
}
/**
* @psalm-return non-empty-string
*/
@@ -438,20 +446,20 @@ class Uuid implements UuidInterface
*/
public static function fromBytes(string $bytes): UuidInterface
{
if (! self::$factoryReplaced && strlen($bytes) === 16) {
if (!self::$factoryReplaced && strlen($bytes) === 16) {
$base16Uuid = bin2hex($bytes);
// Note: we are calling `fromString` internally because we don't know if the given `$bytes` is a valid UUID
return self::fromString(
substr($base16Uuid, 0, 8)
. '-'
. substr($base16Uuid, 8, 4)
. '-'
. substr($base16Uuid, 12, 4)
. '-'
. substr($base16Uuid, 16, 4)
. '-'
. substr($base16Uuid, 20, 12)
. '-'
. substr($base16Uuid, 8, 4)
. '-'
. substr($base16Uuid, 12, 4)
. '-'
. substr($base16Uuid, 16, 4)
. '-'
. substr($base16Uuid, 20, 12)
);
}
@@ -477,7 +485,7 @@ class Uuid implements UuidInterface
public static function fromString(string $uuid): UuidInterface
{
$uuid = strtolower($uuid);
if (! self::$factoryReplaced && preg_match(LazyUuidFromString::VALID_REGEX, $uuid) === 1) {
if (!self::$factoryReplaced && preg_match(LazyUuidFromString::VALID_REGEX, $uuid) === 1) {
assert($uuid !== '');
return new LazyUuidFromString($uuid);
@@ -507,6 +515,33 @@ class Uuid implements UuidInterface
return self::getFactory()->fromDateTime($dateTime, $node, $clockSeq);
}
/**
* Creates a UUID from the Hexadecimal object
*
* @param Hexadecimal $hex Hexadecimal object representing a hexadecimal number
*
* @return UuidInterface A UuidInterface instance created from the Hexadecimal
* object representing a hexadecimal number
*
* @psalm-pure note: changing the internal factory is an edge case not covered by purity invariants,
* but under constant factory setups, this method operates in functionally pure manners
* @psalm-suppress MixedInferredReturnType,MixedReturnStatement
*/
public static function fromHexadecimal(Hexadecimal $hex): UuidInterface
{
$factory = self::getFactory();
if (method_exists($factory, 'fromHexadecimal')) {
/**
* @phpstan-ignore-next-line
* @psalm-suppress UndefinedInterfaceMethod
*/
return self::getFactory()->fromHexadecimal($hex);
}
throw new BadMethodCallException('The method fromHexadecimal() does not exist on the provided factory');
}
/**
* Creates a UUID from a 128-bit integer string
*
@@ -520,6 +555,7 @@ class Uuid implements UuidInterface
*/
public static function fromInteger(string $integer): UuidInterface
{
/** @psalm-suppress ImpureMethodCall */
return self::getFactory()->fromInteger($integer);
}
@@ -532,20 +568,23 @@ class Uuid implements UuidInterface
*
* @psalm-pure note: changing the internal factory is an edge case not covered by purity invariants,
* but under constant factory setups, this method operates in functionally pure manners
*
* @psalm-assert-if-true =non-empty-string $uuid
*/
public static function isValid(string $uuid): bool
{
/** @psalm-suppress ImpureMethodCall */
return self::getFactory()->getValidator()->validate($uuid);
}
/**
* Returns a version 1 (time-based) UUID from a host ID, sequence number,
* Returns a version 1 (Gregorian time) UUID from a host ID, sequence number,
* and the current time
*
* @param Hexadecimal|int|string|null $node A 48-bit number representing the
* hardware address; this number may be represented as an integer or a
* hexadecimal string
* @param int $clockSeq A 14-bit number used to help avoid duplicates that
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates that
* could arise when the clock is set backwards in time or if the node ID
* changes
*
@@ -644,12 +683,12 @@ class Uuid implements UuidInterface
}
/**
* Returns a version 6 (ordered-time) UUID from a host ID, sequence number,
* Returns a version 6 (reordered time) UUID from a host ID, sequence number,
* and the current time
*
* @param Hexadecimal|null $node A 48-bit number representing the hardware
* address
* @param int $clockSeq A 14-bit number used to help avoid duplicates that
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates that
* could arise when the clock is set backwards in time or if the node ID
* changes
*
@@ -662,4 +701,58 @@ class Uuid implements UuidInterface
): UuidInterface {
return self::getFactory()->uuid6($node, $clockSeq);
}
/**
* Returns a version 7 (Unix Epoch time) UUID
*
* @param DateTimeInterface|null $dateTime An optional date/time from which
* to create the version 7 UUID. If not provided, the UUID is generated
* using the current date/time.
*
* @return UuidInterface A UuidInterface instance that represents a
* version 7 UUID
*/
public static function uuid7(?DateTimeInterface $dateTime = null): UuidInterface
{
$factory = self::getFactory();
if (method_exists($factory, 'uuid7')) {
/** @var UuidInterface */
return $factory->uuid7($dateTime);
}
throw new UnsupportedOperationException(
'The provided factory does not support the uuid7() method',
);
}
/**
* Returns a version 8 (custom) UUID
*
* The bytes provided may contain any value according to your application's
* needs. Be aware, however, that other applications may not understand the
* semantics of the value.
*
* @param string $bytes A 16-byte octet string. This is an open blob
* of data that you may fill with 128 bits of information. Be aware,
* however, bits 48 through 51 will be replaced with the UUID version
* field, and bits 64 and 65 will be replaced with the UUID variant. You
* MUST NOT rely on these bits for your application needs.
*
* @return UuidInterface A UuidInterface instance that represents a
* version 8 UUID
*/
public static function uuid8(string $bytes): UuidInterface
{
$factory = self::getFactory();
if (method_exists($factory, 'uuid8')) {
/** @var UuidInterface */
return $factory->uuid8($bytes);
}
throw new UnsupportedOperationException(
'The provided factory does not support the uuid8() method',
);
}
}

View File

@@ -24,6 +24,7 @@ use Ramsey\Uuid\Generator\DefaultTimeGenerator;
use Ramsey\Uuid\Generator\NameGeneratorInterface;
use Ramsey\Uuid\Generator\RandomGeneratorInterface;
use Ramsey\Uuid\Generator\TimeGeneratorInterface;
use Ramsey\Uuid\Generator\UnixTimeGenerator;
use Ramsey\Uuid\Lazy\LazyUuidFromString;
use Ramsey\Uuid\Provider\NodeProviderInterface;
use Ramsey\Uuid\Provider\Time\FixedTimeProvider;
@@ -45,61 +46,26 @@ use const STR_PAD_LEFT;
class UuidFactory implements UuidFactoryInterface
{
private CodecInterface $codec;
private DceSecurityGeneratorInterface $dceSecurityGenerator;
private NameGeneratorInterface $nameGenerator;
private NodeProviderInterface $nodeProvider;
private NumberConverterInterface $numberConverter;
private RandomGeneratorInterface $randomGenerator;
private TimeConverterInterface $timeConverter;
private TimeGeneratorInterface $timeGenerator;
private TimeGeneratorInterface $unixTimeGenerator;
private UuidBuilderInterface $uuidBuilder;
private ValidatorInterface $validator;
/**
* @var CodecInterface
* @var bool whether the feature set was provided from outside, or we can
* operate under "default" assumptions
*/
private $codec;
private bool $isDefaultFeatureSet;
/**
* @var DceSecurityGeneratorInterface
*/
private $dceSecurityGenerator;
/**
* @var NameGeneratorInterface
*/
private $nameGenerator;
/**
* @var NodeProviderInterface
*/
private $nodeProvider;
/**
* @var NumberConverterInterface
*/
private $numberConverter;
/**
* @var RandomGeneratorInterface
*/
private $randomGenerator;
/**
* @var TimeConverterInterface
*/
private $timeConverter;
/**
* @var TimeGeneratorInterface
*/
private $timeGenerator;
/**
* @var UuidBuilderInterface
*/
private $uuidBuilder;
/**
* @var ValidatorInterface
*/
private $validator;
/** @var bool whether the feature set was provided from outside, or we can operate under "default" assumptions */
private $isDefaultFeatureSet;
/**
* @param FeatureSet $features A set of available features in the current environment
* @param FeatureSet|null $features A set of available features in the current environment
*/
public function __construct(?FeatureSet $features = null)
{
@@ -117,6 +83,7 @@ class UuidFactory implements UuidFactoryInterface
$this->timeGenerator = $features->getTimeGenerator();
$this->uuidBuilder = $features->getBuilder();
$this->validator = $features->getValidator();
$this->unixTimeGenerator = $features->getUnixTimeGenerator();
}
/**
@@ -342,7 +309,15 @@ class UuidFactory implements UuidFactoryInterface
$bytes = $timeGenerator->generate($nodeHex, $clockSeq);
return $this->uuidFromBytesAndVersion($bytes, 1);
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_TIME);
}
/**
* @psalm-pure
*/
public function fromHexadecimal(Hexadecimal $hex): UuidInterface
{
return $this->codec->decode($hex->__toString());
}
/**
@@ -352,7 +327,7 @@ class UuidFactory implements UuidFactoryInterface
{
$bytes = $this->timeGenerator->generate($node, $clockSeq);
return $this->uuidFromBytesAndVersion($bytes, 1);
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_TIME);
}
public function uuid2(
@@ -368,7 +343,7 @@ class UuidFactory implements UuidFactoryInterface
$clockSeq
);
return $this->uuidFromBytesAndVersion($bytes, 2);
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_DCE_SECURITY);
}
/**
@@ -377,14 +352,14 @@ class UuidFactory implements UuidFactoryInterface
*/
public function uuid3($ns, string $name): UuidInterface
{
return $this->uuidFromNsAndName($ns, $name, 3, 'md5');
return $this->uuidFromNsAndName($ns, $name, Uuid::UUID_TYPE_HASH_MD5, 'md5');
}
public function uuid4(): UuidInterface
{
$bytes = $this->randomGenerator->generate(16);
return $this->uuidFromBytesAndVersion($bytes, 4);
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_RANDOM);
}
/**
@@ -393,7 +368,7 @@ class UuidFactory implements UuidFactoryInterface
*/
public function uuid5($ns, string $name): UuidInterface
{
return $this->uuidFromNsAndName($ns, $name, 5, 'sha1');
return $this->uuidFromNsAndName($ns, $name, Uuid::UUID_TYPE_HASH_SHA1, 'sha1');
}
public function uuid6(?Hexadecimal $node = null, ?int $clockSeq = null): UuidInterface
@@ -412,7 +387,46 @@ class UuidFactory implements UuidFactoryInterface
$v6Bytes = hex2bin(substr($v6, 1, 12) . '0' . substr($v6, -3));
$v6Bytes .= substr($bytes, 8);
return $this->uuidFromBytesAndVersion($v6Bytes, 6);
return $this->uuidFromBytesAndVersion($v6Bytes, Uuid::UUID_TYPE_REORDERED_TIME);
}
/**
* Returns a version 7 (Unix Epoch time) UUID
*
* @param DateTimeInterface|null $dateTime An optional date/time from which
* to create the version 7 UUID. If not provided, the UUID is generated
* using the current date/time.
*
* @return UuidInterface A UuidInterface instance that represents a
* version 7 UUID
*/
public function uuid7(?DateTimeInterface $dateTime = null): UuidInterface
{
assert($this->unixTimeGenerator instanceof UnixTimeGenerator);
$bytes = $this->unixTimeGenerator->generate(null, null, $dateTime);
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_UNIX_TIME);
}
/**
* Returns a version 8 (Custom) UUID
*
* The bytes provided may contain any value according to your application's
* needs. Be aware, however, that other applications may not understand the
* semantics of the value.
*
* @param string $bytes A 16-byte octet string. This is an open blob
* of data that you may fill with 128 bits of information. Be aware,
* however, bits 48 through 51 will be replaced with the UUID version
* field, and bits 64 and 65 will be replaced with the UUID variant. You
* MUST NOT rely on these bits for your application needs.
*
* @return UuidInterface A UuidInterface instance that represents a
* version 8 UUID
*/
public function uuid8(string $bytes): UuidInterface
{
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_CUSTOM);
}
/**
@@ -430,6 +444,7 @@ class UuidFactory implements UuidFactoryInterface
*/
public function uuid(string $bytes): UuidInterface
{
/** @psalm-suppress ImpurePropertyFetch */
return $this->uuidBuilder->build($this->codec, $bytes);
}
@@ -447,8 +462,12 @@ class UuidFactory implements UuidFactoryInterface
*
* @psalm-pure
*/
private function uuidFromNsAndName($ns, string $name, int $version, string $hashAlgorithm): UuidInterface
{
private function uuidFromNsAndName(
UuidInterface | string $ns,
string $name,
int $version,
string $hashAlgorithm
): UuidInterface {
if (!($ns instanceof UuidInterface)) {
$ns = $this->fromString($ns);
}
@@ -488,6 +507,7 @@ class UuidFactory implements UuidFactoryInterface
return LazyUuidFromString::fromBytes($bytes);
}
/** @psalm-suppress ImpureVariable */
return $this->uuid($bytes);
}
}

View File

@@ -25,6 +25,61 @@ use Ramsey\Uuid\Validator\ValidatorInterface;
*/
interface UuidFactoryInterface
{
/**
* Creates a UUID from a byte string
*
* @param string $bytes A binary string
*
* @return UuidInterface A UuidInterface instance created from a binary
* string representation
*
* @psalm-pure
*/
public function fromBytes(string $bytes): UuidInterface;
/**
* Creates a UUID from a DateTimeInterface instance
*
* @param DateTimeInterface $dateTime The date and time
* @param Hexadecimal|null $node A 48-bit number representing the hardware
* address
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates
* that could arise when the clock is set backwards in time or if the
* node ID changes
*
* @return UuidInterface A UuidInterface instance that represents a
* version 1 UUID created from a DateTimeInterface instance
*/
public function fromDateTime(
DateTimeInterface $dateTime,
?Hexadecimal $node = null,
?int $clockSeq = null
): UuidInterface;
/**
* Creates a UUID from a 128-bit integer string
*
* @param string $integer String representation of 128-bit integer
*
* @return UuidInterface A UuidInterface instance created from the string
* representation of a 128-bit integer
*
* @psalm-pure
*/
public function fromInteger(string $integer): UuidInterface;
/**
* Creates a UUID from the string standard representation
*
* @param string $uuid A hexadecimal string
*
* @return UuidInterface A UuidInterface instance created from a hexadecimal
* string representation
*
* @psalm-pure
*/
public function fromString(string $uuid): UuidInterface;
/**
* Returns the validator to use for the factory
*
@@ -33,7 +88,7 @@ interface UuidFactoryInterface
public function getValidator(): ValidatorInterface;
/**
* Returns a version 1 (time-based) UUID from a host ID, sequence number,
* Returns a version 1 (Gregorian time) UUID from a host ID, sequence number,
* and the current time
*
* @param Hexadecimal|int|string|null $node A 48-bit number representing the
@@ -111,7 +166,7 @@ interface UuidFactoryInterface
public function uuid5($ns, string $name): UuidInterface;
/**
* Returns a version 6 (ordered-time) UUID from a host ID, sequence number,
* Returns a version 6 (reordered time) UUID from a host ID, sequence number,
* and the current time
*
* @param Hexadecimal|null $node A 48-bit number representing the hardware
@@ -124,59 +179,4 @@ interface UuidFactoryInterface
* version 6 UUID
*/
public function uuid6(?Hexadecimal $node = null, ?int $clockSeq = null): UuidInterface;
/**
* Creates a UUID from a byte string
*
* @param string $bytes A binary string
*
* @return UuidInterface A UuidInterface instance created from a binary
* string representation
*
* @psalm-pure
*/
public function fromBytes(string $bytes): UuidInterface;
/**
* Creates a UUID from the string standard representation
*
* @param string $uuid A hexadecimal string
*
* @return UuidInterface A UuidInterface instance created from a hexadecimal
* string representation
*
* @psalm-pure
*/
public function fromString(string $uuid): UuidInterface;
/**
* Creates a UUID from a 128-bit integer string
*
* @param string $integer String representation of 128-bit integer
*
* @return UuidInterface A UuidInterface instance created from the string
* representation of a 128-bit integer
*
* @psalm-pure
*/
public function fromInteger(string $integer): UuidInterface;
/**
* Creates a UUID from a DateTimeInterface instance
*
* @param DateTimeInterface $dateTime The date and time
* @param Hexadecimal|null $node A 48-bit number representing the hardware
* address
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates
* that could arise when the clock is set backwards in time or if the
* node ID changes
*
* @return UuidInterface A UuidInterface instance that represents a
* version 1 UUID created from a DateTimeInterface instance
*/
public function fromDateTime(
DateTimeInterface $dateTime,
?Hexadecimal $node = null,
?int $clockSeq = null
): UuidInterface;
}

View File

@@ -19,6 +19,7 @@ use Ramsey\Uuid\Fields\FieldsInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
use Serializable;
use Stringable;
/**
* A UUID is a universally unique identifier adhering to an agreed-upon
@@ -29,7 +30,8 @@ use Serializable;
interface UuidInterface extends
DeprecatedUuidInterface,
JsonSerializable,
Serializable
Serializable,
Stringable
{
/**
* Returns -1, 0, or 1 if the UUID is less than, equal to, or greater than
@@ -44,7 +46,7 @@ interface UuidInterface extends
*
* @param UuidInterface $other The UUID to compare
*
* @return int -1, 0, or 1 if the UUID is less than, equal to, or greater than $other
* @return int<-1,1> -1, 0, or 1 if the UUID is less than, equal to, or greater than $other
*/
public function compareTo(UuidInterface $other): int;
@@ -83,6 +85,14 @@ interface UuidInterface extends
*/
public function getInteger(): IntegerObject;
/**
* Returns the string standard representation of the UUID as a URN
*
* @link http://en.wikipedia.org/wiki/Uniform_Resource_Name Uniform Resource Name
* @link https://tools.ietf.org/html/rfc4122#section-3 RFC 4122, § 3: Namespace Registration Template
*/
public function getUrn(): string;
/**
* Returns the string standard representation of the UUID
*

View File

@@ -15,17 +15,18 @@ declare(strict_types=1);
namespace Ramsey\Uuid;
use DateTimeInterface;
use Ramsey\Uuid\Type\Hexadecimal;
use Ramsey\Uuid\Type\Integer as IntegerObject;
/**
* Returns a version 1 (time-based) UUID from a host ID, sequence number,
* Returns a version 1 (Gregorian time) UUID from a host ID, sequence number,
* and the current time
*
* @param Hexadecimal|int|string|null $node A 48-bit number representing the
* hardware address; this number may be represented as an integer or a
* hexadecimal string
* @param int $clockSeq A 14-bit number used to help avoid duplicates that
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates that
* could arise when the clock is set backwards in time or if the node ID
* changes
*
@@ -106,12 +107,12 @@ function v5($ns, string $name): string
}
/**
* Returns a version 6 (ordered-time) UUID from a host ID, sequence number,
* Returns a version 6 (reordered time) UUID from a host ID, sequence number,
* and the current time
*
* @param Hexadecimal|null $node A 48-bit number representing the hardware
* address
* @param int $clockSeq A 14-bit number used to help avoid duplicates that
* @param int|null $clockSeq A 14-bit number used to help avoid duplicates that
* could arise when the clock is set backwards in time or if the node ID
* changes
*
@@ -121,3 +122,37 @@ function v6(?Hexadecimal $node = null, ?int $clockSeq = null): string
{
return Uuid::uuid6($node, $clockSeq)->toString();
}
/**
* Returns a version 7 (Unix Epoch time) UUID
*
* @param DateTimeInterface|null $dateTime An optional date/time from which
* to create the version 7 UUID. If not provided, the UUID is generated
* using the current date/time.
*
* @return non-empty-string Version 7 UUID as a string
*/
function v7(?DateTimeInterface $dateTime = null): string
{
return Uuid::uuid7($dateTime)->toString();
}
/**
* Returns a version 8 (custom) UUID
*
* The bytes provided may contain any value according to your application's
* needs. Be aware, however, that other applications may not understand the
* semantics of the value.
*
* @param string $bytes A 16-byte octet string. This is an open blob
* of data that you may fill with 128 bits of information. Be aware,
* however, bits 48 through 51 will be replaced with the UUID version
* field, and bits 64 and 65 will be replaced with the UUID variant. You
* MUST NOT rely on these bits for your application needs.
*
* @return non-empty-string Version 8 UUID as a string
*/
function v8(string $bytes): string
{
return Uuid::uuid8($bytes)->toString();
}