Skip to content

Commit

Permalink
Feat: Changed packaged api.
Browse files Browse the repository at this point in the history
  • Loading branch information
kondi3 committed Jan 22, 2025
1 parent c302172 commit 2892833
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 105 deletions.
18 changes: 5 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,12 @@ composer require codelabmw/testament
```php
<?php

$testament = new \Codelabmw\Testament\Testament::default();
use Codelabmw\Testament\Testament;

$code = $testament->generate(
type: \Codelabmw\Testament\Enums\CodeType::NUMERIC /* code type - numeric | alpa | alphanumeric | password */,
length: 8 /* number of characters */
);

$codeFromUser = getUserCode();
$codeFromStorage = getStorageCode();

$verified = $testament->verify(
expected: $codeFromUser /* ... */,
actual: $codeFromStorage /* ... */,
);
$alpha = Testament::alpha(length: 8);
$numeric = Testament::numeric(length: 6);
$alphaNumeric = Testament::alphaNumeric(/* Length defaults to 6 */);
$password = Testament::password(/* Length defaults to 8 */);
```

## Testing
Expand Down
16 changes: 16 additions & 0 deletions src/Contracts/Generator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Codelabmw\Testament\Contracts;

use Codelabmw\Testament\Enums\CodeType;

/** @internal */
interface Generator
{
/**
* Generate a code of the given type and length.
*/
public function generate(CodeType $codeType, int $length): string;
}
23 changes: 0 additions & 23 deletions src/Contracts/Testament.php

This file was deleted.

1 change: 1 addition & 0 deletions src/Enums/CodeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Codelabmw\Testament\Enums;

