Add @type phpdoc to allow for typed arrays

This commit is contained in:
root 2022-12-03 02:19:49 +01:00
parent 006437f670
commit f1d00615b6
2 changed files with 92 additions and 21 deletions

View File

@ -189,22 +189,22 @@ class JsonDatabase implements IJsonUnflattener
return $name;
}
public function delete(string $className, $identifier): bool
public function delete(string $className, $identifier, ?int $timestamp = null): bool
{
if ($class == null || empty(trim($name)))
if ($className == null || empty(trim($identifier)))
return false;
if ($name != null && !empty(trim(strval($name))))
$name = JSONDatabase::SanitizeIdentifier($name);
if ($identifier != null && !empty(trim(strval($identifier))))
$identifier = JSONDatabase::SanitizeIdentifier($identifier);
else
return false;
$class = trim($class);
$class = trim($className);
if (!$this->audit && $timestamp != null)
return false;
$objPath = $this->getObjectPath($class, $name, ($timestamp != null));
$objPath = $this->getObjectPath($className, $identifier, ($timestamp != null));
if (strlen($objPath) > PHP_MAXPATHLEN)
return false;
@ -215,7 +215,7 @@ class JsonDatabase implements IJsonUnflattener
return unlink($objPath);
}
public function delete(IJsonIdentifiable $obj): bool
public function deleteObj(IJsonIdentifiable $obj): bool
{
return $this->delete(get_class($obj), $obj->flatIdentifier());
}

View File

@ -37,6 +37,52 @@ abstract class JsonSerializable
return (new \ReflectionClass($typeName))->isSubclassOf(JsonSerializable::class);
}
/**
* Check if is strongly typed array of type JsonSerializable and return the type
* @param string $property Property we should check against
* @return bool returns class name if type/class of property extends JsonSerializable
*/
private static function getPropertyArrayType(\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() && $type->getName() != 'array')
return null;
$docComment = $property->getDocComment();
if (!$docComment)
return null;
$line = strtok($docComment, "\r\n");
// TODO: Maybe use preg_match here, maybe faster: https://stackoverflow.com/a/6250269
while ($line !== false) {
$docType = substr($line, strpos($line, '@type ') + 6);
if (!empty(trim($docType)))
{
$docType = trim($docType);
if (class_exists($docType)) {
$refClass = new \ReflectionClass($docType);
if ($refClass->isSubclassOf(JsonSerializable::class))
return $refClass->getName();
}
// We found a type property, do not further iterate
return null;
}
$line = strtok( "\r\n" );
}
return null;
}
/**
* Serialize a JsonSerializable Object to an array or to a json string
* @param bool $nested Indicates if we should also serialize nested object instances
@ -68,6 +114,13 @@ abstract class JsonSerializable
continue;
}
else if (isset($value) && $value != null && is_array($value)) {
$arrayType = self::getPropertyArrayType(new \ReflectionProperty($this, $key));
if ($arrayType != null)
$result[$key] = self::SerializeArray($value, $nested, true);
else
$result[$key] = $value;
}
else {
$result[$key] = $value;
}
@ -86,16 +139,28 @@ abstract class JsonSerializable
* @param bool $nested Indicates if we should also serialize nested object instances
* @return string returns a json formatted string
*/
public static function SerializeArray($objects = [], $nested = false)
public static function SerializeArray($objects = [], $nested = false, $to_array = false)
{
// Initialize a an array of results
$results = array();
// Iterate through provided JsonSerializable Objects and serialize them
foreach ($objects as $object)
$results[] = $object->Serialize($nested, true);
foreach ($objects as $object) {
if ($object instanceof JsonSerializable)
{
if ($nested)
$results[] = $object->Serialize(nested: true, to_array: true);
else if ($object instanceof IJsonIdentifiable && $object->flatIdentifier() !== null)
$results[] = $object->flatIdentifier();
else
continue;
}
}
// Return the json encoded string
// If json is demanded, encode, else just return the array
if ($to_array)
return $results;
else
return json_encode($results);
}
@ -135,7 +200,15 @@ abstract class JsonSerializable
// If simple data type, just assign it to var in class
// TODO: Exception handling, what if class specifies an int but string was provided?
$selfSubclass = self::isPropertyJsonSerializable($property);
if ($selfSubclass)
$arrayType = self::getPropertyArrayType($property);
if ($arrayType != null)
{
if (is_array($value))
$classInstance->{$key} = self::DeserializeArray($value, $nested, $callback, $arrayType);
else
continue;
}
elseif ($selfSubclass)
{
try {
if (is_array($value)) {
@ -183,7 +256,7 @@ abstract class JsonSerializable
* @param string|array $json json string containing the array or array to decode
* @return JsonSerializable[] Returns an Array of Instances of JsonSerializable
*/
public static function DeserializeArray(string|array $json, bool $nested = false)
public static function DeserializeArray(string|array $json, bool $nested = false, IJsonUnflattener $callback = null, string $className = null)
{
// Check if we need to decode from json first
if (is_string($json))
@ -194,14 +267,12 @@ abstract class JsonSerializable
// Loop through the array
foreach ($json as $item)
{
// Uh oh, check if there is an nested array within the array and deserialize children
// If not, or if we are not supposed to, just deserialize the current layer or ignore
if ($nested && is_array($item))
$items[] = self::DeserializeArray(json: $item, nested: $nested);
else if (is_array($item))
continue;
if (is_array($item))
$items[] = self::Deserialize(json: $item, nested: $nested, callback: $callback, className: $className);
else if ($className != null && $callback != null && !is_object($item))
$items[] = call_user_func([$callback, 'unflattenByID'], $className, $item);
else
$items[] = self::Deserialize(json: $item, nested: $nested);
continue;
}
// Returns the result