Skip to content

Commit

Permalink
refactor: refactored coders
Browse files Browse the repository at this point in the history
  • Loading branch information
petrknap committed Apr 1, 2024
1 parent d835c87 commit 35d659b
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 122 deletions.
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@
"name": "petrknap/binary",
"require": {
"php": ">=8.1",
"petrknap/shorts": "^1.4",
"petrknap/singleton": "^2.0"
"petrknap/shorts": "^1.4"
},
"require-dev": {
"ext-mbstring": "*",
Expand Down
2 changes: 1 addition & 1 deletion src/Binary.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private static function getSerializer(): Serializer\SerializerInterface
{
static $serializer;
return $serializer ??= new Serializer\Zlib(
Coder\Zlib::getInstance(),
new Coder\Zlib(),
new Serializer\Php(),
);
}
Expand Down
50 changes: 22 additions & 28 deletions src/Coder/Base64.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,45 @@

namespace PetrKnap\Binary\Coder;

use PetrKnap\Singleton\SingletonInterface;
use PetrKnap\Singleton\SingletonTrait;
use Throwable;

/**
* @see base64_encode()
* @see base64_decode()
*/
final class Base64 implements CoderInterface, SingletonInterface
final class Base64 extends Coder
{
use SingletonTrait;

public const URL_SAFE = false;
private const URL_REPLACE_TABLE = [
['+', '/', '='],
['-', '_', ''],
];

private bool $urlSafe;


public function encode(string $decoded, ?bool $urlSafe = null): string
{
$urlSafe ??= self::URL_SAFE;
try {
$encoded = base64_encode($decoded);
if ($urlSafe) {
$encoded = str_replace(self::URL_REPLACE_TABLE[0], self::URL_REPLACE_TABLE[1], $encoded);
}
return $encoded;
} catch (Throwable $reason) {
throw new Exception\CouldNotEncodeData(__METHOD__, $decoded, $reason);
$this->urlSafe = $urlSafe ?? self::URL_SAFE;
return parent::encode($decoded);
}

protected function doEncode(string $decoded): string
{
$encoded = base64_encode($decoded);
if ($this->urlSafe) {
$encoded = str_replace(self::URL_REPLACE_TABLE[0], self::URL_REPLACE_TABLE[1], $encoded);
}
return $encoded;
}

public function decode(string $encoded): string
protected function doDecode(string $encoded): string
{
try {
$decoded = base64_decode(
str_replace(self::URL_REPLACE_TABLE[1], self::URL_REPLACE_TABLE[0], $encoded),
strict: true,
);
if ($decoded === false) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded);
}
return $decoded;
} catch (Throwable $reason) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded, $reason);
$decoded = base64_decode(
str_replace(self::URL_REPLACE_TABLE[1], self::URL_REPLACE_TABLE[0], $encoded),
strict: true,
);
if ($decoded === false) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded);
}
return $decoded;
}
}
61 changes: 27 additions & 34 deletions src/Coder/Checksum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,57 @@

namespace PetrKnap\Binary\Coder;

use PetrKnap\Binary\HasRequirementsTrait;
use PetrKnap\Shorts\Exception\MissingRequirement;
use PetrKnap\Shorts\HasRequirements;
use PetrKnap\Singleton\SingletonInterface;
use PetrKnap\Singleton\SingletonTrait;
use Throwable;

/**
* @see hash()
* @link https://en.wikipedia.org/wiki/Checksum
*/
final class Checksum implements CoderInterface, HasRequirements, SingletonInterface
final class Checksum extends Coder implements HasRequirements
{
use SingletonTrait;
use HasRequirementsTrait;

public const ALGORITHM = 'crc32';
private const REQUIRED_FUNCTIONS = [
'mb_strlen',
'mb_strcut',
];

protected function __construct()
private string $algorithm;

public function __construct()
{
self::checkRequirements();
}

public function encode(string $decoded, ?string $algorithm = null): string
{
$algorithm ??= self::ALGORITHM;
try {
$checksum = hash($algorithm, $decoded, binary: true);
return $decoded . $checksum;
} catch (Throwable $reason) {
throw new Exception\CouldNotEncodeData(__METHOD__, $decoded, $reason);
}
$this->algorithm = $algorithm ?? self::ALGORITHM;
return parent::encode($decoded);
}

public function decode(string $encoded, ?string $algorithm = null): string
{
$algorithm ??= self::ALGORITHM;
try {
$checksumLength = mb_strlen(hash($algorithm, '', binary: true), encoding: '8bit');
$dataLength = mb_strlen($encoded, encoding: '8bit') - $checksumLength;
$decoded = mb_strcut($encoded, 0, $dataLength, encoding: '8bit');
if ($this->encode($decoded, algorithm: $algorithm) !== $encoded) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded);
}
return $decoded;
} catch (Throwable $reason) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded, $reason);
}
$this->algorithm = $algorithm ?? self::ALGORITHM;
return parent::decode($encoded);
}

protected function doEncode(string $decoded): string
{
$checksum = hash($this->algorithm, $decoded, binary: true);
return $decoded . $checksum;
}

public static function checkRequirements(): void
protected function doDecode(string $encoded): string
{
$functions = [
'mb_strlen',
'mb_strcut',
];
foreach ($functions as $function) {
if (!function_exists($function)) {
throw new MissingRequirement(self::class, 'function', $function);
}
$checksumLength = mb_strlen(hash($this->algorithm, '', binary: true), encoding: '8bit');
$dataLength = mb_strlen($encoded, encoding: '8bit') - $checksumLength;
$decoded = mb_strcut($encoded, 0, $dataLength, encoding: '8bit');
if ($this->doEncode($decoded) !== $encoded) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded);
}
return $decoded;
}
}
45 changes: 45 additions & 0 deletions src/Coder/Coder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace PetrKnap\Binary\Coder;

