diff --git a/CHANGELOG.md b/CHANGELOG.md index aa738eb..2ad0fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1482,3 +1482,22 @@ The powerful `retrieve()` method, which allows for easy access to nested propert ```php $secretConfig->retrieve('database', 'credentials', 'username'); ``` + +# MagicObject Version 3.22.0 + +## What's New + +1. **UUID Generator** + Added a native UUID generator that produces standards-compliant UUID values, suitable for distributed systems and cross-database compatibility. + +2. **Time-Based ID Generator** + Introduced a time-based ID generator that creates sortable, time-ordered identifiers, improving indexing performance and data traceability. + +## What's Changed + +1. **Default Primary Key Generation** + The default primary key generation strategy for `VARCHAR` columns has been changed to use the **time-based ID generator**, replacing the previous approach. + +2. **Improved UUID Implementation** + Replaced the legacy unique ID generation based on PHP’s `uniqid()` function with a **real UUID implementation**, ensuring better uniqueness guarantees and compliance with UUID standards. + diff --git a/src/Database/PicoDatabase.php b/src/Database/PicoDatabase.php index 69dad06..af4c091 100644 --- a/src/Database/PicoDatabase.php +++ b/src/Database/PicoDatabase.php @@ -1257,6 +1257,66 @@ public function generateNewId() return sprintf('%s%s', $uuid, $random); } + /** + * Generate a random UUID (Universally Unique Identifier) version 4. + * + * This function creates a 128-bit UUID using random bytes and applies + * the proper version (4) and variant (RFC 4122) bits. The result is + * formatted as a canonical string representation: + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * Example output: + * ad271d69-6c5a-4002-b2d2-3e0f84f7dfa3 + * + * @return string A UUID v4 string in standard format. + */ + public function generateUUID() { + // Generate 16 bytes (128 bit) random data + $data = random_bytes(16); + + // Set versi ke 0100 (UUID v4) + $data[6] = chr((ord($data[6]) & 0x0f) | 0x40); + // Set variant ke 10xxxxxx + $data[8] = chr((ord($data[8]) & 0x3f) | 0x80); + + // Format ke string UUID + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); + } + + /** + * Generates a unique hexadecimal ID based on the current epoch time in nanoseconds + * combined with a 3-hex-digit random suffix. + * + * The ID consists of: + * - A 17-character hexadecimal timestamp derived from microtime() + * - A 3-character hexadecimal random value + * + * @return string Unique hexadecimal identifier + */ + public function generateTimeBasedId() + { + // Get microtime as "microseconds seconds" + $microtimeParts = explode(' ', microtime()); + + // Format microseconds to 9 decimal places + $microsecondsFormatted = sprintf('%11.9f', $microtimeParts[0]); + + // Split seconds and fractional microseconds + $microsecondsParts = explode('.', $microsecondsFormatted); + + // Combine seconds and fractional part into a nanosecond-like integer string + $epochNanoString = sprintf('%d%+9s', $microtimeParts[1], $microsecondsParts[1]); + + // Convert the timestamp to a fixed-length hexadecimal string + $timestampHex = sprintf('%017x', (int) $epochNanoString); + + // Generate a 3-hex-digit random value + $randomHex = sprintf('%03x', mt_rand(0, 0xFFF)); + + // Concatenate timestamp and random suffix + return $timestampHex . $randomHex; + } + /** * Get the last inserted ID. * diff --git a/src/Database/PicoDatabasePersistence.php b/src/Database/PicoDatabasePersistence.php index 6b83647..ab8dbef 100644 --- a/src/Database/PicoDatabasePersistence.php +++ b/src/Database/PicoDatabasePersistence.php @@ -1143,6 +1143,15 @@ private function setGeneratedValue($prop, $strategy, $firstCall) $this->generatedValue = true; } } + else if(stripos($strategy, "TIMEBASED") !== false) + { + if($firstCall && ($this->object->get($prop) == null || $this->object->get($prop) == "") && !$this->generatedValue) + { + $generatedValue = $this->database->generateTimeBasedId(); + $this->object->set($prop, $generatedValue); + $this->generatedValue = true; + } + } else if(stripos($strategy, "IDENTITY") !== false) { if($firstCall) diff --git a/src/Generator/PicoEntityGenerator.php b/src/Generator/PicoEntityGenerator.php index 16878e1..fc2efbd 100644 --- a/src/Generator/PicoEntityGenerator.php +++ b/src/Generator/PicoEntityGenerator.php @@ -125,7 +125,7 @@ public function createProperty($typeMap, $columnMap, $row, $nonupdatables = null if (!empty($columnKey) && stripos($columnKey, "PRI") === 0) { $docs[] = "\t * @Id"; if (stripos($columnExtra, "auto_increment") === false) { - $docs[] = "\t * @GeneratedValue(strategy=GenerationType.UUID)"; + $docs[] = "\t * @GeneratedValue(strategy=GenerationType.TIMEBASED)"; } }