@ -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. 
				
			 
			
		
	
		
			
				
					 
					 
				
				 
				
					     *