use Throwable;

abstract class Coder implements CoderInterface
{
public function encode(string $decoded): string
{
try {
return $this->doEncode($decoded);
} catch (Throwable $reason) {
if ($reason instanceof Exception\CouldNotEncodeData) {
throw $reason;
}
throw new Exception\CouldNotEncodeData(__METHOD__, $decoded, $reason);
}
}

public function decode(string $encoded): string
{
try {
return $this->doDecode($encoded);
} catch (Throwable $reason) {
if ($reason instanceof Exception\CouldNotDecodeData) {
throw $reason;
}
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded, $reason);
}
}

/**
* @throws Throwable
*/

abstract protected function doEncode(string $decoded): string;

/**
* @throws Throwable
*/
abstract protected function doDecode(string $encoded): string;
}
66 changes: 28 additions & 38 deletions src/Coder/Zlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,58 @@

namespace PetrKnap\Binary\Coder;

use PetrKnap\Binary\HasRequirementsTrait;
use PetrKnap\Shorts\Exception\MissingRequirement;
use PetrKnap\Shorts\HasRequirements;
use PetrKnap\Singleton\SingletonInterface;
use PetrKnap\Singleton\SingletonTrait;
use Throwable;

/**
* @see zlib_encode()
* @see zlib_decode()
*/
/* final */ class Zlib implements CoderInterface, HasRequirements, SingletonInterface
final class Zlib extends Coder implements HasRequirements
{
use SingletonTrait;
use HasRequirementsTrait;

public const ENCODING = ZLIB_ENCODING_RAW;
public const LEVEL = -1;
public const MAX_LENGTH = 0;
private const REQUIRED_FUNCTIONS = [
'zlib_encode',
'zlib_decode',
];

protected function __construct()
{
self::checkRequirements();
}
private int $encoding;
private int $level;
private int $maxLength;

public function encode(string $decoded, ?int $encoding = null, ?int $level = null): string
{
$encoding ??= self::ENCODING;
$level ??= self::LEVEL;
try {
$encoded = zlib_encode($decoded, $encoding, $level);
if ($encoded === false) {
throw new Exception\CouldNotEncodeData(__METHOD__, $decoded);
}
return $encoded;
} catch (Throwable $reason) {
throw new Exception\CouldNotEncodeData(__METHOD__, $decoded, $reason);
}
$this->encoding = $encoding ?? self::ENCODING;
$this->level = $level ?? self::LEVEL;
return parent::encode($decoded);
}

public function decode(string $encoded, ?int $maxLength = null): string
{
$maxLength ??= self::MAX_LENGTH;
try {
$decoded = zlib_decode($encoded, $maxLength);
if ($decoded === false) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded);
}
return $decoded;
} catch (Throwable $reason) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded, $reason);
$this->maxLength = $maxLength ?? self::MAX_LENGTH;
return parent::decode($encoded);
}

protected function doEncode(string $decoded): string
{
$encoded = zlib_encode($decoded, $this->encoding, $this->level);
if ($encoded === false) {
throw new Exception\CouldNotEncodeData(__METHOD__, $decoded);
}
return $encoded;
}

public static function checkRequirements(): void
protected function doDecode(string $encoded): string
{
$functions = [
'zlib_encode',
'zlib_decode',
];
foreach ($functions as $function) {
if (!function_exists($function)) {
throw new MissingRequirement(self::class, 'function', $function);
}
$decoded = zlib_decode($encoded, $this->maxLength);
if ($decoded === false) {
throw new Exception\CouldNotDecodeData(__METHOD__, $encoded);
}
return $decoded;
}
}
6 changes: 3 additions & 3 deletions src/Decoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ class Decoder extends Coder implements DecoderInterface
{
public function base64(): static
{
return static::create($this, Coder\Base64::getInstance()->decode(
return static::create($this, (new Coder\Base64())->decode(
$this->data,
));
}

public function checksum(?string $algorithm = null): static
{
return static::create($this, Coder\Checksum::getInstance()->decode(
return static::create($this, (new Coder\Checksum())->decode(
$this->data,
algorithm: $algorithm,
));
}

public function zlib(?int $maxLength = null): static
{
return static::create($this, Coder\Zlib::getInstance()->decode(
return static::create($this, (new Coder\Zlib())->decode(
$this->data,
maxLength: $maxLength,
));
Expand Down
6 changes: 3 additions & 3 deletions src/Encoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ class Encoder extends Coder implements EncoderInterface
{
public function base64(?bool $urlSafe = null): static
{
return static::create($this, Coder\Base64::getInstance()->encode(
return static::create($this, (new Coder\Base64())->encode(
$this->data,
urlSafe: $urlSafe,
));
}

public function checksum(?string $algorithm = null): static
{
return static::create($this, Coder\Checksum::getInstance()->encode(
return static::create($this, (new Coder\Checksum())->encode(
$this->data,
algorithm: $algorithm,
));
}

public function zlib(?int $encoding = null, ?int $level = null): static
{
return static::create($this, Coder\Zlib::getInstance()->encode(
return static::create($this, (new Coder\Zlib())->encode(
$this->data,
encoding: $encoding,
level: $level,
Expand Down
Loading

0 comments on commit 35d659b

Please sign in to comment.