Initial Version
This commit is contained in:
11
shared/lib/Json/JsonDeserializable.php
Normal file
11
shared/lib/Json/JsonDeserializable.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXF\Json;
|
||||
|
||||
interface JsonDeserializable {
|
||||
|
||||
public function jsonDeserialize(array|string $data): static;
|
||||
|
||||
}
|
||||
11
shared/lib/Json/JsonSerializable.php
Normal file
11
shared/lib/Json/JsonSerializable.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXF\Json;
|
||||
|
||||
interface JsonSerializable extends \JsonSerializable {
|
||||
|
||||
public function jsonSerialize(): mixed;
|
||||
|
||||
}
|
||||
68
shared/lib/Json/JsonSerializableCollection.php
Normal file
68
shared/lib/Json/JsonSerializableCollection.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?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[$key] = $value;
|
||||
}
|
||||
} else {
|
||||
foreach ($data as $value) {
|
||||
$this[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!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[$key] = $instance->jsonDeserialize($value);
|
||||
}
|
||||
} else {
|
||||
foreach ($data as $value) {
|
||||
$instance = $reflection->newInstance();
|
||||
/** @var JsonDeserializable $instance */
|
||||
$this[] = $instance->jsonDeserialize($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
165
shared/lib/Json/JsonSerializableObject.php
Normal file
165
shared/lib/Json/JsonSerializableObject.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXF\Json;
|
||||
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use DateInterval;
|
||||
|
||||
use KTXF\Json\JsonSerializable;
|
||||
use KTXF\Json\JsonDeserializable;
|
||||
|
||||
abstract class JsonSerializableObject implements JsonSerializable, JsonDeserializable {
|
||||
|
||||
protected string $dateTimeFormat = 'c'; // ISO 8601 format by default
|
||||
protected array $serializableProperties = []; // Empty array means serialize all properties
|
||||
protected array $nonSerializableProperties = ['dateTimeFormat', 'serializableProperties', 'nonSerializableProperties']; // Properties to exclude from serialization
|
||||
|
||||
public function jsonSerialize(): mixed {
|
||||
$vars = get_object_vars($this);
|
||||
|
||||
// Filter properties based on serializableProperties if specified
|
||||
if (!empty($this->serializableProperties)) {
|
||||
$vars = array_filter($vars, function($key) {
|
||||
return in_array($key, $this->serializableProperties);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
}
|
||||
|
||||
// Process each property for special types
|
||||
foreach ($vars as $key => $value) {
|
||||
// Skip internal control properties
|
||||
if (in_array($key, $this->nonSerializableProperties)) {
|
||||
unset($vars[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle DateTimeInterface (DateTime/DateTimeImmutable)
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
$vars[$key] = $value->format($this->dateTimeFormat);
|
||||
}
|
||||
// Handle DateTimeZone
|
||||
elseif ($value instanceof DateTimeZone) {
|
||||
$vars[$key] = $value->getName();
|
||||
}
|
||||
// Handle DateInterval
|
||||
elseif ($value instanceof DateInterval) {
|
||||
$vars[$key] = $this->fromDateInterval($value);
|
||||
}
|
||||
// Handle backed enums
|
||||
elseif ($value instanceof \BackedEnum) {
|
||||
$vars[$key] = $value->value;
|
||||
}
|
||||
// Handle JsonSerializable objects
|
||||
elseif ($value instanceof JsonSerializable) {
|
||||
$vars[$key] = $value->jsonSerialize();
|
||||
}
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function jsonDeserialize(array|string $data): static {
|
||||
|
||||
if (is_string($data)) {
|
||||
$data = json_decode($data, true);
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (property_exists($this, $key)) {
|
||||
// Skip internal control properties
|
||||
if (in_array($key, $this->nonSerializableProperties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if property should be deserialized (if serializableProperties is set)
|
||||
if (!empty($this->serializableProperties) && !in_array($key, $this->serializableProperties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = gettype($this->$key);
|
||||
|
||||
// Handle JsonDeserializable objects
|
||||
if ($type === 'object' && $this->$key instanceof JsonDeserializable) {
|
||||
$this->$key = $this->$key->jsonDeserialize($value);
|
||||
}
|
||||
// Handle DateTimeInterface (DateTime/DateTimeImmutable)
|
||||
elseif ($type === 'object' && $this->$key instanceof DateTimeInterface) {
|
||||
$this->$key = new \DateTimeImmutable($value);
|
||||
}
|
||||
// Handle DateTimeZone
|
||||
elseif ($type === 'object' && $this->$key instanceof DateTimeZone) {
|
||||
$this->$key = new DateTimeZone($value);
|
||||
}
|
||||
// Handle DateInterval
|
||||
elseif ($type === 'object' && $this->$key instanceof DateInterval) {
|
||||
$this->$key = $this->toDateInterval($value);
|
||||
}
|
||||
// Handle backed enums
|
||||
elseif ($type === 'object' && $this->$key instanceof \BackedEnum) {
|
||||
$enumClass = get_class($this->$key);
|
||||
$this->$key = $enumClass::from($value);
|
||||
}
|
||||
// Handle regular values
|
||||
else {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fromDateInterval(DateInterval $interval): string {
|
||||
$spec = '';
|
||||
|
||||
// Handle negative intervals
|
||||
if ($interval->invert === 1) {
|
||||
$spec = '-';
|
||||
}
|
||||
|
||||
$spec .= 'P';
|
||||
|
||||
if ($interval->y > 0) $spec .= $interval->y . 'Y';
|
||||
if ($interval->m > 0) $spec .= $interval->m . 'M';
|
||||
if ($interval->d > 0) $spec .= $interval->d . 'D';
|
||||
|
||||
$timePart = '';
|
||||
if ($interval->h > 0) $timePart .= $interval->h . 'H';
|
||||
if ($interval->i > 0) $timePart .= $interval->i . 'M';
|
||||
if ($interval->s > 0) $timePart .= $interval->s . 'S';
|
||||
|
||||
if (!empty($timePart)) {
|
||||
$spec .= 'T' . $timePart;
|
||||
}
|
||||
|
||||
// Handle edge case of zero duration
|
||||
if ($spec === 'P' || $spec === '-P') {
|
||||
$spec = 'PT0S';
|
||||
}
|
||||
|
||||
return $spec;
|
||||
}
|
||||
|
||||
protected function toDateInterval(string $value): DateInterval {
|
||||
$isNegative = false;
|
||||
|
||||
// Check for negative interval
|
||||
if (str_starts_with($value, '-')) {
|
||||
$isNegative = true;
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
|
||||
// Create the interval
|
||||
$interval = new DateInterval($value);
|
||||
|
||||
// Set invert property for negative intervals
|
||||
if ($isNegative) {
|
||||
$interval->invert = 1;
|
||||
}
|
||||
|
||||
return $interval;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user