diff --git a/docs/TODO.md b/docs/TODO.md index 14544b3..026c923 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -43,7 +43,7 @@ [x] → UUIDv7 for APIs? - [ ] → Rate Limiting (Cached in JSON) + [ ] → Rate Limiting [ ] → PHP Mailer / Access Token diff --git a/src/classes/api.php b/src/classes/apis/api.php similarity index 99% rename from src/classes/api.php rename to src/classes/apis/api.php index 574e577..61b2d1e 100644 --- a/src/classes/api.php +++ b/src/classes/apis/api.php @@ -8,7 +8,7 @@ declare(strict_types=1); * @license MIT */ -namespace CodeHydrater; +namespace CodeHydrater\apis; class api { const CONTINUE_STATUS = "Continue"; // 100 diff --git a/src/classes/database/model.php b/src/classes/database/model.php index e49ff70..265e4ae 100644 --- a/src/classes/database/model.php +++ b/src/classes/database/model.php @@ -44,7 +44,7 @@ class model { * JSON API inform AJAX that save failed */ public function save_failed($msg = 'Save Failed!'): void { - \CodeHydrater\api::error(array('code' => 500, 'success' => false, 'error' => $msg, 'responce' => \CodeHydrater\api::INTERNAL_ERROR)); + \CodeHydrater\apis\api::error(array('code' => 500, 'success' => false, 'error' => $msg, 'responce' => \CodeHydrater\apis\api::INTERNAL_ERROR)); } public function dump_table_fields(string $table) { diff --git a/src/classes/enums/api_tier.php b/src/classes/enums/api_tier.php new file mode 100644 index 0000000..29c7fe7 --- /dev/null +++ b/src/classes/enums/api_tier.php @@ -0,0 +1,16 @@ + + * @copyright (c) 2025, Robert Strutts + * @license MIT + */ +namespace CodeHydrater\enums; + + enum api_tier: string { + case FREE = "free"; + case PRO = "pro"; + case ENTERPRISE = "enterprise"; +}; \ No newline at end of file diff --git a/src/classes/page_not_found.php b/src/classes/page_not_found.php index e0e921a..12892fb 100644 --- a/src/classes/page_not_found.php +++ b/src/classes/page_not_found.php @@ -62,8 +62,8 @@ class page_not_found { */ private static function api_method_not_found(): void { $status = 400; // Bad Request - $bad_request = api::BAD_REQUEST; - api::error(array('response' => $bad_request, 'code' => $status, 'reason' => 'Command not found')); + $bad_request = apis\api::BAD_REQUEST; + apis\api::error(array('response' => $bad_request, 'code' => $status, 'reason' => 'Command not found')); } } \ No newline at end of file diff --git a/src/classes/uuidv7.php b/src/classes/uuidv7.php index 586ca52..8806556 100644 --- a/src/classes/uuidv7.php +++ b/src/classes/uuidv7.php @@ -16,14 +16,14 @@ namespace CodeHydrater; */ class uuidv7 { - public static function base32EncodedUUID(): string { + public static function base32_encoded_UUIDv7(): string { $uuid = self::generateUUIDv7(); $uuidHex = str_replace('-', '', $uuid); - return self::base32Encode($uuidHex); + return self::base32_encode($uuidHex); } public static function generateUUIDv7(): string { - $timestamp = microtime(true) * 10000; // current timestamp in milliseconds + $timestamp = (int)(microtime(true) * 1000); // Real Unix milliseconds $timeHex = str_pad(dechex((int)$timestamp), 12, '0', STR_PAD_LEFT); $randomHex = bin2hex(random_bytes(10)); @@ -32,13 +32,17 @@ class uuidv7 { '%s-%s-%s-%s-%s', substr($timeHex, 0, 8), substr($timeHex, 8, 4), - '7' . substr($timeHex, 12, 3) . substr($randomHex, 0, 1), // UUIDv7 - substr($randomHex, 1, 4), - substr($randomHex, 5, 12) + '7' . substr($randomHex, 0, 3), // UUIDv7 + substr($randomHex, 3, 4), + substr($randomHex, 7, 12) ); } - public static function base32Encode(string $hex): string { + /** + * NOTE: the absence of O like Orange and I like Internet, so + * Zero and One are present but not IO... + */ + public static function base32_encode(string $hex): string { $base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; $binary = ''; @@ -56,13 +60,60 @@ class uuidv7 { return $base32; } + public static function base32_decode(string $base32): string { + $base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; + $binary = ''; + + // Convert each base32 character to 5-bit binary + foreach (str_split(strtoupper($base32)) as $char) { + $pos = strpos($base32Chars, $char); + if ($pos === false) { + throw new InvalidArgumentException("Invalid base32 character: $char"); + } + $binary .= str_pad(decbin($pos), 5, '0', STR_PAD_LEFT); + } + + // Split binary string into 4-bit chunks and convert to hex + $hex = ''; + foreach (str_split($binary, 4) as $chunk) { + // Skip incomplete chunks at the end (padding) + if (strlen($chunk) < 4) break; + $hex .= dechex(bindec($chunk)); + } + + return $hex; + } + + public static function get_timestamp_from_UUIDv7(string $uuid): int { + // Remove hyphens and extract the time parts + $cleaned = str_replace('-', '', $uuid); + + // Reconstruct the full 12-character timestamp hex + $timeHex = + substr($cleaned, 0, 8) . // First 8 chars (time_low) + substr($cleaned, 8, 4); // Next 4 chars (time_mid) + + // Convert to decimal (custom scaled time) + $customTime = hexdec($timeHex); + + // Reverse the scaling: divide by 1000 to get seconds + $unixSeconds = (int)($customTime / 1000); + + return $unixSeconds; + } + + public static function get_formated_date(int $timestamp) { + return date('Y-m-d H:i:s', $timestamp); + } + /** * @example Database SQL: CREATE TABLE test_table ( - id CHAR(36) PRIMARY KEY, - name VARCHAR(255) NOT NULL, + id CHAR(36) PRIMARY KEY, -- IMPORTANT PART, to use generateUUIDv7 (UUIDv7) + -- OR CHAR(26) for base32_encoded_UUIDv7 (BASE32 UUIDv7)... + name VARCHAR(255) NOT NULL, -- BLA BLA created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); */ -} +} \ No newline at end of file