diff --git a/composer.json b/composer.json index 7a20ee2..0f6dd38 100644 --- a/composer.json +++ b/composer.json @@ -67,6 +67,7 @@ }, "autoload-dev": { "psr-4": { + "Ramsey\\Uuid\\Benchmark\\": "tests/benchmark/", "Ramsey\\Uuid\\StaticAnalysis\\": "tests/static-analysis/", "Ramsey\\Uuid\\Test\\": "tests/" } diff --git a/src/Lazy/LazyUuidFromString.php b/src/Lazy/LazyUuidFromString.php index 0ea9b19..bad46fb 100644 --- a/src/Lazy/LazyUuidFromString.php +++ b/src/Lazy/LazyUuidFromString.php @@ -22,7 +22,7 @@ use Ramsey\Uuid\Nonstandard\UuidV6; use Ramsey\Uuid\Rfc4122\UuidV1; use Ramsey\Uuid\Type\Hexadecimal; use Ramsey\Uuid\Type\Integer as IntegerObject; -use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidFactory; use Ramsey\Uuid\UuidInterface; use function assert; @@ -38,15 +38,26 @@ use function str_replace; * in consumer libraries. * * @psalm-immutable + * + * Note: the {@see FieldsInterface} does not declare methods that deprecated API + * relies upon: the API has been ported from the {@see \Ramsey\Uuid\Uuid} definition, + * and is deprecated anyway. + * Note: the deprecated API from {@see \Ramsey\Uuid\Uuid} is in use here (on purpose): it will be removed + * once the deprecated API is gone from this class too. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod */ 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 non-empty-string */ + /** + * @var string + * @psalm-var non-empty-string + */ private $uuid; - /** @var non-empty-string */ + /** @psalm-param non-empty-string $uuid */ public function __construct(string $uuid) { $this->uuid = $uuid; @@ -57,103 +68,128 @@ final class LazyUuidFromString implements UuidInterface return $this->uuid; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @param string $serialized + * + * @psalm-param non-empty-string $serialized + */ public function unserialize($serialized): void { $this->uuid = $serialized; } + /** @psalm-suppress DeprecatedMethod */ public function getNumberConverter(): NumberConverterInterface { return $this->unwrap() ->getNumberConverter(); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @psalm-suppress DeprecatedMethod + */ public function getFieldsHex(): array { return $this->unwrap() ->getFieldsHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getClockSeqHiAndReservedHex(): string { return $this->unwrap() ->getClockSeqHiAndReservedHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getClockSeqLowHex(): string { return $this->unwrap() ->getClockSeqLowHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getClockSequenceHex(): string { return $this->unwrap() ->getClockSequenceHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getDateTime(): DateTimeInterface { return $this->unwrap() ->getDateTime(); } + /** @psalm-suppress DeprecatedMethod */ public function getLeastSignificantBitsHex(): string { return $this->unwrap() ->getLeastSignificantBitsHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getMostSignificantBitsHex(): string { return $this->unwrap() ->getMostSignificantBitsHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getNodeHex(): string { return $this->unwrap() ->getNodeHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getTimeHiAndVersionHex(): string { return $this->unwrap() ->getTimeHiAndVersionHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getTimeLowHex(): string { return $this->unwrap() ->getTimeLowHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getTimeMidHex(): string { return $this->unwrap() ->getTimeMidHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getTimestampHex(): string { return $this->unwrap() ->getTimestampHex(); } + /** @psalm-suppress DeprecatedMethod */ public function getUrn(): string { return $this->unwrap() ->getUrn(); } + /** @psalm-suppress DeprecatedMethod */ public function getVariant(): ?int { return $this->unwrap() ->getVariant(); } + /** @psalm-suppress DeprecatedMethod */ public function getVersion(): ?int { return $this->unwrap() @@ -175,9 +211,16 @@ final class LazyUuidFromString implements UuidInterface return $this->uuid === $other->toString(); } + /** + * {@inheritDoc} + * + * @psalm-suppress MoreSpecificReturnType + * @psalm-suppress LessSpecificReturnStatement we know that {@see self::$uuid} is a non-empty string, so + * we know that {@see hex2bin} will retrieve a non-empty string too. + */ public function getBytes(): string { - return hex2bin(str_replace('-', '', $this->uuid)); + return (string) hex2bin(str_replace('-', '', $this->uuid)); } public function getFields(): FieldsInterface @@ -219,6 +262,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getClockSeqHiAndReserved()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getClockSeqHiAndReserved(): string { @@ -238,6 +286,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getClockSeqLow()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getClockSeqLow(): string { @@ -257,6 +310,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getClockSeq()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getClockSequence(): string { @@ -274,6 +332,11 @@ final class LazyUuidFromString implements UuidInterface * @deprecated This method will be removed in 5.0.0. There is no direct * alternative, but the same information may be obtained by splitting * in half the value returned by {@see UuidInterface::getHex()}. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getLeastSignificantBits(): string { @@ -287,6 +350,11 @@ final class LazyUuidFromString implements UuidInterface * @deprecated This method will be removed in 5.0.0. There is no direct * alternative, but the same information may be obtained by splitting * in half the value returned by {@see UuidInterface::getHex()}. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getMostSignificantBits(): string { @@ -302,6 +370,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getNode()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getNode(): string { @@ -321,6 +394,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getTimeHiAndVersion()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getTimeHiAndVersion(): string { @@ -340,6 +418,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getTimeLow()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getTimeLow(): string { @@ -359,6 +442,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getTimeMid()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getTimeMid(): string { @@ -378,6 +466,11 @@ final class LazyUuidFromString implements UuidInterface * instance, you may call {@see Rfc4122FieldsInterface::getTimestamp()} * and use the arbitrary-precision math library of your choice to * convert it to a string integer. + * + * @psalm-suppress UndefinedInterfaceMethod + * @psalm-suppress DeprecatedMethod + * @psalm-suppress MixedArgument + * @psalm-suppress MixedMethodCall */ public function getTimestamp(): string { @@ -414,9 +507,16 @@ final class LazyUuidFromString implements UuidInterface return $instance; } + /** + * @psalm-suppress ImpureMethodCall the retrieval of the factory is a clear violation of purity here: this is a + * known pitfall of the design of this library, where a value object contains + * a mutable reference to a factory. We use a fixed factory here, so the violation + * will not have real-world effects, as this object is only instantiated with the + * default factory settings/features. + */ private function unwrap(): UuidInterface { - return Uuid::getFactory() + return (new UuidFactory()) ->fromString($this->uuid); } } diff --git a/src/Uuid.php b/src/Uuid.php index d7c70ea..d0859a3 100644 --- a/src/Uuid.php +++ b/src/Uuid.php @@ -405,6 +405,11 @@ 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-suppress ImpureStaticProperty we know that the factory being replaced can lead to massive + * havoc across all consumers: that should never happen, and + * is generally to be discouraged. Until the factory is kept + * un-replaced, this method is effectively pure. */ public static function fromBytes(string $bytes): UuidInterface { @@ -437,6 +442,11 @@ 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-suppress ImpureStaticProperty we know that the factory being replaced can lead to massive + * havoc across all consumers: that should never happen, and + * is generally to be discouraged. Until the factory is kept + * un-replaced, this method is effectively pure. */ public static function fromString(string $uuid): UuidInterface { @@ -588,6 +598,11 @@ 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-suppress ImpureMethodCall we know that the factory being replaced can lead to massive + * havoc across all consumers: that should never happen, and + * is generally to be discouraged. Until the factory is kept + * un-replaced, this method is effectively pure. */ public static function uuid5($ns, string $name): UuidInterface { diff --git a/tests/UuidTest.php b/tests/UuidTest.php index ef46a3a..cef8511 100644 --- a/tests/UuidTest.php +++ b/tests/UuidTest.php @@ -1168,7 +1168,6 @@ class UuidTest extends TestCase * as the Python UUID library. * * @param string[] $fields - * @param class-string $class * * @dataProvider providePythonTests */ diff --git a/tests/benchmark/UuidStringConversionBench.php b/tests/benchmark/UuidStringConversionBench.php index 8703550..a15b804 100644 --- a/tests/benchmark/UuidStringConversionBench.php +++ b/tests/benchmark/UuidStringConversionBench.php @@ -132,7 +132,7 @@ final class UuidStringConversionBench /** @var UuidInterface */ private $uuid; /** - * @var UuidInterface + * @var UuidInterface[] * @psalm-var non-empty-list */ private $promiscuousUuids; @@ -152,7 +152,7 @@ final class UuidStringConversionBench */ private $uuidBytes; /** - * @var string + * @var string[] * @psalm-var non-empty-list */ private $promiscuousUuidsBytes; diff --git a/tests/phpstan.neon b/tests/phpstan.neon index 0d3c590..405d2d8 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -24,3 +24,8 @@ parameters: message: '#^Comparison operation ">" between 6 and 0 is always true\.$#' count: 1 path: ../src/Generator/CombGenerator.php + - + # Legacy methods of `Ramsey\Uuid` use interface methods that are NOT defined on `FieldsInterface` + message: '#^Call to an undefined method Ramsey\\Uuid\\Fields\\FieldsInterface::get.*$#' + count: 9 + path: ../src/Lazy/LazyUuidFromString.php