@ -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('/(?< !^)[A-Z]/', '_$0', $input));
}
/**
* @throws SchemaDocumentFieldNameUnsetException
*/
public static function GenerateSchema(string $targetSchemaClassName): array
private static function ProcessSchemaPropertyArguments($arguments): 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;
/*
* 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)
foreach($arguments as $key=>$value)
{
if($key == "Name" )
switch($key)
{
case "Name":
$propertyName = $value;
}
elseif($key == "Child")
{
$propertySchema["#"] = [
"@comment" => $value->Comment,
"@valid_schemas" => $value->ValidSchemas,
];
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($classNameRegex, $validSchema))
if (preg_match(self::CLASS_NAME_REGEX , $validSchema))
$propertySchema["@valid_schemas"][] = URLHandling::GetURLForSchema($validSchema);
elseif (preg_match($urlRegex , $validSchema))
elseif (preg_match(self::URL_REGEX , $validSchema))
$propertySchema["@valid_schemas"][] = $validSchema;
else
$propertySchema["@valid_schemas"][] = "test123";
}
throw new \Exception("Invalid schema property: " . $validSchema);
}
elseif($key == "Existance")
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)
{
$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";
$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];
}
}
elseif(self::VerifyField($key, $value, $validKeys[1]))
{
$propertySchema["@" . self::PascalCaseToSnakeCase(input: $key)] = $value;
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
*/
private static function GenerateSchemaReflectionObject(ReflectionClass $reflection): array
{
$schema = [];
/*
* Schema "meta-data" from here...
*/
foreach($reflection->getAttributes()[0]->getArguments() as $key=>$value)
if(self::VerifyField($key, $value, self::GetValidKeys()[0]))
if($key != "Name")
$schema["@" . self::PascalCaseToSnakeCase(input: $key)] = $value;
/*
* Property handling from here...
*/
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.
*