Skip to content

Commit 1295db4

Browse files
committed
feat: implemented xz coder
1 parent 3e53ee3 commit 1295db4

File tree

9 files changed

+175
-3
lines changed

9 files changed

+175
-3
lines changed

composer.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
"allow-plugins": false,
1515
"sort-packages": true
1616
},
17+
"conflict": {
18+
"symfony/process": "<6|>=8"
19+
},
1720
"description": "Library for work with binary data and objects",
1821
"funding": [
1922
{
@@ -34,7 +37,8 @@
3437
"hexadecimal",
3538
"igbinary",
3639
"serializer",
37-
"zlib"
40+
"zlib",
41+
"xz"
3842
],
3943
"license": "LGPL-3.0-or-later",
4044
"name": "petrknap/binary",
@@ -50,7 +54,8 @@
5054
"nunomaduro/phpinsights": "^2.11",
5155
"phpstan/phpstan": "^1.12",
5256
"phpunit/phpunit": "^10.5",
53-
"squizlabs/php_codesniffer": "^3.7"
57+
"squizlabs/php_codesniffer": "^3.7",
58+
"symfony/process": "*"
5459
},
5560
"scripts": {
5661
"test": "@test-implementation",
@@ -76,6 +81,7 @@
7681
"suggest": {
7782
"ext-igbinary": "Required to serialize data via igbinary",
7883
"ext-mbstring": "Required to bite bytes",
79-
"ext-zlib": "Required to compress data"
84+
"ext-zlib": "Required to compress data",
85+
"symfony/process": "Required to call external executables"
8086
}
8187
}

src/Coder.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ abstract public function checksum(string|null $algorithm = null): static;
4848
*/
4949
abstract public function hex(): static;
5050

51+
/**
52+
* @see Coder\Xz
53+
*
54+
* @throws Coder\Exception\CoderException
55+
*/
56+
abstract public function xz(): static;
57+
5158
/**
5259
* @see Coder\zlib
5360
*

src/Coder/ExternalCoder.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\Binary\Coder;
6+
7+
use PetrKnap\Shorts\HasRequirements;
8+
use Symfony\Component\Process\Exception\ProcessFailedException;
9+
use Symfony\Component\Process\Process;
10+
11+
/**
12+
* @internal shared logic
13+
*/
14+
abstract class ExternalCoder extends Coder
15+
{
16+
use HasRequirements;
17+
18+
/**
19+
* Must be set in {@see self::encode()}
20+
*
21+
* @var array<string>
22+
*/
23+
protected array $encodeCommand;
24+
25+
/**
26+
* Must be set in {@see self::encode()}
27+
*
28+
* @var array<string>
29+
*/
30+
protected array $decodeCommand;
31+
32+
public function __construct()
33+
{
34+
self::checkRequirements(
35+
classes: [
36+
Process::class,
37+
ProcessFailedException::class,
38+
],
39+
);
40+
}
41+
42+
protected function doEncode(string $decoded): string
43+
{
44+
$encoder = new Process($this->encodeCommand);
45+
$encoder->setInput($decoded);
46+
$encoder->run();
47+
if (!$encoder->isSuccessful()) {
48+
throw new ProcessFailedException($encoder);
49+
}
50+
return $encoder->getOutput();
51+
}
52+
53+
protected function doDecode(string $encoded): string
54+
{
55+
$decoder = new Process($this->decodeCommand);
56+
$decoder->setInput($encoded);
57+
$decoder->run();
58+
if (!$decoder->isSuccessful()) {
59+
throw new ProcessFailedException($decoder);
60+
}
61+
return $decoder->getOutput();
62+
}
63+
}

src/Coder/Xz.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\Binary\Coder;
6+
7+
final class Xz extends ExternalCoder
8+
{
9+
public function encode(string $decoded, int|null $level = null): string
10+
{
11+
$this->encodeCommand = ['xz', '--compress', '--stdout'];
12+
if ($level !== null) {
13+
$this->encodeCommand[] = "-{$level}";
14+
}
15+
return parent::encode($decoded);
16+
}
17+
public function decode(string $encoded): string
18+
{
19+
$this->decodeCommand = ['xz', '--decompress', '--stdout'];
20+
return parent::decode($encoded);
21+
}
22+
}

