From 54965b401d40ac8273f04699e732091f1c089a7b Mon Sep 17 00:00:00 2001 From: root Date: Fri, 4 Aug 2023 21:23:36 +0200 Subject: [PATCH] Add backed enum support and typed arrays --- index.php | 15 +++++++--- src/SeriousJSON/ExampleEntity/Ticket.php | 4 +++ src/SeriousJSON/JsonDatabase.php | 38 ++++++++++++++++++++++-- src/SeriousJSON/JsonSerializable.php | 32 +++++++++++++++++++- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/index.php b/index.php index 5e1476f..cf3adaf 100644 --- a/index.php +++ b/index.php @@ -32,16 +32,23 @@ $ticket->start = 1471111; $ticket->end = 1474567; $ticket->user = $user; $ticket->grant = $grant; +$ticket->groups[] = new User(); +$ticket->groups[0]->id = 'baaaa'; +$ticket->groups[0]->cert = 'Cat'; + + $database->save($service); $database->save($user); $database->save($grant); $database->save($ticket); +$database->save($ticket->groups[0]); + echo('Start Time in microseconds: ' . microtime(true) . '


'); echo('Serialize Ticket with Nesting on
'); -echo($ticket->Serialize(true)); -echo('

Deserialize Ticket with Nesting on (vardump)
<'); +echo($ticket->Serialize(true, false, $database)); +echo('

Deserialize Ticket with Nesting on (vardump)
'); echo(var_dump(Ticket::Deserialize($ticket->Serialize(true), true))); echo('

Re-Serialize Deserialized Ticket with Nesting on
'); echo(Ticket::Deserialize($ticket->Serialize(true), true))->Serialize(true); @@ -49,8 +56,8 @@ echo('


End Time in microseconds: ' . microtime(true)) echo('



Start Time in microseconds: ' . microtime(true) . '


'); echo('Serialize Ticket with Nesting off
'); -echo($ticket->Serialize(false)); -echo('

Deserialize Ticket with Nesting off (vardump)
<'); +echo($ticket->Serialize(false, false, $database)); +echo('

Deserialize Ticket with Nesting off (vardump)
'); echo(var_dump(Ticket::Deserialize($ticket->Serialize(false), false, $database))); echo('

Re-Serialize Deserialized Ticket with Nesting off
'); echo(Ticket::Deserialize($ticket->Serialize(false), false, $database))->Serialize(false); diff --git a/src/SeriousJSON/ExampleEntity/Ticket.php b/src/SeriousJSON/ExampleEntity/Ticket.php index 2e07c57..8115501 100644 --- a/src/SeriousJSON/ExampleEntity/Ticket.php +++ b/src/SeriousJSON/ExampleEntity/Ticket.php @@ -7,4 +7,8 @@ class Ticket extends BaseEntity { public User $user; public int $start; public int $end; + /** + * @type SeriousJSON\ExampleEntity\User + */ + public array $groups = []; } diff --git a/src/SeriousJSON/JsonDatabase.php b/src/SeriousJSON/JsonDatabase.php index 13a1500..0e84877 100644 --- a/src/SeriousJSON/JsonDatabase.php +++ b/src/SeriousJSON/JsonDatabase.php @@ -34,7 +34,7 @@ class JsonDatabase implements IJsonUnflattener throw new \Exception('JsonDatabase: Unable to create Directory for Path ' . $dirPath . '.'); } - private static function SanitizeIdentifier($identifier) + public static function SanitizeIdentifier($identifier) { return trim(preg_replace( '/[^a-zA-Z0-9 ()\-\[\]]+/', '-', strtolower($identifier))); } @@ -100,7 +100,7 @@ class JsonDatabase implements IJsonUnflattener return false; $class = trim($class); - + if (!$this->audit && $timestamp != null) return false; @@ -302,6 +302,40 @@ class JsonDatabase implements IJsonUnflattener return $this->delete(get_class($obj), $obj->flatIdentifier()); } + /** + * List all objects by class + * @param string name of class to search for + * @return array of identifiers or empty array + */ + public function listAll(string $className, int $time = null): array + { + if ($className == null) + return []; + + $objStoragePath = $this->getObjectPath($className, '-star-', $time); + // Replace -star- with catchall * so that it becomes path/to/*.json + // TODO: Do this better + $objStoragePath = str_replace('-star-', '*', $objStoragePath); + + if (strlen($objStoragePath) > PHP_MAXPATHLEN) + return []; + + $files = glob($objStoragePath); + + // Call the function is_file on every element + // and filter those that aren't files aka directories out + $files = array_filter($files, 'is_file'); + + array_walk($files, function (&$value, $key) { + // Remove .json + $value = basename($value, JsonDatabase::$EXTENSION); + // Remove invalid chars + $value = JsonDatabase::SanitizeIdentifier($value); + }); + + return $files; + } + /** * Restore a JsonSerializable from the Database by using it's Identifier reference. * @param string $className the full class name with domain of the JsonSerializable property diff --git a/src/SeriousJSON/JsonSerializable.php b/src/SeriousJSON/JsonSerializable.php index bba1835..ea556cb 100644 --- a/src/SeriousJSON/JsonSerializable.php +++ b/src/SeriousJSON/JsonSerializable.php @@ -37,6 +37,32 @@ abstract class JsonSerializable return (new \ReflectionClass($typeName))->isSubclassOf(JsonSerializable::class); } + /** + * Check if a given property extends BackedEnum + * @param string $property Property we should check against + * @return bool returns true if type/class of property extends BackedEnum + */ + private static function isPropertyBackedEnum(\ReflectionProperty $property) + { + if (!isset($property) || $property === null || !$property->hasType()) + return false; + + // Get builtin type or class of given property + $type = $property->getType(); + + // A built-in type is any type that is not a class, interface, or trait. + // Assume false on simple types, we can only check this on classes + if ($type->isBuiltin()) + return false; + + // String name of property type/class + $typeName = $type->getName(); + + // Create an reflection instance of the found class name + // and check if it is a subclass of parent class + return (new \ReflectionClass($typeName))->isSubclassOf(\BackedEnum::class); + } + /** * Check if is strongly typed array of type JsonSerializable and return the type * @param string $property Property we should check against @@ -241,7 +267,11 @@ abstract class JsonSerializable else { try { - $classInstance->{$key} = $value; + // Try to handle backed enums, call method "from" on enum class + if (self::isPropertyBackedEnum($property)) + $classInstance->{$key} = call_user_func(array($property->getType()->getName(), 'from'), $value); + else + $classInstance->{$key} = $value; } catch (\TypeError $e) { // Assignment might fail due to incompatible data types or other reasons