refac: DTO 구조 변경

- Converter - Constructor 관련 경고 수정
- JsonString 관련 의존성 제거, jsonFlag 지정
- DateTimeConveter에서 time format 지정
This commit is contained in:
2022-05-29 00:44:28 +09:00
parent 7464629597
commit 35b178fe3c
10 changed files with 96 additions and 98 deletions
+17 -3
View File
@@ -8,23 +8,37 @@ use sammo\DTO\Converter\Converter;
class Convert class Convert
{ {
public Converter $converter; public Converter $converter;
public array $targetTypes;
public readonly array $args;
public function __construct( public function __construct(
public readonly array $targetTypes,
public readonly string $converterType, public readonly string $converterType,
...$args ...$args
) { ) {
if(!is_subclass_of($converterType, \sammo\DTO\Converter\Converter::class)){ if(!is_subclass_of($converterType, \sammo\DTO\Converter\Converter::class)){
throw new \Exception("$converterType is not a subclass of \sammo\DTO\Converter\Converter"); throw new \Exception("$converterType is not a subclass of DTO\Converter\Converter");
} }
$this->converter = new $converterType($targetTypes, ...$args); $this->args = $args;
}
public function setType(array $targetTypes): self{
$this->targetTypes = $targetTypes;
$converterType = $this->converterType;
$this->converter = new $converterType($targetTypes, ...$this->args);
return $this;
} }
public function convertFrom(string|array|int|float|bool|null $raw): mixed public function convertFrom(string|array|int|float|bool|null $raw): mixed
{ {
if($this->converter === null){
throw new \Exception('converter is not set');
}
return $this->converter->convertFrom($raw); return $this->converter->convertFrom($raw);
} }
public function convertTo(mixed $target): string|array|int|float|bool|null { public function convertTo(mixed $target): string|array|int|float|bool|null {
if($this->converter === null){
throw new \Exception('converter is not set');
}
return $this->converter->convertTo($target); return $this->converter->convertTo($target);
} }
} }
+4 -2
View File
@@ -5,7 +5,9 @@ namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)] #[\Attribute(\Attribute::TARGET_PROPERTY)]
class JsonString class JsonString
{ {
public function __construct(public readonly bool $emptyItemIsArray = false) public function __construct(
{ public readonly bool $emptyItemIsArray = false,
public readonly int $jsonFlag = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK
) {
} }
} }
+6 -8
View File
@@ -5,20 +5,18 @@ namespace sammo\DTO\Converter;
class ArrayConverter implements Converter class ArrayConverter implements Converter
{ {
private Converter $itemConverter; private Converter $itemConverter;
public function __construct(private array $types, ...$args) public function __construct(private array $types, array $itemTypes, ?string $itemConverterClass = null, ...$args)
{ {
$itemTypes = array_shift($args);
if(!is_array($itemTypes)){ if(!is_array($itemTypes)){
throw new \Exception('itemTypes is not a array'); throw new \Exception('itemTypes is not a array');
} }
$itemConverterType = array_shift($args); if($itemConverterClass === null){
if($itemConverterType === null){ $itemConverterClass = DefaultConverter::class;
$itemConverterType = DefaultConverter::class;
} }
else if(!is_subclass_of($itemConverterType, Converter::class)){ else if(!is_subclass_of($itemConverterClass, Converter::class)){
throw new \Exception("$itemConverterType is not a subclass of \sammo\DTO\Converter\Converter"); throw new \Exception("$itemConverterClass is not a subclass of \sammo\DTO\Converter\Converter");
} }
$this->itemConverter = new $itemConverterType($itemTypes, ...$args); $this->itemConverter = new $itemConverterClass($itemTypes, ...$args);
} }
public function convertFrom(string|array|int|float|bool|null $raw): mixed public function convertFrom(string|array|int|float|bool|null $raw): mixed
-1
View File
@@ -2,7 +2,6 @@
namespace sammo\DTO\Converter; namespace sammo\DTO\Converter;
interface Converter{ interface Converter{
public function __construct(array $types, ...$args);
public function convertFrom(string|array|int|float|bool|null $raw): mixed; public function convertFrom(string|array|int|float|bool|null $raw): mixed;
public function convertTo(mixed $data): string|array|int|float|bool|null; public function convertTo(mixed $data): string|array|int|float|bool|null;
} }
+42 -56
View File
@@ -2,64 +2,52 @@
namespace sammo\DTO\Converter; namespace sammo\DTO\Converter;
use sammo\TimeUtil;
class DateTimeConverter implements Converter class DateTimeConverter implements Converter
{ {
protected bool $useFraction; const YMD_HIS = 'Y-m-d H:i:s';
const YMD_HISU = 'Y-m-d H:i:s.u';
protected \DateTimeZone $timeZoneOffset; protected \DateTimeZone $timeZoneOffset;
public function __construct(private array $types, ...$args) public function __construct(private array $types, string|int|float|null $timezone = null, public readonly string $datetimeFormat = self::YMD_HIS)
{ {
if(count($args) > 0) { $this->timeZoneOffset = static::extractDateTimeZone($timezone);
if(!is_bool($args[0])) { }
throw new \Exception('DateTimeConverter constructor argument must be boolean');
} private static function extractDateTimeZone(string|int|float|null $timezone): \DateTimeZone
$this->useFraction = $args[0]; {
} else { if ($timezone === null) {
$this->useFraction = false; return new \DateTimeZone(date_default_timezone_get());
} }
if(count($args) > 1) { if (is_int($timezone)) {
$args1 = $args[1]; if ($timezone < -12 || $timezone > 14) {
if(is_int($args1)){ throw new \InvalidArgumentException('TimeZone argument must be between -12 and 14');
if($args1 < -12 || $args1 > 14){
throw new \Exception('TimeZone argument must be between -12 and 14');
}
if($args1 > 0){
$offset = sprintf('+%02d00', $args1);
}
else{
$offset = sprintf('-%02d00', abs($args1));
}
$this->timeZoneOffset = new \DateTimeZone($offset);
} else if(is_float($args1)) {
if($args1 < -12 || $args1 > 14){
throw new \Exception('TimeZone argument must be between -12 and 14');
}
$isPositive = $args1 > 0;
$offset = abs($args1);
$hour = floor($offset);
$minute = floor(($offset - $hour) * 60);
if($isPositive){
$offset = sprintf('+%02d%02d', $hour, $minute);
} else {
$offset = sprintf('-%02d%02d', $hour, $minute);
}
$this->timeZoneOffset = new \DateTimeZone($offset);
} }
else if(is_string($args1)){ if ($timezone > 0) {
$this->timeZoneOffset = new \DateTimeZone($args1); $offset = sprintf('+%02d00', $timezone);
} else if($args1 instanceof \DateTimeZone){
$this->timeZoneOffset = $args1;
} else if($args1 === null){
$this->timeZoneOffset = new \DateTimeZone(\date_default_timezone_get());
} else { } else {
throw new \Exception('DateTimeConverter constructor argument must be int|string|\DateTimeZone'); $offset = sprintf('-%02d00', abs($timezone));
} }
return new \DateTimeZone($offset);
} }
else{
$this->timeZoneOffset = new \DateTimeZone(\date_default_timezone_get()); if (is_float($timezone)) {
if ($timezone < -12 || $timezone > 14) {
throw new \InvalidArgumentException('TimeZone argument must be between -12 and 14');
}
$isPositive = $timezone > 0;
$offset = abs($timezone);
$hour = floor($offset);
$minute = floor(($offset - $hour) * 60);
if ($isPositive) {
$offset = sprintf('+%02d%02d', $hour, $minute);
} else {
$offset = sprintf('-%02d%02d', $hour, $minute);
}
return new \DateTimeZone($offset);
} }
return new \DateTimeZone($timezone);
} }
public function convertFrom(string|array|int|float|bool|null $raw): mixed public function convertFrom(string|array|int|float|bool|null $raw): mixed
@@ -67,20 +55,18 @@ class DateTimeConverter implements Converter
if ($raw === null && array_search('null', $this->types, true) !== false) { if ($raw === null && array_search('null', $this->types, true) !== false) {
return null; return null;
} }
if (!is_string($raw)){ if (!is_string($raw)) {
throw new \Exception('DateTimeConverter can not convert non-string'); throw new \InvalidArgumentException('DateTimeConverter can not convert non-string');
} }
if (array_search('DateTimeImmutable', $this->types, true) !== false) { if (array_search('DateTimeImmutable', $this->types, true) !== false) {
$objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset); $objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset);
} } else if (array_search('DateTime', $this->types, true) !== false) {
else if (array_search('DateTime', $this->types, true) !== false) {
$objDateTime = new \DateTime($raw, $this->timeZoneOffset); $objDateTime = new \DateTime($raw, $this->timeZoneOffset);
} } else {
else{
$objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset); $objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset);
} }
if($objDateTime->getTimezone() !== $this->timeZoneOffset){ if ($objDateTime->getTimezone() !== $this->timeZoneOffset) {
$objDateTime = $objDateTime->setTimezone($this->timeZoneOffset); $objDateTime = $objDateTime->setTimezone($this->timeZoneOffset);
} }
return $objDateTime; return $objDateTime;
@@ -95,9 +81,9 @@ class DateTimeConverter implements Converter
throw new \Exception('DateTimeConverter can not convert non-DateTimeInterface'); throw new \Exception('DateTimeConverter can not convert non-DateTimeInterface');
} }
if($data->getTimezone() !== $this->timeZoneOffset){ if ($data->getTimezone() !== $this->timeZoneOffset) {
$data = \DateTimeImmutable::createFromInterface($data)->setTimezone($this->timeZoneOffset); $data = \DateTimeImmutable::createFromInterface($data)->setTimezone($this->timeZoneOffset);
} }
return TimeUtil::format($data, $this->useFraction); return $data->format($this->datetimeFormat);
} }
} }
+1 -1
View File
@@ -6,7 +6,7 @@ use sammo\DTO\DTO;
class DefaultConverter implements Converter class DefaultConverter implements Converter
{ {
public function __construct(private array $types, ...$args) public function __construct(private array $types)
{ {
} }
+7 -8
View File
@@ -5,20 +5,19 @@ namespace sammo\DTO\Converter;
class MapConverter implements Converter class MapConverter implements Converter
{ {
private Converter $itemConverter; private Converter $itemConverter;
public function __construct(private array $types, ...$args) public function __construct(private array $types, array $itemTypes, ?string $itemConverterClass = null, ...$args)
{ {
$itemTypes = array_shift($args);
if(!is_array($itemTypes)){ if(!is_array($itemTypes)){
throw new \Exception('itemTypes is not a array'); throw new \Exception('itemTypes is not a array');
} }
$itemConverterType = array_shift($args); $itemConverterClass = array_shift($args);
if($itemConverterType === null){ if($itemConverterClass === null){
$itemConverterType = DefaultConverter::class; $itemConverterClass = DefaultConverter::class;
} }
else if (!is_subclass_of($itemConverterType, Converter::class)) { else if (!is_subclass_of($itemConverterClass, Converter::class)) {
throw new \Exception("$itemConverterType is not a subclass of \sammo\DTO\Converter\Converter"); throw new \Exception("$itemConverterClass is not a subclass of \sammo\DTO\Converter\Converter");
} }
$this->itemConverter = new $itemConverterType($itemTypes, ...$args); $this->itemConverter = new $itemConverterClass($itemTypes, ...$args);
} }
public function convertFrom(string|array|int|float|bool|null $raw): mixed public function convertFrom(string|array|int|float|bool|null $raw): mixed
+16 -14
View File
@@ -4,7 +4,6 @@ namespace sammo\DTO;
use sammo\DTO\Attr\Convert; use sammo\DTO\Attr\Convert;
use sammo\DTO\Converter\DefaultConverter; use sammo\DTO\Converter\DefaultConverter;
use sammo\Json;
abstract class DTO abstract class DTO
{ {
@@ -18,7 +17,7 @@ abstract class DTO
$keyExists = fn (string|int $key) => array_key_exists($key, $array); $keyExists = fn (string|int $key) => array_key_exists($key, $array);
} }
$params = Util\Util::getConstructorParams($reflection); $params = Util\DTOUtil::getConstructorParams($reflection);
$args = []; $args = [];
$lazyMap = []; $lazyMap = [];
@@ -26,7 +25,7 @@ abstract class DTO
foreach ($reflection->getProperties( foreach ($reflection->getProperties(
\ReflectionProperty::IS_PUBLIC \ReflectionProperty::IS_PUBLIC
) as $property) { ) as $property) {
$attrs = Util\Util::getAttrs($property); $attrs = Util\DTOUtil::getAttrs($property);
$name = $property->getName(); $name = $property->getName();
$rawName = $name; $rawName = $name;
@@ -84,17 +83,17 @@ abstract class DTO
if (key_exists(Attr\JsonString::class, $attrs)) { if (key_exists(Attr\JsonString::class, $attrs)) {
$rawAttr = $attrs[Attr\JsonString::class]; $rawAttr = $attrs[Attr\JsonString::class];
$attr = new Attr\JsonString(...$rawAttr->getArguments()); $attr = new Attr\JsonString(...$rawAttr->getArguments());
$value = Json::decode($value); $value = json_decode($value, true);
} }
$propTypes = Util\Util::getPropTypes($property); $propTypes = Util\DTOUtil::getPropTypes($property);
if (key_exists(Attr\Convert::class, $attrs)) { if (key_exists(Attr\Convert::class, $attrs)) {
$rawAttr = $attrs[Attr\Convert::class]; $rawAttr = $attrs[Attr\Convert::class];
$converter = new Convert($propTypes, ...$rawAttr->getArguments()); $converter = new Convert(...$rawAttr->getArguments());
} else { } else {
$converter = new Convert($propTypes, DefaultConverter::class); $converter = new Convert(DefaultConverter::class);
} }
$value = $converter->convertFrom($value); $value = $converter->setType($propTypes)->convertFrom($value);
if($param !== null){ if($param !== null){
$args[$name] = $value; $args[$name] = $value;
@@ -117,7 +116,7 @@ abstract class DTO
$result = []; $result = [];
foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$value = $property->getValue($this); $value = $property->getValue($this);
$attrs = Util\Util::getAttrs($property); $attrs = Util\DTOUtil::getAttrs($property);
$name = $property->getName(); $name = $property->getName();
if(key_exists(Attr\Ignore::class, $attrs)){ if(key_exists(Attr\Ignore::class, $attrs)){
@@ -130,18 +129,21 @@ abstract class DTO
$name = $attr->rawName; $name = $attr->rawName;
} }
$propTypes = Util\Util::getPropTypes($property); $propTypes = Util\DTOUtil::getPropTypes($property);
if (key_exists(Attr\Convert::class, $attrs)) { if (key_exists(Attr\Convert::class, $attrs)) {
$converter = new Convert($propTypes, ...$attrs[Attr\Convert::class]->getArguments()); $converter = new Convert(...$attrs[Attr\Convert::class]->getArguments());
} else { } else {
$converter = new Convert($propTypes, DefaultConverter::class); $converter = new Convert(DefaultConverter::class);
} }
$value = $converter->convertTo($value); $value = $converter->setType($propTypes)->convertTo($value);
if (key_exists(Attr\JsonString::class, $attrs)) { if (key_exists(Attr\JsonString::class, $attrs)) {
$rawAttr = $attrs[Attr\JsonString::class]; $rawAttr = $attrs[Attr\JsonString::class];
$attr = new Attr\JsonString(...$rawAttr->getArguments()); $attr = new Attr\JsonString(...$rawAttr->getArguments());
$value = Json::encode($value, $attr->emptyItemIsArray ? JSON::EMPTY_ARRAY_IS_DICT : 0); if($value === [] && $attr->emptyItemIsArray){
$value = (object)null;
}
$value = json_encode($value, $attr->jsonFlag);
} }
if ($value === null && key_exists(Attr\NullIsUndefined::class, $attrs)) { if ($value === null && key_exists(Attr\NullIsUndefined::class, $attrs)) {
@@ -1,9 +1,7 @@
<?php <?php
namespace sammo\DTO\Util; namespace sammo\DTO\Util;
use Ds\Map; class DTOUtil{
class Util{
private function __construct() private function __construct()
{ {
+2 -2
View File
@@ -163,9 +163,9 @@ class TypeDateTime extends DTO
public function __construct( public function __construct(
#[Convert(DateTimeConverter::class)] #[Convert(DateTimeConverter::class)]
public \DateTimeImmutable $a, public \DateTimeImmutable $a,
#[Convert(DateTimeConverter::class, false, 9)] #[Convert(DateTimeConverter::class, 9, DateTimeConverter::YMD_HIS)]
public \DateTime $b, public \DateTime $b,
#[Convert(DateTimeConverter::class, true, 8)] #[Convert(DateTimeConverter::class, 8, DateTimeConverter::YMD_HISU)]
public \DateTimeImmutable|\DateTime $c, public \DateTimeImmutable|\DateTime $c,
#[Convert(DateTimeConverter::class)] #[Convert(DateTimeConverter::class)]
public \DateTimeInterface $d, public \DateTimeInterface $d,