Deprecate BigNumberConverter and BigNumberTimeConverter

This commit is contained in:
Ben Ramsey
2020-01-09 13:24:19 -06:00
parent 9b2c93c5b5
commit 3546a86f7d
5 changed files with 94 additions and 205 deletions
+16 -27
View File
@@ -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);
}
}
+16 -55
View File
@@ -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);
}
}
+31 -21
View File
@@ -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;
}
/**
@@ -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');
}
@@ -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');
}