Skip to content

Commit

Permalink
added character generator/group abstraction
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Frawley 2nd <rmf@src.run>
  • Loading branch information
robfrawley committed Sep 14, 2018
1 parent 03fdac0 commit 8022208
Show file tree
Hide file tree
Showing 4 changed files with 863 additions and 0 deletions.
172 changes: 172 additions & 0 deletions lib/Characters/AsciiCharacters.php
@@ -0,0 +1,172 @@
<?php

/*
* This file is part of the `src-run/web-app-v1` project.
*
* (c) Rob Frawley 2nd <rmf@src.run>
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace SR\Utilities\Characters;

use SR\Utilities\Characters\Group\CharactersGroup;

class AsciiCharacters implements \Countable, \IteratorAggregate
{
use CharactersTrait;

/**
* @var CharactersGroup[]
*/
private $sets;

public function __construct()
{
$this->bytes = $this->mergedCharactersGroup(
$this->numbers(), $this->letters(), $this->symbols()
)->bytes();
}

/**
* @return CharactersGroup
*/
public function numbers(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [range(48, 57)];
});
}

/**
* @return CharactersGroup
*/
public function letters(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [
$this->lettersLower()->bytes(),
$this->lettersUpper()->bytes(),
];
});
}

/**
* @return CharactersGroup
*/
public function lettersUpper(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [range(65, 90)];
});
}

/**
* @return CharactersGroup
*/
public function lettersLower(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [range(97, 122)];
});
}

/**
* @param bool $readable
*
* @return CharactersGroup
*/
public function symbols(bool $readable = false): CharactersGroup
{
return $readable ? $this->symbolsSel() : $this->symbolsAll();
}

/**
* @return CharactersGroup
*/
public function passwords(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [
$this->numbers()->bytes(),
$this->letters()->bytes(),
$this->symbols(true)->bytes(),
];
});
}

/**
* @return CharactersGroup
*/
private function symbolsAll(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [
range(32, 47),
range(58, 64),
range(91, 96),
range(123, 126),
];
});
}

/**
* @return CharactersGroup
*/
private function symbolsSel(): CharactersGroup
{
return $this->cachedCharactersGroup(__FUNCTION__, function (): array {
return [[
33, // exclamation mark
35, // number sign
36, // dollar sign
37, // percent sign
38, // ampersand
40, // parentheses (opening)
41, // parentheses (closing)
42, // asterisk
43, // plus sign
45, // hyphen-minus
58, // colon
59, // semicolon
61, // equal sign
63, // question mark
64, // at sign
91, // square bracket (opening)
93, // square bracket (closing)
94, // circumflex accent
95, // underscore
123,// curly bracket (opening)
124,// vertical bar
125,// curly bracket (closing)
126 // tilde
]];
});
}

/**
* @param CharactersGroup ...$groups
*
* @return CharactersGroup
*/
private function mergedCharactersGroup(CharactersGroup ...$groups): CharactersGroup
{
return $this->createCharacterGroup(...array_map(function (CharactersGroup $set): array {
return $set->bytes();
}, $groups));
}

/**
* @param string $name
* @param \Closure $provider
*
* @return CharactersGroup
*/
private function cachedCharactersGroup(string $name, \Closure $provider): CharactersGroup
{
return isset($this->sets[$name])
? $this->sets[$name]
: $this->sets[$name] = $this->createCharacterGroup(...$provider());
}
}
178 changes: 178 additions & 0 deletions lib/Characters/CharactersTrait.php
@@ -0,0 +1,178 @@
<?php

/*
* This file is part of the `src-run/web-app-v1` project.
*
* (c) Rob Frawley 2nd <rmf@src.run>
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace SR\Utilities\Characters;

use SR\Utilities\Characters\Group\CharactersGroup;

trait CharactersTrait
{
/**
* @var int[]
*/
private $bytes = [];

/**
* @return string
*/
public function __toString(): string
{
return self::arrayToString($this->chars());
}

/**
* @return int[]
*/
public function bytes(): array
{
return $this->bytes;
}

/**
* @return string[]
*/
public function chars(): array
{
return array_map(function (int $byte): string {
return $this->byteToChar($byte);
}, $this->bytes());
}

/**
* @return int
*/
public function count(): int
{
return count($this->bytes);
}

/**
* @return \ArrayIterator
*/
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator(
array_combine($this->bytes(), $this->chars())
);
}

/**
* @param int $byte
*
* @return bool
*/
public function isValidByte(int $byte): bool
{
return in_array($byte, $this->bytes, true) || ($byte >= 0 && $byte <= 255);
}

/**
* @param string $char
*
* @return bool
*/
public function isValidChar(string $char): bool
{
if (1 !== strlen($char)) {
throw new \InvalidArgumentException(sprintf(
'Provided value "%s" must be a single character (input length of %d provided).', $char, strlen($char)
));
}

return $this->isValidByte($this->charToByte($char));
}

/**
* @param string $char
*
* @return int|null
*/
public function charToByte(string $char): ?int
{
if (1 !== strlen($char)) {
throw new \InvalidArgumentException(sprintf(
'Provided value "%s" must be a single character.', $char
));
}

return $this->isValidByte($byte = ord($char)) ? $byte : null;
}

/**
* @param int $byte
*
* @return string|null
*/
public function byteToChar(int $byte): ?string
{
return $this->isValidByte($byte) ? chr($byte) : null;
}

/**
* @return int
*/
public function randomByte(): int
{
return $this->bytes[random_int(0, count($this->bytes) - 1)];
}

/**
* @return string
*/
public function randomChar(): string
{
return $this->byteToChar($this->randomByte());
}

/**
* @param int $length
*
* @return CharactersGroup
*/
public function randomGroup(int $length = 12): CharactersGroup
{
return $this->createCharacterGroup(array_map(function (): int {
return $this->randomByte();
}, 0 === $length ? [] : range(1, $length)));
}

/**
* @param int $length
*
* @return string
*/
public function randomString(int $length = 12): string
{
return $this->randomGroup($length)->__toString();
}

/**
* @param array $array
*
* @return string
*/
private static function arrayToString(array $array): string
{
return implode('', $array);
}

/**
* @param array[] $byteSets
*
* @return CharactersGroup
*/
private function createCharacterGroup(array ...$byteSets): CharactersGroup
{
return new CharactersGroup(...array_reduce($byteSets, function (array $all, $set): array {
return array_merge($all, $set);
}, []));
}
}
27 changes: 27 additions & 0 deletions lib/Characters/Group/CharactersGroup.php
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the `src-run/web-app-v1` project.
*
* (c) Rob Frawley 2nd <rmf@src.run>
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace SR\Utilities\Characters\Group;

use SR\Utilities\Characters\CharactersTrait;

final class CharactersGroup implements \Countable, \IteratorAggregate
{
use CharactersTrait;

/**
* @param int ...$decimals
*/
public function __construct(int ...$decimals)
{
$this->bytes = $decimals;
}
}

0 comments on commit 8022208

Please sign in to comment.