Skip to content

Commit

Permalink
refactor: Adds improved padding logic in getValue to ensure a fixed l…
Browse files Browse the repository at this point in the history
…ength of 27 characters. (#12)
  • Loading branch information
gustavofreze authored Oct 4, 2024
1 parent 8a29e21 commit 462fb25
Show file tree
Hide file tree
Showing 15 changed files with 187 additions and 121 deletions.
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/tests export-ignore
/vendor export-ignore

/LICENSE export-ignore
/Makefile export-ignore
/README.md export-ignore
/phpmd.xml export-ignore
/phpunit.xml export-ignore
/phpstan.neon.dist export-ignore
/infection.json.dist export-ignore

/.github export-ignore
/.gitignore export-ignore
/.gitattributes export-ignore
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

- name: Use PHP 8.2
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'

- name: Install dependencies
run: composer update --no-progress --optimize-autoloader

Expand All @@ -33,6 +38,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v3

- name: Use PHP 8.2
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'

- name: Install dependencies
run: composer update --no-progress --optimize-autoloader

Expand Down
11 changes: 7 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
DOCKER_RUN = docker run --rm -it --net=host -v ${PWD}:/app -w /app gustavofreze/php:8.2

.PHONY: configure test test-no-coverage review show-reports clean
.PHONY: configure test test-file test-no-coverage review show-reports clean

configure:
@${DOCKER_RUN} composer update --optimize-autoloader

test: review
test:
@${DOCKER_RUN} composer tests

test-no-coverage: review
test-file:
@${DOCKER_RUN} composer tests-file-no-coverage ${FILE}

test-no-coverage:
@${DOCKER_RUN} composer tests-no-coverage

review:
Expand All @@ -19,4 +22,4 @@ show-reports:

clean:
@sudo chown -R ${USER}:${USER} ${PWD}
@rm -rf report vendor
@rm -rf report vendor .phpunit.cache
23 changes: 14 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
"minimum-stability": "stable",
"keywords": [
"psr",
"psr-4",
"ksuid",
"psr-12",
"base62",
"unique",
"identifier",
Expand All @@ -22,6 +20,10 @@
"homepage": "https://github.com/gustavofreze"
}
],
"support": {
"issues": "https://github.com/tiny-blocks/ksuid/issues",
"source": "https://github.com/tiny-blocks/ksuid"
},
"config": {
"sort-packages": true,
"allow-plugins": {
Expand All @@ -39,28 +41,31 @@
}
},
"require": {
"php": "^8.1||^8.2",
"tiny-blocks/encoder": "^1.2"
"php": "^8.2",
"tiny-blocks/encoder": "^2"
},
"require-dev": {
"infection/infection": "^0.26",
"phpmd/phpmd": "^2.13",
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.7"
"phpmd/phpmd": "^2.15",
"phpunit/phpunit": "^11",
"phpstan/phpstan": "^1",
"infection/infection": "^0.29",
"squizlabs/php_codesniffer": "^3.10"
},
"suggest": {
"ext-gmp": "Enables faster math with arbitrary-precision integers using GMP."
},
"scripts": {
"phpcs": "phpcs --standard=PSR12 --extensions=php ./src",
"phpmd": "phpmd ./src text phpmd.xml --suffixes php --ignore-violations-on-exit",
"phpstan": "phpstan analyse -c phpstan.neon.dist --quiet --no-progress",
"test": "phpunit --log-junit=report/coverage/junit.xml --coverage-xml=report/coverage/coverage-xml --coverage-html=report/coverage/coverage-html tests",
"test-mutation": "infection --only-covered --logger-html=report/coverage/mutation-report.html --coverage=report/coverage --min-msi=100 --min-covered-msi=100 --threads=4",
"test-no-coverage": "phpunit --no-coverage",
"test-mutation-no-coverage": "infection --only-covered --min-msi=100 --threads=4",
"review": [
"@phpcs",
"@phpmd"
"@phpmd",
"@phpstan"
],
"tests": [
"@test",
Expand Down
18 changes: 5 additions & 13 deletions infection.json.dist
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
{
"timeout": 10,
"testFramework": "phpunit",
"tmpDir": "report/",
"tmpDir": "report/infection/",
"source": {
"directories": [
"src"
]
},
"logs": {
"text": "report/logs/infection-text.log",
"summary": "report/logs/infection-summary.log"
"text": "report/infection/logs/infection-text.log",
"summary": "report/infection/logs/infection-summary.log"
},
"mutators": {
"@default": true,
"Plus": false,
"Minus": false,
"Concat": false,
"CastArray": false,
"GreaterThan": false,
"UnwrapSubstr": false,
"UnwrapStrRepeat": false,
"IncrementInteger": false,
"DecrementInteger": false,
"PublicVisibility": false
"CastArray": false
},
"phpUnit": {
"configDir": "",
"customPath": "./vendor/bin/phpunit"
}
}
}
8 changes: 8 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
parameters:
paths:
- src
level: 9
tmpDir: report/phpstan
ignoreErrors:
- '#return type has no value type specified in iterable type array#'
reportUnmatchedIgnoredErrors: false
38 changes: 24 additions & 14 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/autoload.php"
cacheResultFile="report/.phpunit.result.cache"
backupGlobals="false"
backupStaticAttributes="false"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
cacheDirectory=".phpunit.cache"
beStrictAboutOutputDuringTests="true">

<source>
<include>
<directory>src</directory>
</include>
</source>

<testsuites>
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
<directory>tests</directory>
</testsuite>
</testsuites>

<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
<report>
<text outputFile="report/coverage.txt"/>
<html outputDirectory="report/html/"/>
<clover outputFile="report/coverage-clover.xml"/>
</report>
</coverage>

<logging>
<junit outputFile="report/execution-result.xml"/>
</logging>

</phpunit>
2 changes: 2 additions & 0 deletions src/Internal/Exceptions/InvalidKsuidForInspection.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace TinyBlocks\Ksuid\Internal\Exceptions;

use RuntimeException;
Expand Down
2 changes: 2 additions & 0 deletions src/Internal/Exceptions/InvalidPayloadSize.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace TinyBlocks\Ksuid\Internal\Exceptions;

use RuntimeException;
Expand Down
9 changes: 6 additions & 3 deletions src/Internal/Payload.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<?php

declare(strict_types=1);

namespace TinyBlocks\Ksuid\Internal;

use TinyBlocks\Encoder\Base62;
use TinyBlocks\Ksuid\Internal\Exceptions\InvalidPayloadSize;

final class Payload
final readonly class Payload
{
public const PAYLOAD_BYTES = 16;

private function __construct(private readonly string $value)
private function __construct(private string $value)
{
$currentSize = strlen($value);

Expand All @@ -30,7 +32,8 @@ public static function from(string $value): Payload

public static function fromBytes(string $value): Payload
{
$bytes = Base62::decode(value: $value);
$decoder = Base62::from(value: $value);
$bytes = $decoder->decode();
$payload = substr($bytes, -self::PAYLOAD_BYTES);

return new Payload(value: $payload);
Expand Down
9 changes: 6 additions & 3 deletions src/Internal/Timestamp.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<?php

declare(strict_types=1);

namespace TinyBlocks\Ksuid\Internal;

use DateTime;
use DateTimeZone;
use TinyBlocks\Encoder\Base62;

final class Timestamp
final readonly class Timestamp
{
public const EPOCH = 1400000000;

private function __construct(private readonly int $value)
private function __construct(private int $value)
{
}

Expand All @@ -21,7 +23,8 @@ public static function from(int $value): Timestamp

public static function fromBytes(string $value): Timestamp
{
$bytes = Base62::decode(value: $value);
$decoder = Base62::from(value: $value);
$bytes = $decoder->decode();
$timestamp = substr($bytes, 0, -16);
$timestamp = substr($timestamp, -4);
$timestamp = (array)unpack('Nuint', $timestamp);
Expand Down
18 changes: 8 additions & 10 deletions src/Ksuid.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
<?php

declare(strict_types=1);

namespace TinyBlocks\Ksuid;

use TinyBlocks\Encoder\Base62;
use TinyBlocks\Ksuid\Internal\Exceptions\InvalidKsuidForInspection;
use TinyBlocks\Ksuid\Internal\Payload;
use TinyBlocks\Ksuid\Internal\Timestamp;

final class Ksuid
final readonly class Ksuid
{
public const ENCODED_SIZE = 27;

private function __construct(private readonly Payload $payload, private readonly Timestamp $timestamp)
private function __construct(private Payload $payload, private Timestamp $timestamp)
{
}

Expand Down Expand Up @@ -52,19 +54,15 @@ public static function inspectFrom(string $ksuid): array

public function getValue(): string
{
$encoded = Base62::encode(value: $this->getBytes());
$padding = self::ENCODED_SIZE - strlen($encoded);

if ($padding > 0) {
$encoded = str_repeat('0', $padding) . $encoded;
}
$encoder = Base62::from(value: $this->getBytes());
$encoded = $encoder->encode();

return $encoded;
return str_pad($encoded, self::ENCODED_SIZE, '0', STR_PAD_LEFT);
}

public function getBytes(): string
{
return pack('N', $this->timestamp->getValue()) . $this->payload->getValue();
return sprintf('%s%s', pack('N', $this->timestamp->getValue()), $this->payload->getValue());
}

public function getPayload(): string
Expand Down
23 changes: 12 additions & 11 deletions tests/Internal/PayloadTest.php
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
<?php

declare(strict_types=1);

namespace TinyBlocks\Ksuid\Internal;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use TinyBlocks\Ksuid\Internal\Exceptions\InvalidPayloadSize;

class PayloadTest extends TestCase
final class PayloadTest extends TestCase
{
/**
* @dataProvider providerForTestExceptionWhenInvalidPayloadSize
*/
#[DataProvider('providerForTestExceptionWhenInvalidPayloadSize')]
public function testExceptionWhenInvalidPayloadSize(string $invalidData, int $currentSize): void
{
/** @Given a invalid data */
/** @Given an invalid payload data of size other than expected */
$data = $invalidData;
$template = 'Current length is <%s> bytes. Payload size must be exactly <%s> bytes.';

/** @Then an exception indicating that the payload size is invalid should occur */
/** @Then an InvalidPayloadSize exception should be thrown with the appropriate message */
$template = 'Current length is <%s> bytes. Payload size must be exactly <%s> bytes.';
$this->expectException(InvalidPayloadSize::class);
$this->expectExceptionMessage(sprintf($template, $currentSize, Payload::PAYLOAD_BYTES));

/** @When requesting the creation of the payload with invalid data */
/** @When attempting to create a Payload instance with invalid data size */
Payload::from(value: $data);
}

public function providerForTestExceptionWhenInvalidPayloadSize(): array
public static function providerForTestExceptionWhenInvalidPayloadSize(): array
{
return [
[
'Empty Payload' => [
'invalidData' => '',
'currentSize' => 0
],
[
'Short Payload' => [
'invalidData' => 'ABC',
'currentSize' => 3
]
Expand Down
Loading

0 comments on commit 462fb25

Please sign in to comment.