Files
server/shared/lib/Json/JsonSerializableCollection.php
T
2026-07-17 15:52:07 -04:00

87 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXF\Json;
use KTXF\Json\JsonSerializable;
use KTXF\Json\JsonDeserializable;
use KTXF\Utile\Collection\CollectionAbstract;
abstract class JsonSerializableCollection extends CollectionAbstract implements JsonSerializable, JsonDeserializable {
protected array $primitiveTypes = [
self::TYPE_STRING,
self::TYPE_INT,
self::TYPE_FLOAT,
self::TYPE_BOOL,
self::TYPE_ARRAY,
self::TYPE_DATE,
];
public function jsonSerialize(): mixed {
return $this->getArrayCopy();
}
public function jsonDeserialize(array|string $data): static {
if (is_string($data)) {
$data = json_decode($data, true);
}
$this->exchangeArray([]);
if (in_array($this->typeValue, $this->primitiveTypes)) {
if ($this->associative) {
foreach ($data as $key => $value) {
$this[$this->normalizeKey($key)] = $value;
}
} else {
foreach ($data as $value) {
$this[] = $value;
}
}
}
if (!in_array($this->typeValue, $this->primitiveTypes) && is_subclass_of($this->typeValue, \BackedEnum::class)) {
if ($this->associative) {
foreach ($data as $key => $value) {
$this[$this->normalizeKey($key)] = ($this->typeValue)::from($value);
}
} else {
foreach ($data as $value) {
$this[] = ($this->typeValue)::from($value);
}
}
} elseif (!in_array($this->typeValue, $this->primitiveTypes) && class_exists($this->typeValue)) {
$reflection = new \ReflectionClass($this->typeValue);
if ($reflection->implementsInterface(JsonDeserializable::class)) {
if ($this->associative) {
foreach ($data as $key => $value) {
$instance = $reflection->newInstance();
/** @var JsonDeserializable $instance */
$this[$this->normalizeKey($key)] = $instance->jsonDeserialize($value);
}
} else {
foreach ($data as $value) {
$instance = $reflection->newInstance();
/** @var JsonDeserializable $instance */
$this[] = $instance->jsonDeserialize($value);
}
}
}
}
return $this;
}
/**
* JSON round trips turn numeric string keys into integers (and list-shaped
* payloads carry integer keys), which a string-keyed collection would
* otherwise reject on offsetSet.
*/
private function normalizeKey(int|string $key): int|string {
return $this->typeKey === self::TYPE_STRING ? (string)$key : $key;
}
}