|
1 | 1 | <?php |
2 | 2 |
|
| 3 | +declare(strict_types=1); |
| 4 | + |
3 | 5 | namespace TinyBlocks\Encoder; |
4 | 6 |
|
5 | | -use TinyBlocks\Encoder\Internal\Exceptions\InvalidBase62Encoding; |
| 7 | +use TinyBlocks\Encoder\Internal\Exceptions\InvalidDecoding; |
| 8 | +use TinyBlocks\Encoder\Internal\Hexadecimal; |
6 | 9 |
|
7 | | -final class Base62 |
| 10 | +final readonly class Base62 implements Encoder |
8 | 11 | { |
9 | 12 | private const BASE62_RADIX = 62; |
10 | 13 | private const BASE62_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
| 14 | + private const BASE62_CHARACTER_LENGTH = 1; |
11 | 15 | private const BASE62_HEXADECIMAL_RADIX = 16; |
12 | 16 |
|
13 | | - public static function encode(string $value): string |
| 17 | + private function __construct(private string $value) |
14 | 18 | { |
15 | | - $bytes = 0; |
16 | | - $hexadecimal = bin2hex($value); |
| 19 | + } |
17 | 20 |
|
18 | | - while (str_starts_with($hexadecimal, '00')) { |
19 | | - $bytes++; |
20 | | - $hexadecimal = substr($hexadecimal, 2); |
21 | | - } |
| 21 | + public static function from(string $value): Encoder |
| 22 | + { |
| 23 | + return new Base62(value: $value); |
| 24 | + } |
| 25 | + |
| 26 | + public function encode(): string |
| 27 | + { |
| 28 | + $hexadecimal = Hexadecimal::fromBinary(binary: $this->value); |
| 29 | + $bytes = $hexadecimal->removeLeadingZeroBytes(); |
22 | 30 |
|
23 | 31 | $base62 = str_repeat(self::BASE62_ALPHABET[0], $bytes); |
24 | 32 |
|
25 | | - if (empty($hexadecimal)) { |
| 33 | + if ($hexadecimal->isEmpty()) { |
26 | 34 | return $base62; |
27 | 35 | } |
28 | 36 |
|
29 | | - $number = gmp_init($hexadecimal, self::BASE62_HEXADECIMAL_RADIX); |
| 37 | + $number = $hexadecimal->toGmpInit(base: self::BASE62_HEXADECIMAL_RADIX); |
30 | 38 |
|
31 | | - return $base62 . gmp_strval($number, self::BASE62_RADIX); |
| 39 | + return sprintf('%s%s', $base62, gmp_strval($number, self::BASE62_RADIX)); |
32 | 40 | } |
33 | 41 |
|
34 | | - public static function decode(string $value): string |
| 42 | + public function decode(): string |
35 | 43 | { |
36 | | - if (strlen($value) !== strspn($value, self::BASE62_ALPHABET)) { |
37 | | - throw new InvalidBase62Encoding(value: $value); |
| 44 | + if (strlen($this->value) !== strspn($this->value, self::BASE62_ALPHABET)) { |
| 45 | + throw new InvalidDecoding(value: $this->value); |
38 | 46 | } |
39 | 47 |
|
40 | 48 | $bytes = 0; |
| 49 | + $value = $this->value; |
41 | 50 |
|
42 | 51 | while (!empty($value) && str_starts_with($value, self::BASE62_ALPHABET[0])) { |
43 | 52 | $bytes++; |
44 | | - $value = substr($value, 1); |
| 53 | + $value = substr($value, self::BASE62_CHARACTER_LENGTH); |
45 | 54 | } |
46 | 55 |
|
47 | 56 | if (empty($value)) { |
48 | 57 | return str_repeat("\x00", $bytes); |
49 | 58 | } |
50 | 59 |
|
51 | 60 | $number = gmp_init($value, self::BASE62_RADIX); |
52 | | - $hexadecimal = gmp_strval($number, self::BASE62_HEXADECIMAL_RADIX); |
| 61 | + $hexadecimal = Hexadecimal::fromGmp(number: $number, base: self::BASE62_HEXADECIMAL_RADIX); |
| 62 | + $hexadecimal->padLeft(); |
| 63 | + |
| 64 | + $binary = hex2bin(sprintf('%s%s', str_repeat('00', $bytes), $hexadecimal->toString())); |
53 | 65 |
|
54 | | - if (strlen($hexadecimal) % 2) { |
55 | | - $hexadecimal = '0' . $hexadecimal; |
| 66 | + if (!is_string($binary)) { |
| 67 | + throw new InvalidDecoding(value: $this->value); |
56 | 68 | } |
57 | 69 |
|
58 | | - return hex2bin(str_repeat('00', $bytes) . $hexadecimal); |
| 70 | + return $binary; |
59 | 71 | } |
60 | 72 | } |
0 commit comments