Skip to content

Commit 9634cb6

Browse files
authored
Merge pull request #2 from petrknap/hash
Add support for checksums
2 parents 8152edb + 8d4f1d4 commit 9634cb6

9 files changed

+105
-5
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ See the sample below for more information, or check out [`CoderInterface`](./src
77
use PetrKnap\Binary\Binary;
88

99
$data = base64_decode('hmlpFnFwbchsoQARSibVpfbWVfuwAHLbGxjFl9eC8fiGaWkWcXBtyGyhABFKJtWl9tZV+7AActsbGMWX14Lx+A==');
10-
$encoded = Binary::encode($data)->zlib()->base64(urlSafe: true)->getData();
11-
$decoded = Binary::decode($encoded)->base64()->zlib()->getData();
10+
$encoded = Binary::encode($data)->checksum()->zlib()->base64(urlSafe: true)->getData();
11+
$decoded = Binary::decode($encoded)->base64()->zlib()->checksum()->getData();
1212

1313
printf('Data was coded into `%s` %s.', $encoded, $decoded === $data ? 'successfully' : 'unsuccessfully');
1414
```

composer.json

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"base64",
2929
"encoder",
3030
"decoder",
31+
"checksum",
3132
"zlib",
3233
"compression",
3334
"decompression"
@@ -38,6 +39,7 @@
3839
"php": ">=8.1"
3940
},
4041
"require-dev": {
42+
"ext-mbstring": "*",
4143
"ext-zlib": "*",
4244
"nunomaduro/phpinsights": "^2.11",
4345
"petrknap/shorts": "^1.3",
@@ -59,6 +61,7 @@
5961
]
6062
},
6163
"suggest": {
64+
"ext-mbstring": "Required to check checksum",
6265
"ext-zlib": "Required to compress data"
6366
}
6467
}

src/CoderInterface.php

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
*/
1010
interface CoderInterface
1111
{
12+
public const CHECKSUM_ALGORITHM = 'crc32';
13+
1214
public function getData(): string;
1315

1416
/**
15-
* {@see base64_encode()}/{@see base64_decode()} data
17+
* {@see base64_encode()}/{@see base64_decode()} the data
1618
*
1719
* @link https://en.wikipedia.org/wiki/Base64
1820
*
@@ -21,7 +23,16 @@ public function getData(): string;
2123
public function base64(): static;
2224

2325
/**
24-
* {@see zlib_encode()}/{@see zlib_decode()} data
26+
* Encodes/decodes the data {@see hash()} into the data
27+
*
28+
* @link https://en.wikipedia.org/wiki/Checksum
29+
*
30+
* @throws TExceptionCouldNotCodeData
31+
*/
32+
public function checksum(string $algorithm = self::CHECKSUM_ALGORITHM): static;
33+
34+
/**
35+
* {@see zlib_encode()}/{@see zlib_decode()} the data
2536
*
2637
* @link https://en.wikipedia.org/wiki/Zlib
2738
*

src/Decoder.php

+15
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ public function base64(): static
2424
}
2525
}
2626

27+
public function checksum(string $algorithm = self::CHECKSUM_ALGORITHM): static
28+
{
29+
try {
30+
$checksumLength = mb_strlen(hash($algorithm, '', binary: true), encoding: '8bit');
31+
$dataLength = mb_strlen($this->data, encoding: '8bit') - $checksumLength;
32+
$data = mb_strcut($this->data, 0, $dataLength, encoding: '8bit');
33+
if ((new Encoder($data))->checksum($algorithm)->getData() !== $this->data) {
34+
throw new Exception\CouldNotDecodeData($this, __METHOD__);
35+
}
36+
return static::create($this, $data);
37+
} catch (Throwable $reason) {
38+
throw new Exception\CouldNotDecodeData($this, __METHOD__, $reason);
39+
}
40+
}
41+
2742
public function zlib(int $maxLength = self::ZLIB_MAX_LENGTH): static
2843
{
2944
try {

src/Encoder.php

+10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ public function base64(bool $urlSafe = self::BASE64_URL_SAFE): static
2121
}
2222
}
2323

24+
public function checksum(string $algorithm = self::CHECKSUM_ALGORITHM): static
25+
{
26+
try {
27+
$checksum = hash($algorithm, $this->data, binary: true);
28+
return static::create($this, $this->data . $checksum);
29+
} catch (Throwable $reason) {
30+
throw new Exception\CouldNotEncodeData($this, __METHOD__, $reason);
31+
}
32+
}
33+
2434
public function zlib(int $encoding = self::ZLIB_ENCODING, int $level = self::ZLIB_LEVEL): static
2535
{
2636
try {

tests/CoderTestCase.php

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ public static function dataBase64(): array
1717
];
1818
}
1919

20+
public static function dataChecksum(): array
21+
{
22+
$data = base64_decode(self::DATA_BASE64);
23+
return [
24+
'crc32' => [$data, $data . hex2bin('95a41ef8'), 'crc32'],
25+
'sha1' => [$data, $data . hex2bin('d0dc1cf9bf61884f8e7982e0b1b87954bd9ee9c7'), 'sha1'],
26+
];
27+
}
28+
2029
public static function dataZlib(): array
2130
{
2231
$data = base64_decode(self::DATA_BASE64);

tests/DecoderTest.php

+28
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,34 @@ public static function dataBase64Throws(): array
3030
];
3131
}
3232

33+
#[DataProvider('dataChecksum')]
34+
public function testDecodesChecksum(string $decoded, string $encoded, string $algorithm): void
35+
{
36+
self::assertSame(
37+
$decoded,
38+
(new Decoder($encoded))->checksum($algorithm)->getData(),
39+
);
40+
}
41+
42+
#[DataProvider('dataChecksumThrows')]
43+
public function testChecksumThrows(string $data, string $algorithm): void
44+
{
45+
self::expectException(Exception\CouldNotDecodeData::class);
46+
47+
(new Decoder($data))->checksum($algorithm);
48+
}
49+
50+
public static function dataChecksumThrows(): array
51+
{
52+
$dataSet = self::dataChecksum()[CoderInterface::CHECKSUM_ALGORITHM];
53+
return [
54+
'wrong algorithm' => [$dataSet[1], '?'],
55+
'short data' => ['?', CoderInterface::CHECKSUM_ALGORITHM],
56+
'wrong data' => [$dataSet[0], CoderInterface::CHECKSUM_ALGORITHM],
57+
'wrong checksum' => ['?' . $dataSet[1], CoderInterface::CHECKSUM_ALGORITHM],
58+
];
59+
}
60+
3361
#[DataProvider('dataZlib')]
3462
public function testDecodesZlib(string $decoded, string $encoded): void
3563
{

tests/EncoderTest.php

+24
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,30 @@ public function testEncodesBase64(string $decoded, string $encoded, bool $urlSaf
1515
);
1616
}
1717

18+
#[DataProvider('dataChecksum')]
19+
public function testEncodesChecksum(string $decoded, string $encoded, string $algorithm): void
20+
{
21+
self::assertSame(
22+
$encoded,
23+
(new Encoder($decoded))->checksum($algorithm)->getData(),
24+
);
25+
}
26+
27+
#[DataProvider('dataChecksumThrows')]
28+
public function testChecksumThrows(string $algorithm): void
29+
{
30+
self::expectException(Exception\CouldNotEncodeData::class);
31+
32+
(new Encoder(''))->checksum($algorithm);
33+
}
34+
35+
public static function dataChecksumThrows(): array
36+
{
37+
return [
38+
'wrong algorithm' => ['?'],
39+
];
40+
}
41+
1842
#[DataProvider('dataZlib')]
1943
public function testEncodesZlib(string $decoded, string $encoded, int $encoding, int $level): void
2044
{

tests/ReadmeTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public static function getPathToMarkdownFile(): string
1818
public static function getExpectedOutputsOfPhpExamples(): iterable
1919
{
2020
return [
21-
'coders' => 'Data was coded into `a8vMFCssyD2Rs5BB0Evt6tJv10J_b2Aoui0tcXT69aaPP9oIyAMA` successfully.',
21+
'coders' => 'Data was coded into `a8vMFCssyD2Rs5BB0Evt6tJv10J_b2Aoui0tcXT69aaPP9oIyB-fLeAHAA` successfully.',
2222
];
2323
}
2424
}

0 commit comments

Comments
 (0)