From 0ee81236e35c72df5a5ef9a5c4575426561a45b1 Mon Sep 17 00:00:00 2001 From: Cerys Date: Sun, 1 Dec 2024 22:47:48 +0000 Subject: [PATCH] more work on schema generation --- src/Attributes/SchemaDocumentField.php | 30 +++-- src/SchemaBuilder/SchemaBuilder.php | 177 +++++++++++++++---------- 2 files changed, 127 insertions(+), 80 deletions(-) diff --git a/src/Attributes/SchemaDocumentField.php b/src/Attributes/SchemaDocumentField.php index d4832bc..21144f4 100644 --- a/src/Attributes/SchemaDocumentField.php +++ b/src/Attributes/SchemaDocumentField.php @@ -12,26 +12,34 @@ class SchemaDocumentField extends Attribute { public string $Name; - public SchemaFieldExistence $Existence; - public string $Comment; - public array $ValidSchemas; - public int $MaxSize; - public string $MimeType; + public ?SchemaFieldExistence $Existence; + public ?string $Comment; + public ?array $ValidSchemas; + public int $MaxSize = PHP_INT_MIN; + public ?string $MimeType; + public ?SchemaDocumentField $Child; + public ?array $Children; public function __construct( string $Name, - SchemaFieldExistence $Existence = SchemaFieldExistence::MAY, - string $Comment = "", - array $ValidSchemas = [], - int $MaxSize = 0, - string $MimeType = "", - SchemaDocumentField $Child = null + ?SchemaFieldExistence $Existence = null, + ?string $Comment = null, + ?array $ValidSchemas = null, + int $MaxSize = PHP_INT_MIN, + ?string $MimeType = null, + ?SchemaDocumentField $Child = null, + ?array $Children = null, ) { + $this->Name = $Name; $this->Existence = $Existence; $this->Comment = $Comment; $this->ValidSchemas = $ValidSchemas; + $this->MaxSize = $MaxSize; $this->MimeType = $MimeType; + $this->Child = $Child; + $this->Children = $Children; + parent::__construct(new Name("SchemaDocumentField"), [], []); } diff --git a/src/SchemaBuilder/SchemaBuilder.php b/src/SchemaBuilder/SchemaBuilder.php index f0b9ae1..914bcb3 100644 --- a/src/SchemaBuilder/SchemaBuilder.php +++ b/src/SchemaBuilder/SchemaBuilder.php @@ -10,8 +10,13 @@ use JetBrains\PhpStorm\NoReturn; use PHPUnit\Util\Exception; use ReflectionClass; + class SchemaBuilder { + private const CLASS_NAME_REGEX = '/^(([\\\\]*)?[a-zA-Z_][a-zA-Z0-9_]*)(([\\\\]*[a-zA-Z0-9_]*)*)$/'; + private const URL_REGEX = '/^https?:\/\/[^\s$.?#].[^\s]*$/i'; + + /** * Goes through the Attribute classes and makes a list of all the Properties they have. * This is so if a user adds another variable to the Attribute constructor, it won't appear in the final Schema. @@ -60,94 +65,128 @@ class SchemaBuilder return strtolower(preg_replace('/(?$value) + { + switch($key) + { + case "Name": + $propertyName = $value; + break; + case "Existence": + if($value == null) continue; + $propertySchema["@existence"] = $value; + break; + case "Comment": + if($value == null) continue; + $propertySchema["@comment"] = $value; + break; + case "ValidSchemas": + if($value == null) continue; + + $propertySchema["@valid_schemas"] = []; + foreach($value as $validSchema) + { + if (preg_match(self::CLASS_NAME_REGEX, $validSchema)) + $propertySchema["@valid_schemas"][] = URLHandling::GetURLForSchema($validSchema); + elseif (preg_match(self::URL_REGEX, $validSchema)) + $propertySchema["@valid_schemas"][] = $validSchema; + else + throw new \Exception("Invalid schema property: " . $validSchema); + } + break; + case "MaxSize": + if($value == PHP_INT_MIN) continue; + $propertySchema["@max_size"] = $value; + break; + case "MimeType": + if($value == null) continue; + $propertySchema["@mime_type"] = $value; + break; + case "Child": + if($value == null) continue; + $temp = json_decode(json_encode($value), true); + unset($temp['nodeType']); + unset($temp['attributes']); + unset($temp['name']); + unset($temp['args']); + $propertySchema["#"] = self::ProcessSchemaPropertyArguments($temp)[1]; + break; + case "Children": + if($value == null) continue; + foreach($value as $child) + { + $temp = json_decode(json_encode($child), true); + unset($temp['nodeType']); + unset($temp['attributes']); + unset($temp['name']); + unset($temp['args']); + $propertySchema[$child->Name] = self::ProcessSchemaPropertyArguments($temp)[1]; + } + break; + default: + throw new \Exception("unknown property: " . $key); + } + } + return [$propertyName, $propertySchema]; + } + + private static function ProcessSchemaProperties($reflection): array + { + $schema = []; + foreach ($reflection->getProperties() as $property) + { + $targetAttribute = null; + foreach ($property->getAttributes() as $attribute) + if($attribute->getName() == SchemaDocumentField::class) + $targetAttribute = $attribute; + + if($targetAttribute == null) + throw new \Exception("required attribute does not exist"); + + $temp = self::ProcessSchemaPropertyArguments($targetAttribute->getArguments()); + $propertyName = $temp[0]; + $propertySchema = $temp[1]; + if($propertyName == "") + throw new SchemaDocumentFieldNameUnsetException(); + $schema["$propertyName"] = $propertySchema; + } + return $schema; + } /** * @throws SchemaDocumentFieldNameUnsetException */ - public static function GenerateSchema(string $targetSchemaClassName): array + private static function GenerateSchemaReflectionObject(ReflectionClass $reflection): array { $schema = []; - $validKeys = self::GetValidKeys(); - $reflection = new ReflectionClass(new $targetSchemaClassName()); /* * Schema "meta-data" from here... */ foreach($reflection->getAttributes()[0]->getArguments() as $key=>$value) - if(self::VerifyField($key, $value, $validKeys[0])) - $schema["@" . self::PascalCaseToSnakeCase(input: $key)] = $value; + if(self::VerifyField($key, $value, self::GetValidKeys()[0])) + if($key != "Name") + $schema["@" . self::PascalCaseToSnakeCase(input: $key)] = $value; /* * Property handling from here... */ - foreach ($reflection->getProperties() as $property) - { - $classNameRegex = '/^([\\]*)?[a-zA-Z_][a-zA-Z0-9_]*)(([\\]*[a-zA-Z0-9_]*)*)$/'; - $urlRegex = '/^https?:\/\/[^\s$.?#].[^\s]*$/i'; - - $propertyName = ""; - $propertySchema = []; - - foreach ($property->getAttributes() as $attribute) - { - if($attribute->getName() != SchemaDocumentField::class) - continue; - - foreach($attribute->getArguments() as $key=>$value) - { - if($key == "Name") - { - $propertyName = $value; - } - elseif($key == "Child") - { - $propertySchema["#"] = [ - "@comment" => $value->Comment, - "@valid_schemas" => $value->ValidSchemas, - ]; - $propertySchema["@valid_schemas"] = []; - foreach($value as $validSchema) - { - if (preg_match($classNameRegex, $validSchema)) - $propertySchema["@valid_schemas"][] = URLHandling::GetURLForSchema($validSchema); - elseif (preg_match($urlRegex, $validSchema)) - $propertySchema["@valid_schemas"][] = $validSchema; - else - $propertySchema["@valid_schemas"][] = "test123"; - } - } - elseif($key == "Existance") - { - $propertySchema["#"] = $value->value; - } - elseif($key == "ValidSchemas") - { - $propertySchema["@valid_schemas"] = []; - foreach($value as $validSchema) - { - if (preg_match($classNameRegex, $validSchema)) - $propertySchema["@valid_schemas"][] = URLHandling::GetURLForSchema($validSchema); - elseif (preg_match($urlRegex, $validSchema)) - $propertySchema["@valid_schemas"][] = $validSchema; - else - $propertySchema["@valid_schemas"][] = "test123"; - } - } - elseif(self::VerifyField($key, $value, $validKeys[1])) - { - $propertySchema["@" . self::PascalCaseToSnakeCase(input: $key)] = $value; - } - } - } - if($propertyName == "") - throw new SchemaDocumentFieldNameUnsetException(); - - $schema["$propertyName"] = $propertySchema; - } + foreach(self::ProcessSchemaProperties($reflection) as $key=>$value) + $schema[$key] = $value; return $schema; } + public static function GenerateSchema(string $targetSchemaClassName): array + { + $reflection = new ReflectionClass(new $targetSchemaClassName()); + return self::GenerateSchemaReflectionObject($reflection); + } + /** * Generates the Schema using the SchemaBuilder::GenerateSchema() function, sets the http header to application/json, echos the schema as JSON, then dies. *