Skip to content

Commit f127e9a

Browse files
authored
Merge pull request #2 from petrknap/xz-wrapper
Implemented xz compression and decompression
2 parents 3c7d1ee + e16c7c1 commit f127e9a

12 files changed

+261
-2
lines changed

.github/workflows/docker.yaml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: composer ci-script
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
run:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v3
11+
- run: docker build -t $GITHUB_REPOSITORY:$GITHUB_SHA .
12+
- name: Run composer ci-script
13+
run: docker run $GITHUB_REPOSITORY:$GITHUB_SHA composer ci-script

.molireali

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ dockerfile php 8.1-cli
44
docker-scripts petrknap/php-xz-utils
55
donation
66
github-templates
7+
github-workflow docker "composer ci-script"
78
github-workflow linter-docker
89
github-workflow linter-php 8.1
910
license LGPL-3.0-or-later

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# XZ Utils (wrapper)
22

33
User-friendly wrapper for XZ Utils, providing simplified and streamlined access to powerful data compression and decompression tools.
4+
For ease of use, you can use the approach known from the [compression extensions](https://www.php.net/manual/en/refs.compression.php):
5+
```php
6+
$encoded = xzencode(b'data') or throw new Exception();
7+
$decoded = xzdecode($encoded) or throw new Exception();
8+
```
49

510
---
611

composer.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
"autoload": {
44
"psr-4": {
55
"PetrKnap\\XzUtils\\": "src"
6-
}
6+
},
7+
"files": [
8+
"src/ext-xz.php"
9+
]
710
},
811
"autoload-dev": {
912
"psr-4": {
@@ -32,7 +35,9 @@
3235
"license": "LGPL-3.0-or-later",
3336
"name": "petrknap/xz-utils",
3437
"require": {
35-
"php": ">=8.1"
38+
"php": ">=8.1",
39+
"petrknap/shorts": "^3.0",
40+
"symfony/process": "^6.4"
3641
},
3742
"require-dev": {
3843
"nunomaduro/phpinsights": "^2.11",

src/Exception/Exception.php

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils\Exception;
6+
7+
use Throwable;
8+
9+
/**
10+
* @internal root exception
11+
*/
12+
interface Exception extends Throwable
13+
{
14+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils\Exception;
6+
7+
use PetrKnap\Shorts\Exception\CouldNotProcessData;
8+
9+
/**
10+
* @extends CouldNotProcessData<string>
11+
*/
12+
final class XzCouldNotCompressData extends CouldNotProcessData implements XzException
13+
{
14+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils\Exception;
6+
7+
use PetrKnap\Shorts\Exception\CouldNotProcessData;
8+
9+
/**
10+
* @extends CouldNotProcessData<string>
11+
*/
12+
final class XzCouldNotDecompressData extends CouldNotProcessData implements XzException
13+
{
14+
}

src/Exception/XzException.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils\Exception;
6+
7+
interface XzException extends Exception
8+
{
9+
}

src/Xz.php

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils;
6+
7+
use Symfony\Component\Process\Exception\ProcessFailedException;
8+
use Symfony\Component\Process\Process;
9+
use Throwable;
10+
11+
final class Xz
12+
{
13+
/**
14+
* @param array<non-empty-string> $options
15+
*
16+
* @throws Exception\XzCouldNotCompressData
17+
*/
18+
public function compress(
19+
string $data,
20+
int|null $compressionPreset = null,
21+
array $options = [],
22+
): string {
23+
$command = [
24+
'xz',
25+
'--compress',
26+
];
27+
if ($compressionPreset !== null) {
28+
$command[] = '-' . $compressionPreset;
29+
}
30+
try {
31+
return self::run(array_merge($command, $options), $data);
32+
} catch (Throwable $reason) {
33+
throw new Exception\XzCouldNotCompressData(__METHOD__, $data, $reason);
34+
}
35+
}
36+
37+
/**
38+
* @param array<non-empty-string> $options
39+
*
40+
* @throws Exception\XzCouldNotDecompressData
41+
*/
42+
public function decompress(
43+
string $data,
44+
array $options = [],
45+
): string {
46+
$command = [
47+
'xz',
48+
'--decompress',
49+
];
50+
try {
51+
return self::run(array_merge($command, $options), $data);
52+
} catch (Throwable $reason) {
53+
throw new Exception\XzCouldNotDecompressData(__METHOD__, $data, $reason);
54+
}
55+
}
56+
57+
/**
58+
* @param non-empty-array<non-empty-string> $command
59+
*
60+
* @throws ProcessFailedException
61+
*
62+
* @todo move it to another package
63+
*/
64+
private static function run(array $command, string $input): string
65+
{
66+
$process = new Process($command);
67+
$process->setInput($input);
68+
$process->run();
69+
if (!$process->isSuccessful()) {
70+
throw new ProcessFailedException($process);
71+
}
72+
return $process->getOutput();
73+
}
74+
}

src/ext-xz.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
if (!function_exists('xzencode')) {
6+
/**
7+
* @see gzencode()
8+
* @see PetrKnap\XzUtils\Xz::compress()
9+
*/
10+
function xzencode(string $data, int|null $compressionPreset = null): string|false
11+
{
12+
try {
13+
return (new PetrKnap\XzUtils\Xz())->compress($data, $compressionPreset);
14+
} catch (Throwable) {
15+
return false;
16+
}
17+
}
18+
}
19+
20+
if (!function_exists('xzdecode')) {
21+
/**
22+
* @see gzdecode()
23+
* @see PetrKnap\XzUtils\Xz::decompress()
24+
*/
25+
function xzdecode(string $data): string|false
26+
{
27+
try {
28+
return (new PetrKnap\XzUtils\Xz())->decompress($data);
29+
} catch (Throwable) {
30+
return false;
31+
}
32+
}
33+
}

tests/ReadmeTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils;
6+
7+
use PetrKnap\Shorts\PhpUnit\MarkdownFileTestInterface;
8+
use PetrKnap\Shorts\PhpUnit\MarkdownFileTestTrait;
9+
use PHPUnit\Framework\TestCase;
10+
11+
final class ReadmeTest extends TestCase implements MarkdownFileTestInterface
12+
{
13+
use MarkdownFileTestTrait;
14+
15+
public static function getPathToMarkdownFile(): string
16+
{
17+
return __DIR__ . '/../README.md';
18+
}
19+
20+
public static function getExpectedOutputsOfPhpExamples(): iterable
21+
{
22+
return [
23+
'ext-xz' => '',
24+
];
25+
}
26+
}

tests/XzTest.php

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PetrKnap\XzUtils;
6+
7+
use PHPUnit\Framework\Attributes\DataProvider;
8+
use PHPUnit\Framework\TestCase;
9+
10+
final class XzTest extends TestCase
11+
{
12+
#[DataProvider('data')]
13+
public function testCompressesData(string $decompressed, string $compressed, int|null $compressionPreset): void
14+
{
15+
self::assertSame(
16+
bin2hex($compressed),
17+
bin2hex((new Xz())->compress($decompressed, $compressionPreset)),
18+
);
19+
}
20+
21+
#[DataProvider('data')]
22+
public function testDecompressesData(string $decompressed, string $compressed): void
23+
{
24+
self::assertSame(
25+
bin2hex($decompressed),
26+
bin2hex((new Xz())->decompress($compressed)),
27+
);
28+
}
29+
30+
public static function data(): array
31+
{
32+
$decompressed = base64_decode('h/J4V2gAK1njeEOqyTiTjg==');
33+
return [
34+
'default compression preset' => [
35+
$decompressed,
36+
base64_decode('/Td6WFoAAATm1rRGAgAhARYAAAB0L+WjAQAPh/J4V2gAK1njeEOqyTiTjgDL7VTjlSH1vwABKBDlC2xgH7bzfQEAAAAABFla'),
37+
null,
38+
],
39+
'compression preset 0' => [
40+
$decompressed,
41+
base64_decode('/Td6WFoAAATm1rRGAgAhAQwAAACPmEGcAQAPh/J4V2gAK1njeEOqyTiTjgDL7VTjlSH1vwABKBDlC2xgH7bzfQEAAAAABFla'),
42+
0,
43+
],
44+
'compression preset 9' => [
45+
$decompressed,
46+
base64_decode('/Td6WFoAAATm1rRGAgAhARwAAAAQz1jMAQAPh/J4V2gAK1njeEOqyTiTjgDL7VTjlSH1vwABKBDlC2xgH7bzfQEAAAAABFla'),
47+
9,
48+
],
49+
];
50+
}
51+
}

0 commit comments

Comments
 (0)