From 3546a86f7d911b8eb4ebca1c14337ea1e1ec232d Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Thu, 9 Jan 2020 13:24:19 -0600 Subject: [PATCH] Deprecate BigNumberConverter and BigNumberTimeConverter --- src/Converter/Number/BigNumberConverter.php | 43 ++++------ src/Converter/Time/BigNumberTimeConverter.php | 71 ++++------------ src/FeatureSet.php | 52 +++++++----- .../Number/BigNumberConverterTest.php | 51 ++---------- .../Time/BigNumberTimeConverterTest.php | 82 ++++++------------- 5 files changed, 94 insertions(+), 205 deletions(-) diff --git a/src/Converter/Number/BigNumberConverter.php b/src/Converter/Number/BigNumberConverter.php index 313ebbb..446672f 100644 --- a/src/Converter/Number/BigNumberConverter.php +++ b/src/Converter/Number/BigNumberConverter.php @@ -14,53 +14,42 @@ declare(strict_types=1); namespace Ramsey\Uuid\Converter\Number; -use Moontoast\Math\BigNumber; -use Ramsey\Uuid\Converter\DependencyCheckTrait; use Ramsey\Uuid\Converter\NumberConverterInterface; -use Ramsey\Uuid\Converter\NumberStringTrait; -use Ramsey\Uuid\Exception\InvalidArgumentException; -use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; +use Ramsey\Uuid\Math\BrickMathCalculator; /** - * BigNumberConverter uses moontoast/math to convert UUIDs from hexadecimal - * characters into string representations of integers and vice versa + * Previously used to integrate moontoast/math as a bignum arithmetic library, + * BigNumberConverter is deprecated in favor of ArbitraryPrecisionNumberConverter + * + * @deprecated Transition to {@see GenericNumberConverter}. */ class BigNumberConverter implements NumberConverterInterface { - use DependencyCheckTrait; - use NumberStringTrait; + /** + * @var NumberConverterInterface + */ + private $converter; + + public function __construct() + { + $this->converter = new GenericNumberConverter(new BrickMathCalculator()); + } /** - * @throws InvalidArgumentException if $hex is not a hexadecimal string - * @throws UnsatisfiedDependencyException if the chosen converter is not present - * * @inheritDoc - * * @psalm-pure */ public function fromHex(string $hex): string { - $this->checkMoontoastMathLibrary(); - $this->checkHexadecimalString($hex, 'hex'); - - /** @psalm-suppress ImpureMethodCall */ - return BigNumber::convertToBase10($hex, 16); + return $this->converter->fromHex($hex); } /** - * @throws InvalidArgumentException if $integer is not an integer string - * @throws UnsatisfiedDependencyException if the chosen converter is not present - * * @inheritDoc - * * @psalm-pure */ public function toHex(string $number): string { - $this->checkMoontoastMathLibrary(); - $this->checkIntegerString($number, 'number'); - - /** @psalm-suppress ImpureMethodCall */ - return BigNumber::convertFromBase10($number, 16); + return $this->converter->toHex($number); } } diff --git a/src/Converter/Time/BigNumberTimeConverter.php b/src/Converter/Time/BigNumberTimeConverter.php index dafc471..6d04a5d 100644 --- a/src/Converter/Time/BigNumberTimeConverter.php +++ b/src/Converter/Time/BigNumberTimeConverter.php @@ -14,81 +14,42 @@ declare(strict_types=1); namespace Ramsey\Uuid\Converter\Time; -use Moontoast\Math\BigNumber; -use Ramsey\Uuid\Converter\DependencyCheckTrait; -use Ramsey\Uuid\Converter\NumberStringTrait; use Ramsey\Uuid\Converter\TimeConverterInterface; -use Ramsey\Uuid\Exception\InvalidArgumentException; -use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; +use Ramsey\Uuid\Math\BrickMathCalculator; /** - * BigNumberTimeConverter uses the moontoast/math library's `BigNumber` to - * provide facilities for converting parts of time into representations that may - * be used in UUIDs + * Previously used to integrate moontoast/math as a bignum arithmetic library, + * BigNumberTimeConverter is deprecated in favor of ArbitraryPrecisionTimeConverter + * + * @deprecated Transition to {@see GenericTimeConverter}. */ class BigNumberTimeConverter implements TimeConverterInterface { - use DependencyCheckTrait; - use NumberStringTrait; + /** + * @var TimeConverterInterface + */ + private $converter; + + public function __construct() + { + $this->converter = new GenericTimeConverter(new BrickMathCalculator()); + } /** - * @throws InvalidArgumentException if $seconds or $microseconds are not integer strings - * @throws UnsatisfiedDependencyException if the chosen converter is not present - * * @inheritDoc - * * @psalm-pure - * @psalm-suppress ImpureMethodCall The use of the external moontoast/math - * library causes Psalm to complain about impure method calls. */ public function calculateTime(string $seconds, string $microSeconds): array { - $this->checkMoontoastMathLibrary(); - $this->checkIntegerString($seconds, 'seconds'); - $this->checkIntegerString($microSeconds, 'microSeconds'); - - $uuidTime = new BigNumber('0'); - - $sec = new BigNumber($seconds); - $sec->multiply('10000000'); - - $usec = new BigNumber($microSeconds); - $usec->multiply('10'); - - $uuidTime - ->add($sec) - ->add($usec) - ->add('122192928000000000'); - - $uuidTimeHex = sprintf('%016s', $uuidTime->convertToBase(16)); - - return [ - 'low' => substr($uuidTimeHex, 8), - 'mid' => substr($uuidTimeHex, 4, 4), - 'hi' => substr($uuidTimeHex, 0, 4), - ]; + return $this->converter->calculateTime($seconds, $microSeconds); } /** - * @throws InvalidArgumentException if $timestamp is not an integer string - * @throws UnsatisfiedDependencyException if the chosen converter is not present - * * @inheritDoc - * * @psalm-pure - * @psalm-suppress ImpureMethodCall The use of the external moontoast/math - * library causes Psalm to complain about impure method calls. */ public function convertTime(string $timestamp): string { - $this->checkMoontoastMathLibrary(); - $this->checkIntegerString($timestamp, 'timestamp'); - - $ts = new BigNumber($timestamp, 20); - $ts->subtract('122192928000000000'); - $ts->divide('10000000.0'); - $ts->round(); - - return $ts->getValue(); + return $this->converter->convertTime($timestamp); } } diff --git a/src/FeatureSet.php b/src/FeatureSet.php index 01eb0dc..8afaa3f 100644 --- a/src/FeatureSet.php +++ b/src/FeatureSet.php @@ -21,12 +21,12 @@ use Ramsey\Uuid\Builder\UuidBuilderInterface; use Ramsey\Uuid\Codec\CodecInterface; use Ramsey\Uuid\Codec\GuidStringCodec; use Ramsey\Uuid\Codec\StringCodec; -use Ramsey\Uuid\Converter\Number\BigNumberConverter; use Ramsey\Uuid\Converter\Number\DegradedNumberConverter; +use Ramsey\Uuid\Converter\Number\GenericNumberConverter; use Ramsey\Uuid\Converter\Number\GmpConverter; use Ramsey\Uuid\Converter\NumberConverterInterface; -use Ramsey\Uuid\Converter\Time\BigNumberTimeConverter; use Ramsey\Uuid\Converter\Time\DegradedTimeConverter; +use Ramsey\Uuid\Converter\Time\GenericTimeConverter; use Ramsey\Uuid\Converter\Time\GmpTimeConverter; use Ramsey\Uuid\Converter\Time\PhpTimeConverter; use Ramsey\Uuid\Converter\TimeConverterInterface; @@ -38,6 +38,8 @@ use Ramsey\Uuid\Generator\TimeGeneratorInterface; use Ramsey\Uuid\Guid\DegradedGuidBuilder; use Ramsey\Uuid\Guid\GuidBuilder; use Ramsey\Uuid\Nonstandard\DegradedUuidBuilder as NonstandardDegradedUuidBuilder; +use Ramsey\Uuid\Math\BrickMathCalculator; +use Ramsey\Uuid\Math\CalculatorInterface; use Ramsey\Uuid\Nonstandard\UuidBuilder as NonstandardUuidBuilder; use Ramsey\Uuid\Provider\Node\FallbackNodeProvider; use Ramsey\Uuid\Provider\Node\RandomNodeProvider; @@ -121,6 +123,11 @@ class FeatureSet */ private $validator; + /** + * @var CalculatorInterface + */ + private $calculator; + /** * @param bool $useGuids True build UUIDs using the GuidStringCodec * @param bool $force32Bit True to force the use of 32-bit functionality @@ -148,6 +155,7 @@ class FeatureSet $this->ignoreSystemNode = $ignoreSystemNode; $this->enablePecl = $enablePecl; + $this->calculator = new BrickMathCalculator(); $this->numberConverter = $this->buildNumberConverter(); $this->timeConverter = $this->buildTimeConverter(); $this->builder = $this->buildUuidBuilder($useGuids); @@ -166,6 +174,14 @@ class FeatureSet return $this->builder; } + /** + * Returns the calculator configured for this environment + */ + public function getCalculator(): CalculatorInterface + { + return $this->calculator; + } + /** * Returns the codec configured for this environment */ @@ -214,6 +230,14 @@ class FeatureSet return $this->validator; } + /** + * Sets the calculator to use in this environment + */ + public function setCalculator(CalculatorInterface $calculator): void + { + $this->calculator = $calculator; + } + /** * Sets the time provider to use in this environment */ @@ -264,15 +288,7 @@ class FeatureSet */ private function buildNumberConverter(): NumberConverterInterface { - if ($this->hasGmp()) { - return new GmpConverter(); - } - - if ($this->hasBigNumber()) { - return new BigNumberConverter(); - } - - return new DegradedNumberConverter(); + return new GenericNumberConverter($this->getCalculator()); } /** @@ -307,19 +323,13 @@ class FeatureSet */ private function buildTimeConverter(): TimeConverterInterface { + $genericConverter = new GenericTimeConverter($this->getCalculator()); + if ($this->is64BitSystem()) { - return new PhpTimeConverter(); + return new PhpTimeConverter($genericConverter); } - if ($this->hasGmp()) { - return new GmpTimeConverter(); - } - - if ($this->hasBigNumber()) { - return new BigNumberTimeConverter(); - } - - return new DegradedTimeConverter(); + return $genericConverter; } /** diff --git a/tests/Converter/Number/BigNumberConverterTest.php b/tests/Converter/Number/BigNumberConverterTest.php index f96a56a..83bcff2 100644 --- a/tests/Converter/Number/BigNumberConverterTest.php +++ b/tests/Converter/Number/BigNumberConverterTest.php @@ -4,62 +4,18 @@ declare(strict_types=1); namespace Ramsey\Uuid\Test\Converter\Number; -use AspectMock\Test as AspectMock; use Ramsey\Uuid\Converter\Number\BigNumberConverter; use Ramsey\Uuid\Exception\InvalidArgumentException; -use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; use Ramsey\Uuid\Test\TestCase; class BigNumberConverterTest extends TestCase { - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testFromHexThrowsExceptionWhenMoontoastMathNotPresent(): void - { - $classExists = AspectMock::func( - 'Ramsey\Uuid\Converter', - 'class_exists', - false - ); - - $converter = new BigNumberConverter(); - - $this->expectException(UnsatisfiedDependencyException::class); - $this->expectExceptionMessage('moontoast/math must be present to use this converter'); - - $converter->fromHex('abcd'); - $classExists->verifyInvokedOnce(['Moontoast\Math\BigNumber']); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testToHexThrowsExceptionWhenMoontoastMathNotPresent(): void - { - $classExists = AspectMock::func( - 'Ramsey\Uuid\Converter', - 'class_exists', - false - ); - - $converter = new BigNumberConverter(); - - $this->expectException(UnsatisfiedDependencyException::class); - $this->expectExceptionMessage('moontoast/math must be present to use this converter'); - - $converter->toHex('1234'); - $classExists->verifyInvokedOnce(['Moontoast\Math\BigNumber']); - } - public function testFromHexThrowsExceptionWhenStringDoesNotContainOnlyHexadecimalCharacters(): void { $converter = new BigNumberConverter(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$hex must contain only hexadecimal characters'); + $this->expectExceptionMessage('"." is not a valid character in base 16'); $converter->fromHex('123.34'); } @@ -69,7 +25,10 @@ class BigNumberConverterTest extends TestCase $converter = new BigNumberConverter(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$number must contain only digits'); + $this->expectExceptionMessage( + 'Value must be a signed integer or a string containing only digits ' + . '0-9 and, optionally, a sign (+ or -)' + ); $converter->toHex('123.34'); } diff --git a/tests/Converter/Time/BigNumberTimeConverterTest.php b/tests/Converter/Time/BigNumberTimeConverterTest.php index 356c05d..fb1d437 100644 --- a/tests/Converter/Time/BigNumberTimeConverterTest.php +++ b/tests/Converter/Time/BigNumberTimeConverterTest.php @@ -4,26 +4,31 @@ declare(strict_types=1); namespace Ramsey\Uuid\Test\Converter\Time; -use AspectMock\Test as AspectMock; +use Brick\Math\BigInteger; use Ramsey\Uuid\Converter\Time\BigNumberTimeConverter; use Ramsey\Uuid\Exception\InvalidArgumentException; -use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; use Ramsey\Uuid\Test\TestCase; class BigNumberTimeConverterTest extends TestCase { public function testCalculateTimeReturnsArrayOfTimeSegments(): void { - $this->skip64BitTest(); + $seconds = BigInteger::of(5); + $microSeconds = BigInteger::of(3); - $seconds = 5; - $microSeconds = 3; - $calculatedTime = ($seconds * 10000000) + ($microSeconds * 10) + 0x01b21dd213814000; + $calculatedTime = BigInteger::zero() + ->plus($seconds->multipliedBy(10000000)) + ->plus($microSeconds->multipliedBy(10)) + ->plus(BigInteger::fromBase('01b21dd213814000', 16)); + + $maskLow = BigInteger::fromBase('ffffffff', 16); + $maskMid = BigInteger::fromBase('ffff', 16); + $maskHi = BigInteger::fromBase('0fff', 16); $expectedArray = [ - 'low' => sprintf('%08x', $calculatedTime & 0xffffffff), - 'mid' => sprintf('%04x', ($calculatedTime >> 32) & 0xffff), - 'hi' => sprintf('%04x', ($calculatedTime >> 48) & 0x0fff), + 'low' => sprintf('%08s', $calculatedTime->and($maskLow)->toBase(16)), + 'mid' => sprintf('%04s', $calculatedTime->shiftedRight(32)->and($maskMid)->toBase(16)), + 'hi' => sprintf('%04s', $calculatedTime->shiftedRight(48)->and($maskHi)->toBase(16)), ]; $converter = new BigNumberTimeConverter(); @@ -34,62 +39,21 @@ class BigNumberTimeConverterTest extends TestCase public function testConvertTime(): void { - $this->skip64BitTest(); - $converter = new BigNumberTimeConverter(); $returned = $converter->convertTime('135606608744910000'); $this->assertSame('1341368074', $returned); } - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testCalculateTimeThrowsExceptionWhenGmpExtensionNotPresent(): void - { - $classExists = AspectMock::func( - 'Ramsey\Uuid\Converter', - 'class_exists', - false - ); - - $converter = new BigNumberTimeConverter(); - - $this->expectException(UnsatisfiedDependencyException::class); - $this->expectExceptionMessage('moontoast/math must be present to use this converter'); - - $converter->calculateTime('1234', '5678'); - $classExists->verifyInvokedOnce(['Moontoast\Math\BigNumber']); - } - - /** - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testConvertTimeThrowsExceptionWhenGmpExtensionNotPresent(): void - { - $classExists = AspectMock::func( - 'Ramsey\Uuid\Converter', - 'class_exists', - false - ); - - $converter = new BigNumberTimeConverter(); - - $this->expectException(UnsatisfiedDependencyException::class); - $this->expectExceptionMessage('moontoast/math must be present to use this converter'); - - $converter->convertTime('1234'); - $classExists->verifyInvokedOnce(['Moontoast\Math\BigNumber']); - } - public function testCalculateTimeThrowsExceptionWhenSecondsIsNotOnlyDigits(): void { $converter = new BigNumberTimeConverter(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$seconds must contain only digits'); + $this->expectExceptionMessage( + 'Value must be a signed integer or a string containing only digits ' + . '0-9 and, optionally, a sign (+ or -)' + ); $converter->calculateTime('12.34', '5678'); } @@ -99,7 +63,10 @@ class BigNumberTimeConverterTest extends TestCase $converter = new BigNumberTimeConverter(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$microSeconds must contain only digits'); + $this->expectExceptionMessage( + 'Value must be a signed integer or a string containing only digits ' + . '0-9 and, optionally, a sign (+ or -)' + ); $converter->calculateTime('1234', '56.78'); } @@ -109,7 +76,10 @@ class BigNumberTimeConverterTest extends TestCase $converter = new BigNumberTimeConverter(); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$timestamp must contain only digits'); + $this->expectExceptionMessage( + 'Value must be a signed integer or a string containing only digits ' + . '0-9 and, optionally, a sign (+ or -)' + ); $converter->convertTime('1234.56'); }