src/Decoder.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ public function hex(): static
2828
));
2929
}
3030

31+
public function xz(): static
32+
{
33+
return $this->withData((new Coder\XZ())->decode(
34+
$this->data,
35+
));
36+
}
37+
3138
public function zlib(int|null $maxLength = null): static
3239
{
3340
return $this->withData((new Coder\Zlib())->decode(

src/Encoder.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ public function hex(): static
2929
));
3030
}
3131

32+
public function xz(int|null $level = null): static
33+
{
34+
return $this->withData((new Coder\XZ())->encode(
35+
$this->data,
36+
level: $level,
37+
));
38+
}
39+
3240
public function zlib(int|null $encoding = null, int|null $level = null): static
3341
{
3442
return $this->withData((new Coder\Zlib())->encode(

tests/Coder/XzTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\Binary\Coder;
6+
7+
use PHPUnit\Framework\Attributes\DataProvider;
8+
9+
final class XzTest extends CoderTestCase
10+
{
11+
public static function data(): array
12+
{
13+
$data = self::getDecodedData();
14+
return [
15+
'default preset' => [$data, base64_decode('/Td6WFoAAATm1rRGAgAhARYAAAB0L+Wj4AA7ABxdAG0OUG7jCUfPj0z08gJbP9KgthIHS/jgLMk44AAAwVvY3vTzsZsAATg8V3V2GB+2830BAAAAAARZWg=='), null],
16+
'preset 0' => [$data, base64_decode('/Td6WFoAAATm1rRGAgAhAQwAAACPmEGc4AA7ABxdAG0OUG7jCUfPj0z08gJbP9KgthIHS/jgLMk44AAAwVvY3vTzsZsAATg8V3V2GB+2830BAAAAAARZWg=='), 0],
17+
'preset 9' => [$data, base64_decode('/Td6WFoAAATm1rRGAgAhARwAAAAQz1jM4AA7ABxdAG0OUG7jCUfPj0z08gJbP9KgthIHS/jgLMk44AAAwVvY3vTzsZsAATg8V3V2GB+2830BAAAAAARZWg=='), 9],
18+
];
19+
}
20+
21+
#[DataProvider('data')]
22+
public function testEncodes(string $decoded, string $encoded, int|null $level): void
23+
{
24+
self::assertBinarySame(
25+
$encoded,
26+
(new Xz())->encode(
27+
$decoded,
28+
level: $level,
29+
),
30+
);
31+
}
32+
33+
#[DataProvider('data')]
34+
public function testDecodes(string $decoded, string $encoded): void
35+
{
36+
self::assertBinarySame(
37+
$decoded,
38+
(new Xz())->decode(
39+
$encoded,
40+
),
41+
);
42+
}
43+
}

tests/DecoderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ public function testDecodesHex(): void
3030
);
3131
}
3232

33+
public function testDecodesXz(): void
34+
{
35+
self::assertBinarySame(
36+
Coder\XzTest::getDecodedData(),
37+
(new Decoder(Coder\XzTest::getEncodedData()))->xz()->data,
38+
);
39+
}
40+
3341
public function testDecodesZlib(): void
3442
{
3543
self::assertBinarySame(

tests/EncoderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ public function testEncodesHex(): void
3030
);
3131
}
3232

33+
public function testEncodesXz(): void
34+
{
35+
self::assertBinarySame(
36+
Coder\XzTest::getEncodedData(),
37+
(new Encoder(Coder\XzTest::getDecodedData()))->xz()->data,
38+
);
39+
}
40+
3341
public function testEncodesZlib(): void
3442
{
3543
self::assertBinarySame(

0 commit comments

Comments
 (0)