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

View File

@ -37,6 +37,52 @@ abstract class JsonSerializable
return (new \ReflectionClass($typeName))->isSubclassOf(JsonSerializable::class); 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 * Serialize a JsonSerializable Object to an array or to a json string
* @param bool $nested Indicates if we should also serialize nested object instances * @param bool $nested Indicates if we should also serialize nested object instances
@ -68,6 +114,13 @@ abstract class JsonSerializable
continue; 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 { else {
$result[$key] = $value; $result[$key] = $value;
} }
@ -86,17 +139,29 @@ abstract class JsonSerializable
* @param bool $nested Indicates if we should also serialize nested object instances * @param bool $nested Indicates if we should also serialize nested object instances
* @return string returns a json formatted string * @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 // Initialize a an array of results
$results = array(); $results = array();
// Iterate through provided JsonSerializable Objects and serialize them // Iterate through provided JsonSerializable Objects and serialize them
foreach ($objects as $object) foreach ($objects as $object) {
$results[] = $object->Serialize($nested, true); 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
return json_encode($results); 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 // If simple data type, just assign it to var in class
// TODO: Exception handling, what if class specifies an int but string was provided? // TODO: Exception handling, what if class specifies an int but string was provided?
$selfSubclass = self::isPropertyJsonSerializable($property); $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 { try {
if (is_array($value)) { if (is_array($value)) {
@ -183,7 +256,7 @@ abstract class JsonSerializable
* @param string|array $json json string containing the array or array to decode * @param string|array $json json string containing the array or array to decode
* @return JsonSerializable[] Returns an Array of Instances of JsonSerializable * @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 // Check if we need to decode from json first
if (is_string($json)) if (is_string($json))
@ -194,14 +267,12 @@ abstract class JsonSerializable
// Loop through the array // Loop through the array
foreach ($json as $item) foreach ($json as $item)
{ {
// Uh oh, check if there is an nested array within the array and deserialize children if (is_array($item))
// If not, or if we are not supposed to, just deserialize the current layer or ignore $items[] = self::Deserialize(json: $item, nested: $nested, callback: $callback, className: $className);
if ($nested && is_array($item)) else if ($className != null && $callback != null && !is_object($item))
$items[] = self::DeserializeArray(json: $item, nested: $nested); $items[] = call_user_func([$callback, 'unflattenByID'], $className, $item);
else if (is_array($item))
continue;
else else
$items[] = self::Deserialize(json: $item, nested: $nested); continue;
} }
// Returns the result // Returns the result