You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
4.3 KiB

<?php
namespace Darksparrow\AuxiliumSchemaBuilder\SchemaBuilder;
use Darksparrow\AuxiliumSchemaBuilder\Exceptions\SchemaDocumentFieldNameUnsetException;
use Darksparrow\AuxiliumSchemaBuilder\Attributes\SchemaDocument;
use Darksparrow\AuxiliumSchemaBuilder\Attributes\SchemaDocumentField;
use JetBrains\PhpStorm\NoReturn;
use ReflectionClass;
class SchemaBuilder
{
/**
* 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.
*
* @return array[]
*/
private static function GetValidKeys(): array
{
$validDocumentAttributeNames = [];
$validPropertyAttributeNames = [];
foreach((new ReflectionClass(new SchemaDocument("EMPTY")))->getProperties() as $temp)
$validDocumentAttributeNames[] = $temp->getName();
foreach((new ReflectionClass(new SchemaDocumentField("EMPTY")))->getProperties() as $temp)
$validPropertyAttributeNames[] = $temp->getName();
return [$validDocumentAttributeNames, $validPropertyAttributeNames];
}
/**
* Checks to see if a value is "okay" for a Schema.
*
* @param string $key
* @param mixed $value
* @param array $valids
* @return bool
*/
private static function VerifyField(string $key, mixed $value, array $valids): bool
{
if(!in_array(needle: $key, haystack: $valids))
return false;
if($value == "")
return false;
return true;
}
/**
* Just converts PascalCase to snake_case.
*
* @param string $input
* @return string
*/
private static function PascalCaseToSnakeCase(string $input): string
{
return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $input));
}
/**
* @throws SchemaDocumentFieldNameUnsetException
*/
public static function GenerateSchema(object $targetSchema): array
{
$schema = [];
$validKeys = self::GetValidKeys();
$reflection = new ReflectionClass($targetSchema);
/*
* 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)
{
$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,
];
}
elseif(self::VerifyField($key, $value, $validKeys[1]))
{
$propertySchema["@" . self::PascalCaseToSnakeCase(input: $key)] = $value;
}
}
}
if($propertyName == "")
throw new SchemaDocumentFieldNameUnsetException();
$schema["$propertyName"] = $propertySchema;
}
return $schema;
}
/**
* Generates the Schema using the SchemaBuilder::GenerateSchema() function, sets the http header to application/json, echos the schema as JSON, then dies.
*
* @param object $targetSchema The Schema class object to generate from.
* @return void
* @throws SchemaDocumentFieldNameUnsetException
*/
#[NoReturn] public static function RenderSchema(object $targetSchema): void
{
$result = self::GenerateSchema(new $targetSchema());
header('Content-Type: application/json');
echo json_encode($result, JSON_PRETTY_PRINT);
die();
}
}