From b2b28ae6710c7a7de93c29313175ac832badaf99 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 28 Apr 2024 21:42:51 +0200 Subject: [PATCH] Decoder: big integers are decoded as strings [Closes #9] --- src/Neon/Node/LiteralNode.php | 33 +++++++++++++++++++++++++++++---- tests/Neon/Decoder.phpt | 6 ++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Neon/Node/LiteralNode.php b/src/Neon/Node/LiteralNode.php index 2dd5990..23b4b32 100644 --- a/src/Neon/Node/LiteralNode.php +++ b/src/Neon/Node/LiteralNode.php @@ -46,16 +46,16 @@ public static function parse(string $value, bool $isKey = false): mixed return self::SimpleTypes[$value]; } elseif (is_numeric($value)) { - return $value * 1; + return is_int($num = $value * 1) || preg_match('#[.eE]#', $value) ? $num : $value; } elseif (preg_match(self::PatternHex, $value)) { - return hexdec($value); + return self::baseConvert(substr($value, 2), 16); } elseif (preg_match(self::PatternOctal, $value)) { - return octdec($value); + return self::baseConvert(substr($value, 2), 8); } elseif (preg_match(self::PatternBinary, $value)) { - return bindec($value); + return self::baseConvert(substr($value, 2), 2); } elseif (!$isKey && preg_match(self::PatternDatetime, $value)) { return new \DateTimeImmutable($value); @@ -66,6 +66,30 @@ public static function parse(string $value, bool $isKey = false): mixed } + public static function baseConvert(string $number, int $base): string|int + { + if (strlen($number) < 16) { + $res = base_convert($number, $base, 10); + } elseif (!extension_loaded('bcmath')) { + throw new Exception("The number '$number' is too large, enable 'bcmath' extension to handle it."); + } else { + $res = '0'; + for ($i = 0; $i < strlen($number); $i++) { + $char = $number[$i]; + $char = match (true) { + $char >= 'a' => ord($char) - 87, + $char >= 'A' => ord($char) - 55, + default => $char, + }; + $res = bcmul($res, (string) $base, 0); + $res = bcadd($res, (string) $char, 0); + } + } + + return is_int($num = $res * 1) ? $num : $res; + } + + public function toString(): string { if ($this->value instanceof \DateTimeInterface) { @@ -82,6 +106,7 @@ public function toString(): string return str_contains($res, '.') ? $res : $res . '.0'; } elseif (is_int($this->value) || is_bool($this->value) || $this->value === null) { + return json_encode($this->value); } else { diff --git a/tests/Neon/Decoder.phpt b/tests/Neon/Decoder.phpt index 8646904..215a630 100644 --- a/tests/Neon/Decoder.phpt +++ b/tests/Neon/Decoder.phpt @@ -35,6 +35,12 @@ $dataSet = [ ['1.1E1', 11.0], ['1.1E+1', 11.0], ['1.1E-1', 0.11], + ['2147483647', 2_147_483_647], + + [(string) PHP_INT_MAX, PHP_INT_MAX], + ['12341234123412344564654657845465', '12341234123412344564654657845465'], + ['0x12341234123412344564654657845465', '24196472569643293506997087088694023269'], + ['12341234123412344564654657845465.0', 12_341_234_123_412_344_564_654_657_845_465.0], // literals ['null', null],