Files
michaelschiemer/src/Framework/Core/Encoding/Base32Alphabet.php
Michael Schiemer e30753ba0e fix: resolve RedisCache array offset error and improve discovery diagnostics
- Fix RedisCache driver to handle MGET failures gracefully with fallback
- Add comprehensive discovery context comparison debug tools
- Identify root cause: WEB context discovery missing 166 items vs CLI
- WEB context missing RequestFactory class entirely (52 vs 69 commands)
- Improved exception handling with detailed binding diagnostics
2025-09-12 20:05:18 +02:00

152 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Core\Encoding;
/**
* Base32 Alphabet Enum
*
* Defines different Base32 alphabets for various use cases:
* - RFC3548: Standard Base32 alphabet for TOTP and general use
* - CROCKFORD: Crockford's Base32 for ULIDs and human-readable IDs
*/
enum Base32Alphabet: string
{
case RFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
case CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
/**
* Get the alphabet string
*/
public function getAlphabet(): string
{
return $this->value;
}
/**
* Check if this alphabet uses padding
*/
public function usesPadding(): bool
{
return match ($this) {
self::RFC3548 => true,
self::CROCKFORD => false
};
}
/**
* Get the character count (always 32 for Base32)
*/
public function getCharacterCount(): int
{
return 32;
}
/**
* Validate if a character exists in this alphabet
*/
public function containsCharacter(string $char): bool
{
return str_contains($this->value, strtoupper($char));
}
/**
* Get the index of a character in the alphabet
*/
public function getCharacterIndex(string $char): int
{
$index = strpos($this->value, strtoupper($char));
if ($index === false) {
throw new \InvalidArgumentException("Character '{$char}' not found in {$this->name} alphabet");
}
return $index;
}
/**
* Get character at specific index
*/
public function getCharacterAt(int $index): string
{
if ($index < 0 || $index >= 32) {
throw new \InvalidArgumentException("Index must be between 0 and 31, got {$index}");
}
return $this->value[$index];
}
/**
* Validate an encoded string against this alphabet
*/
public function isValidEncoded(string $encoded): bool
{
// Remove padding if this alphabet uses it
if ($this->usesPadding()) {
$encoded = rtrim($encoded, '=');
}
$encoded = strtoupper($encoded);
// Check if all characters are valid
for ($i = 0, $len = strlen($encoded); $i < $len; $i++) {
if (! $this->containsCharacter($encoded[$i])) {
return false;
}
}
return true;
}
/**
* Get recommended use cases for this alphabet
* @return array<int, string>
*/
public function getUseCases(): array
{
return match ($this) {
self::RFC3548 => [
'TOTP secrets',
'General Base32 encoding',
'Email verification codes',
'API tokens',
],
self::CROCKFORD => [
'ULIDs',
'Human-readable identifiers',
'Short URLs',
'Database primary keys',
]
};
}
/**
* Get description of this alphabet
*/
public function getDescription(): string
{
return match ($this) {
self::RFC3548 => 'RFC 3548 standard Base32 alphabet with padding',
self::CROCKFORD => 'Crockford\'s Base32 alphabet without padding, optimized for human readability'
};
}
/**
* Generate a random string using this alphabet
*/
public function generateRandom(int $length): string
{
if ($length < 1) {
throw new \InvalidArgumentException('Length must be positive');
}
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $this->value[random_int(0, 31)];
}
return $result;
}
}