/** @internal */
enum CodeType
{
case NUMERIC;
Expand Down
75 changes: 75 additions & 0 deletions src/Generators/DefaultGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Codelabmw\Testament\Generators;

use Codelabmw\Testament\Contracts\Generator;
use Codelabmw\Testament\Enums\CodeType;

/** @internal */
final class DefaultGenerator implements Generator
{
/**
* Generate a code of the given type and length.
*/
public function generate(CodeType $codeType, int $length): string
{
$code = '';

$generator = function () use ($codeType, $length) {
for ($i = 0; $i < $length; $i++) {
yield match ($codeType) {
CodeType::NUMERIC => random_int(0, 9),
CodeType::ALPHA => chr(random_int(65, 90)),
CodeType::ALPHANUMERIC => $this->getRandomAlphanumericCharAscii(),
CodeType::PASSWORD => $this->getRandomStrongPasswordChar(),
};
}
};

foreach ($generator() as $char) {
$code .= $char;
}

return $code;
}

/**
* Get a charecter thats either a letter or number.
*/
private function getRandomAlphanumericCharAscii(): string
{
$asciiMin = 48;
$asciiMax = 122;

$randomAscii = random_int($asciiMin, $asciiMax);

while (($randomAscii > 57 && $randomAscii < 65) || ($randomAscii > 90 && $randomAscii < 97)) {
$randomAscii = random_int($asciiMin, $asciiMax);
}

return chr($randomAscii);
}

/**
* Get a charecter thats either a letter, number or special charecter.
*/
private function getRandomStrongPasswordChar(): string
{
$lowerCaseRange = range(97, 122); // a-z
$upperCaseRange = range(65, 90); // A-Z
$numberRange = range(48, 57); // 0-9
$specialCharRange = range(33, 47) // !"#$%&'()*+,-./
+ range(58, 64) // :;<=>?@
+ range(91, 96); // [\]^_`

$charTypeRanges = [$lowerCaseRange, $upperCaseRange, $numberRange, $specialCharRange];
$randomRangeIndex = random_int(0, count($charTypeRanges) - 1);
$selectedRange = $charTypeRanges[$randomRangeIndex];

$randomAscii = $selectedRange[random_int(0, count($selectedRange) - 1)];

return chr($randomAscii);
}
}
68 changes: 41 additions & 27 deletions src/Testament.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,63 @@

namespace Codelabmw\Testament;

use Codelabmw\Testament\Contracts\Testament as ITestament;
use Codelabmw\Testament\Contracts\Generator;
use Codelabmw\Testament\Enums\CodeType;
use Codelabmw\Testament\Generators\DefaultGenerator;

class Testament implements ITestament
final class Testament
{
/**
* Create a new instance of Testament with default settings.
* Keeps track of current Testament instance.
*/
public static function default(): self
private static ?self $instance = null;

/**
* Creates a private instance of Testament.
*/
private function __construct(private readonly Generator $generator) {}

/**
* A private Testament factory using DefaultGenerator.
*/
private static function make(): static
{
return new self;
if (! self::$instance instanceof \Codelabmw\Testament\Testament) {
self::$instance = new self(new DefaultGenerator);
}

return self::$instance;
}

/**
* Generates a new cryptographically secure code.
* Generate an alphabetical code of the given length (Defaults to 6).
*/
public function generate(CodeType $type, int $length): string
public static function alpha(int $length = 6): string
{
$code = '';

$generator = function () use ($type, $length) {
for ($i = 0; $i < $length; $i++) {
yield match ($type) {
CodeType::NUMERIC => random_int(0, 9),
CodeType::ALPHA => chr(random_int(65, 90)),
CodeType::ALPHANUMERIC => chr(random_int(65, 90)).chr(random_int(48, 57)),
CodeType::PASSWORD => chr(random_int(65, 90)).chr(random_int(97, 122)).chr(random_int(48, 57)),
};
}
};

foreach ($generator() as $char) {
$code .= $char;
}
return self::make()->generator->generate(CodeType::ALPHA, $length);
}

return $code;
/**
* Generate a numeric code of the given length (Defaults to 6).
*/
public static function numeric(int $length = 6): string
{
return self::make()->generator->generate(CodeType::NUMERIC, $length);
}

/**
* Generate an alpha-numeric code of the given length (Defaults to 6).
*/
public static function alphaNumeric(int $length = 6): string
{
return self::make()->generator->generate(CodeType::ALPHANUMERIC, $length);
}

/**
* Verifies that two given codes are equal.
* Generate a password code of the given length (Defaults to 8).
*/
public function verify(string $expected, string $actual): bool
public static function password(int $length = 8): string
{
return $expected === $actual;
return self::make()->generator->generate(CodeType::PASSWORD, $length);
}
}
12 changes: 11 additions & 1 deletion tests/Architecture/ArchTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
<?php

use Codelabmw\Testament\Contracts\Generator;

arch()->preset()->php();
arch()->preset()->security();
arch()->preset()->strict();

arch('generators implements generator contract')
->expect('Codelabmw\Testament\Generators')
->toImplement(Generator::class);

arch('it will not use debugging functions')
->expect(['dd', 'dump', 'ray'])
->expect(['dd', 'dump', 'ray', 'var_dump'])
->each->not->toBeUsed();
60 changes: 19 additions & 41 deletions tests/Unit/TestamentTest.php
Original file line number Diff line number Diff line change
@@ -1,61 +1,39 @@
<?php

use Codelabmw\Testament\Enums\CodeType;
use Codelabmw\Testament\Testament;

it('can creare a testament instance with default static method', function (): void {
$testament = Testament::default();

expect($testament)->toBeInstanceOf(Testament::class);
});

it('can generate a code with correct length', function (): void {
$testament = Testament::default();
$code = $testament->generate(type: CodeType::NUMERIC, length: 6);
it('can generate an alpha code', function (): void {
// Act
$code = Testament::alpha();

// Assert
expect($code)->toHaveLength(6);
expect($code)->toMatch('/^[a-zA-Z]+$/');
});

it('can generate a numeric code', function (): void {
$testament = Testament::default();
$code = $testament->generate(type: CodeType::NUMERIC, length: 6);
// Act
$code = Testament::numeric();

// Assert
expect($code)->toHaveLength(6);
expect($code)->toMatch('/^\d+$/');
});

it('can generate an alphabetical code', function (): void {
$testament = Testament::default();
$code = $testament->generate(type: CodeType::ALPHA, length: 6);

expect($code)->toMatch('/^[a-zA-Z]+$/');
});

it('can generate an alphanumeric code', function (): void {
$testament = Testament::default();
$code = $testament->generate(type: CodeType::ALPHANUMERIC, length: 6);
it('can generate an alpha-numeric code', function (): void {
// Act
$code = Testament::alphaNumeric(20);

// Assert
expect($code)->toHaveLength(20);
expect($code)->toMatch('/^[a-zA-Z0-9]+$/');
});

it('can generate a password code', function (): void {
$testament = Testament::default();
$code = $testament->generate(type: CodeType::PASSWORD, length: 6);

expect($code)->toMatch('/^[a-zA-Z0-9!@#$%^&*()]+$/');
});

it('can verify that two codes are equal', function (): void {
$testament = Testament::default();
$expected = '123456';
$actual = '123456';

expect($testament->verify($expected, $actual))->toBeTrue();
});

it('can verify that two codes are not equal', function (): void {
$testament = Testament::default();
$expected = '123456';
$actual = '654321';
// Act
$code = Testament::password();

expect($testament->verify($expected, $actual))->toBeFalse();
// Assert
expect($code)->toHaveLength(8);
expect($code)->toMatch('/^.*$/');
});

0 comments on commit 2892833

Please sign in to comment.