From 35b178fe3cdb2067c546c2c6ba64963c8deafc82 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sun, 29 May 2022 00:44:28 +0900 Subject: [PATCH] =?UTF-8?q?refac:=20DTO=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20Converter=20-=20Constructor=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EA=B2=BD=EA=B3=A0=20=EC=88=98=EC=A0=95=20-=20JsonS?= =?UTF-8?q?tring=20=EA=B4=80=EB=A0=A8=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0,=20jsonFlag=20=EC=A7=80=EC=A0=95=20-=20DateT?= =?UTF-8?q?imeConveter=EC=97=90=EC=84=9C=20time=20format=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sammo/DTO/Attr/Convert.php | 20 +++- src/sammo/DTO/Attr/JsonString.php | 6 +- src/sammo/DTO/Converter/ArrayConverter.php | 14 ++- src/sammo/DTO/Converter/Converter.php | 1 - src/sammo/DTO/Converter/DateTimeConverter.php | 98 ++++++++----------- src/sammo/DTO/Converter/DefaultConverter.php | 2 +- src/sammo/DTO/Converter/MapConverter.php | 15 ++- src/sammo/DTO/DTO.php | 30 +++--- src/sammo/DTO/Util/{Util.php => DTOUtil.php} | 4 +- tests/DTOTest.php | 4 +- 10 files changed, 96 insertions(+), 98 deletions(-) rename src/sammo/DTO/Util/{Util.php => DTOUtil.php} (98%) diff --git a/src/sammo/DTO/Attr/Convert.php b/src/sammo/DTO/Attr/Convert.php index 1ce47d23..ce65f149 100644 --- a/src/sammo/DTO/Attr/Convert.php +++ b/src/sammo/DTO/Attr/Convert.php @@ -8,23 +8,37 @@ use sammo\DTO\Converter\Converter; class Convert { public Converter $converter; + public array $targetTypes; + public readonly array $args; public function __construct( - public readonly array $targetTypes, public readonly string $converterType, ...$args ) { 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 { + if($this->converter === null){ + throw new \Exception('converter is not set'); + } return $this->converter->convertFrom($raw); } 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); } } diff --git a/src/sammo/DTO/Attr/JsonString.php b/src/sammo/DTO/Attr/JsonString.php index 55c12516..16390295 100644 --- a/src/sammo/DTO/Attr/JsonString.php +++ b/src/sammo/DTO/Attr/JsonString.php @@ -5,7 +5,9 @@ namespace sammo\DTO\Attr; #[\Attribute(\Attribute::TARGET_PROPERTY)] 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 + ) { } } diff --git a/src/sammo/DTO/Converter/ArrayConverter.php b/src/sammo/DTO/Converter/ArrayConverter.php index 080d2c57..070d32f2 100644 --- a/src/sammo/DTO/Converter/ArrayConverter.php +++ b/src/sammo/DTO/Converter/ArrayConverter.php @@ -5,20 +5,18 @@ namespace sammo\DTO\Converter; class ArrayConverter implements Converter { 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)){ throw new \Exception('itemTypes is not a array'); } - $itemConverterType = array_shift($args); - if($itemConverterType === null){ - $itemConverterType = DefaultConverter::class; + if($itemConverterClass === null){ + $itemConverterClass = DefaultConverter::class; } - else if(!is_subclass_of($itemConverterType, Converter::class)){ - throw new \Exception("$itemConverterType is not a subclass of \sammo\DTO\Converter\Converter"); + else if(!is_subclass_of($itemConverterClass, Converter::class)){ + 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 diff --git a/src/sammo/DTO/Converter/Converter.php b/src/sammo/DTO/Converter/Converter.php index 6cd35aa0..2a354bbb 100644 --- a/src/sammo/DTO/Converter/Converter.php +++ b/src/sammo/DTO/Converter/Converter.php @@ -2,7 +2,6 @@ namespace sammo\DTO\Converter; interface Converter{ - public function __construct(array $types, ...$args); public function convertFrom(string|array|int|float|bool|null $raw): mixed; public function convertTo(mixed $data): string|array|int|float|bool|null; } \ No newline at end of file diff --git a/src/sammo/DTO/Converter/DateTimeConverter.php b/src/sammo/DTO/Converter/DateTimeConverter.php index 4e66dc4c..d3e1e2a1 100644 --- a/src/sammo/DTO/Converter/DateTimeConverter.php +++ b/src/sammo/DTO/Converter/DateTimeConverter.php @@ -2,64 +2,52 @@ namespace sammo\DTO\Converter; -use sammo\TimeUtil; - 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; - 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) { - if(!is_bool($args[0])) { - throw new \Exception('DateTimeConverter constructor argument must be boolean'); - } - $this->useFraction = $args[0]; - } else { - $this->useFraction = false; + $this->timeZoneOffset = static::extractDateTimeZone($timezone); + } + + private static function extractDateTimeZone(string|int|float|null $timezone): \DateTimeZone + { + if ($timezone === null) { + return new \DateTimeZone(date_default_timezone_get()); } - if(count($args) > 1) { - $args1 = $args[1]; - if(is_int($args1)){ - 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); + if (is_int($timezone)) { + if ($timezone < -12 || $timezone > 14) { + throw new \InvalidArgumentException('TimeZone argument must be between -12 and 14'); } - else if(is_string($args1)){ - $this->timeZoneOffset = new \DateTimeZone($args1); - } else if($args1 instanceof \DateTimeZone){ - $this->timeZoneOffset = $args1; - } else if($args1 === null){ - $this->timeZoneOffset = new \DateTimeZone(\date_default_timezone_get()); + if ($timezone > 0) { + $offset = sprintf('+%02d00', $timezone); } 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 @@ -67,20 +55,18 @@ class DateTimeConverter implements Converter if ($raw === null && array_search('null', $this->types, true) !== false) { return null; } - if (!is_string($raw)){ - throw new \Exception('DateTimeConverter can not convert non-string'); + if (!is_string($raw)) { + throw new \InvalidArgumentException('DateTimeConverter can not convert non-string'); } if (array_search('DateTimeImmutable', $this->types, true) !== false) { $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); - } - else{ + } else { $objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset); } - if($objDateTime->getTimezone() !== $this->timeZoneOffset){ + if ($objDateTime->getTimezone() !== $this->timeZoneOffset) { $objDateTime = $objDateTime->setTimezone($this->timeZoneOffset); } return $objDateTime; @@ -95,9 +81,9 @@ class DateTimeConverter implements Converter 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); } - return TimeUtil::format($data, $this->useFraction); + return $data->format($this->datetimeFormat); } } diff --git a/src/sammo/DTO/Converter/DefaultConverter.php b/src/sammo/DTO/Converter/DefaultConverter.php index b38a0afc..49011ea5 100644 --- a/src/sammo/DTO/Converter/DefaultConverter.php +++ b/src/sammo/DTO/Converter/DefaultConverter.php @@ -6,7 +6,7 @@ use sammo\DTO\DTO; class DefaultConverter implements Converter { - public function __construct(private array $types, ...$args) + public function __construct(private array $types) { } diff --git a/src/sammo/DTO/Converter/MapConverter.php b/src/sammo/DTO/Converter/MapConverter.php index 287d0665..49227114 100644 --- a/src/sammo/DTO/Converter/MapConverter.php +++ b/src/sammo/DTO/Converter/MapConverter.php @@ -5,20 +5,19 @@ namespace sammo\DTO\Converter; class MapConverter implements Converter { 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)){ throw new \Exception('itemTypes is not a array'); } - $itemConverterType = array_shift($args); - if($itemConverterType === null){ - $itemConverterType = DefaultConverter::class; + $itemConverterClass = array_shift($args); + if($itemConverterClass === null){ + $itemConverterClass = DefaultConverter::class; } - else if (!is_subclass_of($itemConverterType, Converter::class)) { - throw new \Exception("$itemConverterType is not a subclass of \sammo\DTO\Converter\Converter"); + else if (!is_subclass_of($itemConverterClass, Converter::class)) { + 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 diff --git a/src/sammo/DTO/DTO.php b/src/sammo/DTO/DTO.php index 3dfa4bc5..1dfb3dd2 100644 --- a/src/sammo/DTO/DTO.php +++ b/src/sammo/DTO/DTO.php @@ -4,7 +4,6 @@ namespace sammo\DTO; use sammo\DTO\Attr\Convert; use sammo\DTO\Converter\DefaultConverter; -use sammo\Json; abstract class DTO { @@ -18,7 +17,7 @@ abstract class DTO $keyExists = fn (string|int $key) => array_key_exists($key, $array); } - $params = Util\Util::getConstructorParams($reflection); + $params = Util\DTOUtil::getConstructorParams($reflection); $args = []; $lazyMap = []; @@ -26,7 +25,7 @@ abstract class DTO foreach ($reflection->getProperties( \ReflectionProperty::IS_PUBLIC ) as $property) { - $attrs = Util\Util::getAttrs($property); + $attrs = Util\DTOUtil::getAttrs($property); $name = $property->getName(); $rawName = $name; @@ -84,17 +83,17 @@ abstract class DTO if (key_exists(Attr\JsonString::class, $attrs)) { $rawAttr = $attrs[Attr\JsonString::class]; $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)) { $rawAttr = $attrs[Attr\Convert::class]; - $converter = new Convert($propTypes, ...$rawAttr->getArguments()); + $converter = new Convert(...$rawAttr->getArguments()); } 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){ $args[$name] = $value; @@ -117,7 +116,7 @@ abstract class DTO $result = []; foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $value = $property->getValue($this); - $attrs = Util\Util::getAttrs($property); + $attrs = Util\DTOUtil::getAttrs($property); $name = $property->getName(); if(key_exists(Attr\Ignore::class, $attrs)){ @@ -130,18 +129,21 @@ abstract class DTO $name = $attr->rawName; } - $propTypes = Util\Util::getPropTypes($property); + $propTypes = Util\DTOUtil::getPropTypes($property); 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 { - $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)) { $rawAttr = $attrs[Attr\JsonString::class]; $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)) { diff --git a/src/sammo/DTO/Util/Util.php b/src/sammo/DTO/Util/DTOUtil.php similarity index 98% rename from src/sammo/DTO/Util/Util.php rename to src/sammo/DTO/Util/DTOUtil.php index caa1c10b..46bdb281 100644 --- a/src/sammo/DTO/Util/Util.php +++ b/src/sammo/DTO/Util/DTOUtil.php @@ -1,9 +1,7 @@