dep,refac: DTO를 composer 외부로 이동

This commit is contained in:
2022-08-05 23:43:38 +09:00
parent f8cd58bcb3
commit da27ae2bad
27 changed files with 90 additions and 1264 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ return [
"target_php_version" => '8.1',
"minimum_target_php_version" => '8.1',
'backward_compatibility_checks ' => true,
'minimum_severity' => \Phan\Issue::SEVERITY_NORMAL,
'minimum_severity' => \Phan\Issue::SEVERITY_CRITICAL,
'file_list' => [
'f_config/config.php',
+2 -2
View File
@@ -26,8 +26,8 @@
"nette/caching": "^3.0",
"illuminate/database": "^9.12",
"illuminate/events": "^9.12",
"spatie/data-transfer-object": "^3.7",
"php-ds/php-ds": "^1.4"
"php-ds/php-ds": "^1.4",
"hided62/ldto": "^1.0"
},
"autoload": {
"psr-4": {
Generated
+56 -65
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "908099160e2765c04448fef3f35794c4",
"content-hash": "fe4387d1f2df3d3162c053a1d50a733a",
"packages": [
{
"name": "brandonwamboldt/utilphp",
@@ -537,6 +537,60 @@
},
"time": "2021-06-11T22:34:44+00:00"
},
{
"name": "hided62/ldto",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/hided62/ldto.git",
"reference": "4c05401406f8afc1636a90763c13884f8fff0d14"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hided62/ldto/zipball/4c05401406f8afc1636a90763c13884f8fff0d14",
"reference": "4c05401406f8afc1636a90763c13884f8fff0d14",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
"phan/phan": "^5.3",
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"LDTO\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Hide_D",
"email": "hided62@gmail.com",
"homepage": "https://hided.net",
"role": "Developer"
}
],
"description": "php lightweight dto library",
"homepage": "https://github.com/hided62/ldto",
"keywords": [
"data-transfer-object",
"dto",
"hided62",
"lightweight",
"serialize"
],
"support": {
"issues": "https://github.com/hided62/ldto/issues",
"source": "https://github.com/hided62/ldto/tree/v1.0.0"
},
"time": "2022-08-05T14:30:13+00:00"
},
{
"name": "illuminate/bus",
"version": "v9.12.2",
@@ -2238,69 +2292,6 @@
},
"time": "2021-06-29T04:31:23+00:00"
},
{
"name": "spatie/data-transfer-object",
"version": "3.7.3",
"source": {
"type": "git",
"url": "https://github.com/spatie/data-transfer-object.git",
"reference": "341f72c77e0fce40ea2e0dcb212cb54dc27bbe72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/data-transfer-object/zipball/341f72c77e0fce40ea2e0dcb212cb54dc27bbe72",
"reference": "341f72c77e0fce40ea2e0dcb212cb54dc27bbe72",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"illuminate/collections": "^8.36",
"jetbrains/phpstorm-attributes": "^1.0",
"larapack/dd": "^1.1",
"phpunit/phpunit": "^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\DataTransferObject\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brent Roose",
"email": "brent@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Data transfer objects with batteries included",
"homepage": "https://github.com/spatie/data-transfer-object",
"keywords": [
"data-transfer-object",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/data-transfer-object/issues",
"source": "https://github.com/spatie/data-transfer-object/tree/3.7.3"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2021-12-30T20:31:10+00:00"
},
{
"name": "symfony/console",
"version": "v6.0.8",
@@ -5337,5 +5328,5 @@
"platform-overrides": {
"php": "8.1"
},
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.3.0"
}
+6 -6
View File
@@ -2,13 +2,13 @@
namespace sammo\DTO;
use sammo\DTO\Attr\Convert;
use sammo\DTO\Attr\JsonString;
use sammo\DTO\Attr\NullIsUndefined;
use sammo\DTO\Attr\RawName;
use sammo\DTO\Converter\DateTimeConverter;
use LDTO\Attr\Convert;
use LDTO\Attr\JsonString;
use LDTO\Attr\NullIsUndefined;
use LDTO\Attr\RawName;
use LDTO\Converter\DateTimeConverter;
class AuctionBidItem extends DTO
class AuctionBidItem extends \LDTO\DTO
{
public function __construct(
#[NullIsUndefined]
+2 -2
View File
@@ -2,9 +2,9 @@
namespace sammo\DTO;
use sammo\DTO\Attr\NullIsUndefined;
use LDTO\Attr\NullIsUndefined;
class AuctionBidItemData extends DTO
class AuctionBidItemData extends \LDTO\DTO
{
public function __construct(
#[NullIsUndefined]
+6 -6
View File
@@ -2,15 +2,15 @@
namespace sammo\DTO;
use sammo\DTO\Attr\Convert;
use sammo\DTO\Attr\JsonString;
use sammo\DTO\Attr\NullIsUndefined;
use sammo\DTO\Attr\RawName;
use sammo\DTO\Converter\DateTimeConverter;
use LDTO\Attr\Convert;
use LDTO\Attr\JsonString;
use LDTO\Attr\NullIsUndefined;
use LDTO\Attr\RawName;
use LDTO\Converter\DateTimeConverter;
use sammo\Enums\AuctionType;
use sammo\Enums\ResourceType;
class AuctionInfo extends DTO
class AuctionInfo extends \LDTO\DTO
{
public function __construct(
#[NullIsUndefined]
+4 -4
View File
@@ -2,11 +2,11 @@
namespace sammo\DTO;
use sammo\DTO\Attr\Convert;
use sammo\DTO\Attr\NullIsUndefined;
use sammo\DTO\Converter\DateTimeConverter;
use LDTO\Attr\Convert;
use LDTO\Attr\NullIsUndefined;
use LDTO\Converter\DateTimeConverter;
class AuctionInfoDetail extends DTO
class AuctionInfoDetail extends \LDTO\DTO
{
public function __construct(
public string $title,
+3 -3
View File
@@ -2,13 +2,13 @@
namespace sammo\DTO;
use sammo\DTO\Attr\Convert;
use sammo\DTO\Converter\MapConverter;
use LDTO\Attr\Convert;
use LDTO\Converter\MapConverter;
use sammo\DTO\SelectItem;
//https://json2dto.atymic.dev/
class BettingInfo extends DTO
class BettingInfo extends \LDTO\DTO
{
public function __construct(
public int $id,
+3 -3
View File
@@ -2,10 +2,10 @@
namespace sammo\DTO;
use sammo\DTO\Attr\NullIsUndefined;
use sammo\DTO\Attr\RawName;
use LDTO\Attr\NullIsUndefined;
use LDTO\Attr\RawName;
class BettingItem extends DTO
class BettingItem extends \LDTO\DTO
{
public function __construct(
#[RawName('id')]
+3 -3
View File
@@ -2,10 +2,10 @@
namespace sammo\DTO;
use sammo\DTO\Attr\Convert;
use sammo\DTO\Converter\MapConverter;
use LDTO\Attr\Convert;
use LDTO\Converter\MapConverter;
class SelectItem extends DTO
class SelectItem extends \LDTO\DTO
{
public function __construct(
public string $title,
+3 -3
View File
@@ -2,10 +2,10 @@
namespace sammo\DTO;
use sammo\DTO\Attr\NullIsUndefined;
use sammo\DTO\Attr\RawName;
use LDTO\Attr\NullIsUndefined;
use LDTO\Attr\RawName;
class VoteComment extends DTO
class VoteComment extends \LDTO\DTO
{
public function __construct(
#[NullIsUndefined]
+1 -1
View File
@@ -2,7 +2,7 @@
namespace sammo\DTO;
class VoteInfo extends DTO
class VoteInfo extends \LDTO\DTO
{
public function __construct(
public int $id,
-44
View File
@@ -1,44 +0,0 @@
<?php
namespace sammo\DTO\Attr;
use sammo\DTO\Converter\Converter;
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_CLASS)]
class Convert
{
public Converter $converter;
public array $targetTypes;
public readonly array $args;
public function __construct(
public readonly string $converterType,
...$args
) {
if(!is_subclass_of($converterType, \sammo\DTO\Converter\Converter::class)){
throw new \Exception("$converterType is not a subclass of DTO\Converter\Converter");
}
$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, string $name): mixed
{
if($this->converter === null){
throw new \Exception("converter[{$name}] is not set");
}
return $this->converter->convertFrom($raw, $name);
}
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);
}
}
-16
View File
@@ -1,16 +0,0 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class DefaultValue
{
public function __construct(public readonly null|bool|int|float|string|array $defaultValue)
{
}
public function getDefaultValue(): mixed
{
return $this->defaultValue;
}
}
@@ -1,20 +0,0 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class DefaultValueGenerator
{
public function __construct(public readonly string $generator)
{
if (!is_callable($generator)) {
throw new \Exception("$generator is not a callable");
}
}
public function getDefaultValue(): mixed
{
$generator = $this->generator;
return $generator();
}
}
-11
View File
@@ -1,11 +0,0 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Ignore
{
public function __construct()
{
}
}
-13
View File
@@ -1,13 +0,0 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class JsonString
{
public function __construct(
public readonly bool $emptyItemIsArray = false,
public readonly int $jsonFlag = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
) {
}
}
-11
View File
@@ -1,11 +0,0 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class NullIsUndefined
{
public function __construct()
{
}
}
-11
View File
@@ -1,11 +0,0 @@
<?php
namespace sammo\DTO\Attr;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class RawName
{
public function __construct(public readonly string $rawName)
{
}
}
@@ -1,43 +0,0 @@
<?php
namespace sammo\DTO\Converter;
class ArrayConverter implements Converter
{
private Converter $itemConverter;
public function __construct(private array $types, array $itemTypes, ?string $itemConverterClass = null, ...$args)
{
if(!is_array($itemTypes)){
throw new \Exception('itemTypes is not a array');
}
if($itemConverterClass === null){
$itemConverterClass = DefaultConverter::class;
}
else if(!is_subclass_of($itemConverterClass, Converter::class)){
throw new \Exception("$itemConverterClass is not a subclass of \sammo\DTO\Converter\Converter");
}
$this->itemConverter = new $itemConverterClass($itemTypes, ...$args);
}
public function convertFrom(string|array|int|float|bool|null $raw, string $name): mixed
{
if ($raw === null && array_search('null', $this->types, true) !== false) {
return null;
}
if (!is_array($raw) || !array_is_list($raw)) {
throw new \Exception("value is not a array: $name");
}
return array_map(fn ($v) => $this->itemConverter->convertFrom($v, $name), $raw);
}
public function convertTo(mixed $data): string|array|int|float|bool|null
{
if ($data === null && array_search('null', $this->types) !== false) {
return null;
}
if (!is_array($data) || !array_is_list($data)) {
throw new \Exception('value is not a array');
}
return array_map(fn ($v) => $this->itemConverter->convertTo($v), $data);
}
}
-7
View File
@@ -1,7 +0,0 @@
<?php
namespace sammo\DTO\Converter;
interface Converter{
public function convertFrom(string|array|int|float|bool|null $raw, string $name): mixed;
public function convertTo(mixed $data): string|array|int|float|bool|null;
}
@@ -1,89 +0,0 @@
<?php
namespace sammo\DTO\Converter;
class DateTimeConverter implements Converter
{
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, string|int|float|null $timezone = null, public readonly string $datetimeFormat = self::YMD_HIS)
{
$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 (is_int($timezone)) {
if ($timezone < -12 || $timezone > 14) {
throw new \InvalidArgumentException('TimeZone argument must be between -12 and 14');
}
if ($timezone > 0) {
$offset = sprintf('+%02d00', $timezone);
} else {
$offset = sprintf('-%02d00', abs($timezone));
}
return new \DateTimeZone($offset);
}
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, string $name): mixed
{
if ($raw === null && array_search('null', $this->types, true) !== false) {
return null;
}
if (!is_string($raw)) {
throw new \InvalidArgumentException("DateTimeConverter can not convert non-string: {$name}");
}
if (array_search('DateTimeImmutable', $this->types, true) !== false) {
$objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset);
} else if (array_search('DateTime', $this->types, true) !== false) {
$objDateTime = new \DateTime($raw, $this->timeZoneOffset);
} else {
$objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset);
}
if ($objDateTime->getTimezone() !== $this->timeZoneOffset) {
$objDateTime = $objDateTime->setTimezone($this->timeZoneOffset);
}
return $objDateTime;
}
public function convertTo(mixed $data): string|array|int|float|bool|null
{
if ($data === null && array_search('null', $this->types, true) !== false) {
return null;
}
if (!$data instanceof \DateTimeInterface) {
throw new \Exception('DateTimeConverter can not convert non-DateTimeInterface');
}
if ($data->getTimezone() !== $this->timeZoneOffset) {
$data = \DateTimeImmutable::createFromInterface($data)->setTimezone($this->timeZoneOffset);
}
return $data->format($this->datetimeFormat);
}
}
@@ -1,147 +0,0 @@
<?php
namespace sammo\DTO\Converter;
use sammo\DTO\DTO;
class DefaultConverter implements Converter
{
public function __construct(private array $types)
{
}
public static function convertFromItem(string $type, $raw, string $name, bool &$success): mixed
{
$success = false;
if (is_subclass_of($type, \UnitEnum::class)) {
$enumType = new \ReflectionEnum($type);
if ($enumType->isBacked()) {
$enum = $enumType->getMethod('tryFrom')->invoke(null, $raw);
if ($enum === null) {
return null;
}
$success = true;
return $enum;
}
if (!$enumType->hasCase($raw)) {
return null;
}
$success = true;
return $enumType->getCase($raw)->getValue();
}
if (is_subclass_of($type, DTO::class)) {
try {
$class = new \ReflectionClass($type);
$obj = $class->getMethod('fromArray')->invoke(null, $raw);
$success = true;
return $obj;
} catch (\Throwable) {
}
return null;
}
if ($type === 'array') {
if (!is_array($raw)) {
return null;
}
if (!array_is_list($raw)) {
throw new \Exception("value is not a array: $name");
}
foreach ($raw as $value) {
if (is_int($value) || is_float($value) || is_string($value)) {
continue;
}
if (is_bool($value) || is_null($value)) {
continue;
}
throw new \Exception("DefaultConverter can not convert array: $name");
}
$success = true;
return $raw;
}
if($type === 'int' && is_int($raw)){
$success = true;
return $raw;
}
if($type === 'float' && is_float($raw)){
$success = true;
return $raw;
}
if($type === 'string' && is_string($raw)){
$success = true;
return $raw;
}
if($type === 'bool'){
if(is_bool($raw)){
$success = true;
return $raw;
}
if($raw === 0 || $raw === 1){
$success = true;
return $raw !== 0;
}
}
return null;
}
public function convertFrom(string|array|int|float|bool|null $raw, string $name): mixed
{
if ($raw === null && array_search('null', $this->types, true) !== false) {
return null;
}
foreach ($this->types as $type) {
$success = false;
$value = self::convertFromItem($type, $raw, $name, $success);
if ($success) {
return $value;
}
}
throw new \Exception('DefaultConverter can not convert '.gettype($raw).": $name as ".join('|', $this->types));
}
public function convertTo(mixed $data): string|array|int|float|bool|null
{
if ($data === null) {
return $data;
}
if ($data instanceof \UnitEnum) {
if ($data instanceof \BackedEnum) {
return $data->value;
}
return $data->name;
}
if ($data instanceof DTO) {
return $data->toArray();
}
if (is_array($data)) {
if (!array_is_list($data)) {
throw new \Exception('value is not a array');
}
foreach ($data as $value) {
if (is_int($value) || is_float($value) || is_string($value)) {
continue;
}
if (is_bool($value) || is_null($value)) {
continue;
}
throw new \Exception('DefaultConverter can not convert array');
}
}
return $data;
}
}
-53
View File
@@ -1,53 +0,0 @@
<?php
namespace sammo\DTO\Converter;
class MapConverter implements Converter
{
private Converter $itemConverter;
public function __construct(private array $types, array $itemTypes, ?string $itemConverterClass = null, ...$args)
{
if(!is_array($itemTypes)){
throw new \Exception('itemTypes is not a array');
}
$itemConverterClass = array_shift($args);
if($itemConverterClass === null){
$itemConverterClass = DefaultConverter::class;
}
else if (!is_subclass_of($itemConverterClass, Converter::class)) {
throw new \Exception("$itemConverterClass is not a subclass of \sammo\DTO\Converter\Converter");
}
$this->itemConverter = new $itemConverterClass($itemTypes, ...$args);
}
public function convertFrom(string|array|int|float|bool|null $raw, string $name): mixed
{
if ($raw === null && array_search('null', $this->types, true) !== false) {
return null;
}
if (!is_array($raw)) {
throw new \Exception("value is not a array: $name");
}
$result = [];
foreach ($raw as $key => $value) {
$result[$key] = $this->itemConverter->convertFrom($value, $name);
}
return $result;
}
public function convertTo(mixed $data): string|array|int|float|bool|null
{
if ($data === null && array_search('null', $this->types, true) !== false) {
return null;
}
if (!is_array($data) && !($data instanceof \Traversable)) {
throw new \Exception('value is not a array');
}
$result = [];
foreach ($data as $key => $value) {
$result[$key] = $this->itemConverter->convertTo($value);
}
return $result;
}
}
-164
View File
@@ -1,164 +0,0 @@
<?php
namespace sammo\DTO;
use Ds\Set;
use sammo\DTO\Attr\Convert;
use sammo\DTO\Converter\DefaultConverter;
abstract class DTO
{
public static function fromArray(\ArrayAccess|array $array): static
{
$reflection = new \ReflectionClass(static::class);
if ($array instanceof \ArrayAccess) {
$keyExists = fn (string|int $key) => $array->offsetExists($key);
} else {
$keyExists = fn (string|int $key) => array_key_exists($key, $array);
}
$params = Util\DTOUtil::getConstructorParams($reflection);
$args = [];
$lazyMap = [];
foreach ($reflection->getProperties(
\ReflectionProperty::IS_PUBLIC
) as $property) {
$attrs = Util\DTOUtil::getAttrs($property);
$name = $property->getName();
$rawName = $name;
$param = $params[$name] ?? null;
if(key_exists(Attr\Ignore::class, $attrs)){
if($param !== null){
throw new \Exception("Property {$name} is ignored but has a constructor parameter");
}
continue;
}
if (key_exists(Attr\DefaultValueGenerator::class, $attrs)){
/** @var Attr\DefaultValueGenerator */
$defaultValueSetter = $attrs[Attr\DefaultValueGenerator::class]->newInstance();
} else if (key_exists(Attr\DefaultValue::class, $attrs)) {
/** @var Attr\DefaultValue */
$defaultValueSetter = $attrs[Attr\DefaultValue::class]->newInstance();
}
else{
$defaultValueSetter = null;
}
if (key_exists(Attr\RawName::class, $attrs)) {
$rawAttr = $attrs[Attr\RawName::class];
$attr = new Attr\RawName(...$rawAttr->getArguments());
$rawName = $attr->rawName;
}
if (!$keyExists($rawName)) {
if ($param !== null && $param->isOptional()){
$defaultValue = $param->getDefaultValue();
}
else if ($property->hasDefaultValue()) {
$defaultValue = $property->getDefaultValue();
} else if($defaultValueSetter !== null){
$defaultValue = $defaultValueSetter->getDefaultValue();
} else if ($property->getType()->allowsNull()) {
$defaultValue = null;
} else {
throw new \Exception("Missing property: {$name}");
}
if($param !== null){
$args[$name] = $defaultValue;
}
else{
$lazyMap[$name] = $defaultValue;
}
continue;
}
$value = $array[$rawName];
if (key_exists(Attr\JsonString::class, $attrs)) {
$rawAttr = $attrs[Attr\JsonString::class];
$attr = new Attr\JsonString(...$rawAttr->getArguments());
$value = json_decode($value, true);
}
$propTypes = Util\DTOUtil::getPropTypes($property);
if (key_exists(Attr\Convert::class, $attrs)) {
$rawAttr = $attrs[Attr\Convert::class];
$converter = new Convert(...$rawAttr->getArguments());
} else {
$converter = new Convert(DefaultConverter::class);
}
$value = $converter->setType($propTypes)->convertFrom($value, $name);
if($param !== null){
$args[$name] = $value;
}
else{
$lazyMap[$name] = $value;
}
}
$object = $reflection->newInstanceArgs($args);
foreach($lazyMap as $name => $value){
$object->{$name} = $value;
}
return $object;
}
public function toArray(string ...$exceptKeys): array
{
$reflection = new \ReflectionClass($this::class);
$result = [];
$exceptKeySet = new Set($exceptKeys);
foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$value = $property->getValue($this);
$attrs = Util\DTOUtil::getAttrs($property);
$name = $property->getName();
if(key_exists(Attr\Ignore::class, $attrs)){
continue;
}
if($exceptKeySet->contains($name)){
continue;
}
if (key_exists(Attr\RawName::class, $attrs)) {
$rawAttr = $attrs[Attr\RawName::class];
$attr = new Attr\RawName(...$rawAttr->getArguments());
$name = $attr->rawName;
}
$propTypes = Util\DTOUtil::getPropTypes($property);
if (key_exists(Attr\Convert::class, $attrs)) {
$converter = new Convert(...$attrs[Attr\Convert::class]->getArguments());
} else {
$converter = new Convert(DefaultConverter::class);
}
$value = $converter->setType($propTypes)->convertTo($value);
if (key_exists(Attr\JsonString::class, $attrs)) {
$rawAttr = $attrs[Attr\JsonString::class];
$attr = new Attr\JsonString(...$rawAttr->getArguments());
if($value === [] && $attr->emptyItemIsArray){
$value = (object)null;
}
$value = json_encode($value, $attr->jsonFlag);
}
if ($value === null && key_exists(Attr\NullIsUndefined::class, $attrs)) {
continue;
}
$result[$name] = $value;
}
return $result;
}
}
-68
View File
@@ -1,68 +0,0 @@
<?php
namespace sammo\DTO\Util;
class DTOUtil{
private function __construct()
{
}
/**
* @return array<string,\ReflectionAttribute>
*/
public static function getAttrs(\ReflectionProperty $prop): array{
$result = [];
foreach($prop->getAttributes() as $attr){
$result[$attr->getName()] = $attr;
}
return $result;
}
/**
* @return array<string,\ReflectionParameter>
*/
public static function getConstructorParams(\ReflectionClass $class): array{
$constructor = $class->getConstructor();
if($constructor === null){
return [];
}
$result = [];
foreach($constructor->getParameters() as $param){
$result[$param->getName()] = $param;
}
return $result;
}
/**
* @return array<string>
*/
public static function getPropTypes(\ReflectionProperty $prop): array{
$result = [];
$type = $prop->getType();
if($type === null){
throw new \Exception("Property {$prop->getName()} has no type");
}
if($type->allowsNull()){
$result[] = 'null';
}
if($type instanceof \ReflectionIntersectionType){
throw new \Exception("Intersection types are not supported");
}
if($type instanceof \ReflectionNamedType){
$result[] = $type->getName();
return $result;
}
if($type instanceof \ReflectionUnionType){
foreach($type->getTypes() as $type){
$result[] = $type->getName();
}
return $result;
}
throw new \sammo\MustNotBeReachedException;
}
}
-468
View File
@@ -1,468 +0,0 @@
<?php
use sammo\DTO\Attr\Convert;
use sammo\DTO\Attr\DefaultValue;
use sammo\DTO\Attr\DefaultValueGenerator;
use sammo\DTO\Attr\Ignore;
use sammo\DTO\DTO;
use sammo\DTO\Attr\JsonString;
use sammo\DTO\Attr\NullIsUndefined;
use sammo\DTO\Attr\RawName;
use sammo\DTO\Converter\ArrayConverter;
use sammo\DTO\Converter\Converter;
use sammo\DTO\Converter\MapConverter;
use sammo\DTO\Converter\DateTimeConverter;
use sammo\Json;
class TypeA extends DTO
{
public function __construct(
public string $a,
public string $b,
public bool $c,
public ?int $d,
public float $e,
) {
}
}
class TypeBA extends DTO
{
public function __construct(
public int $ba1,
public int $ba2,
) {
}
}
class TypeB extends DTO
{
public function __construct(
public string $a,
public TypeBA $ba,
) {
}
}
class TypeC extends DTO
{
public function __construct(
public string $a,
#[JsonString]
public TypeBA $ba,
) {
}
}
enum EnumA
{
case A;
case B;
case cc;
}
class TypeD extends DTO
{
public function __construct(
public ?string $a,
public EnumA $b,
) {
}
}
class TypeE extends DTO
{
public function __construct(
public ?string $a,
#[NullIsUndefined]
public ?string $b,
) {
}
}
class ConverterDouble implements Converter
{
public function __construct(array $types, ...$args)
{
}
public function convertFrom(string|array|int|float|bool|null $raw, string $name): mixed
{
return $raw * 2;
}
public function convertTo(mixed $data): string|array|int|float|bool|null
{
return $data / 2;
}
}
class TypeF extends DTO
{
public function __construct(
public ?string $a,
#[Convert(ConverterDouble::class)]
public int $b,
) {
}
}
class TypeArr extends DTO
{
public function __construct(
public array $a,
public array $b,
) {
}
}
class TypeArrConverter extends DTO
{
public function __construct(
public array $a,
#[Convert(ArrayConverter::class, ['string'])]
public array $b,
) {
}
}
class TypeMap extends DTO
{
public function __construct(
public int $a,
#[Convert(MapConverter::class, ['null', 'string'])]
public array $b,
) {
}
}
class TypeNestedMap extends DTO
{
public function __construct(
public int $a,
#[Convert(MapConverter::class, ['null', TypeBA::class])]
public array $b,
) {
}
}
class TypeRawName extends DTO
{
public function __construct(
#[RawName('arg_name')]
public int $argName,
#[RawName('vID')]
public int $vID,
) {
}
}
class TypeDateTime extends DTO
{
public function __construct(
#[Convert(DateTimeConverter::class)]
public \DateTimeImmutable $a,
#[Convert(DateTimeConverter::class, 9, DateTimeConverter::YMD_HIS)]
public \DateTime $b,
#[Convert(DateTimeConverter::class, 8, DateTimeConverter::YMD_HISU)]
public \DateTimeImmutable|\DateTime $c,
#[Convert(DateTimeConverter::class)]
public \DateTimeInterface $d,
) {
}
}
function returnDateTime()
{
return new \DateTimeImmutable('2022-04-01 00:00:00');
}
class TypeDefaultValue extends DTO
{
public bool $g = true;
public function __construct(
public ?string $a,
#[DefaultValue(false)]
public bool $b,
public int $c,
#[DefaultValue([1,2,3])]
public array $e,
#[DefaultValueGenerator('returnDateTime')]
#[Convert(DateTimeConverter::class)]
public \DateTimeInterface $d,
public int $f = 111, //contstruct의 뒤에서 default 값이 설정될 경우에만
) {
}
}
class TypeIgnore extends DTO
{
#[Ignore]
public int $c = 100;
#[Ignore]
public $d;
#[Ignore]
public $e;
public function __construct(
public int $a,
public int $b,
) {
$this->d = $a * 2;
}
}
class DTOTest extends PHPUnit\Framework\TestCase
{
public function testBasic()
{
$rawType = [
'a' => '123',
'b' => 'aa',
'c' => false,
'd' => null,
'e' => 123.123,
];
$obj = TypeA::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testNested()
{
$rawType = [
'a' => '123',
'ba' => [
'ba1' => 3,
'ba2' => 4,
]
];
$obj = TypeB::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testNestedJson()
{
$x = Json::encode([
'ba1' => 3,
'ba2' => 4,
]);
$rawType = [
'a' => '123',
'ba' => $x
];
$obj = TypeC::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testEnum()
{
$rawType = [
'a' => '123',
'b' => 'B',
];
$obj = TypeD::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testNull()
{
$rawType = [
'a' => null,
];
$obj = TypeE::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testConverter()
{
$rawType = [
'a' => '123',
'b' => 123,
];
$obj = TypeF::fromArray($rawType);
$this->assertEquals($obj->b, 246);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testNullIsUndefined()
{
$rawType = [
'a' => null,
];
$obj = TypeE::fromArray($rawType);
$this->assertEquals($obj->a, null);
$this->assertEquals($obj->b, null);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testArr()
{
$rawType = [
'a' => [1, 2, 3],
'b' => [4, 5, 6],
];
$obj = TypeArr::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testArrConverterErr()
{
$this->expectException(Exception::class);
$rawType = [
'a' => [1, 2, 3],
'b' => [4, 5, 6],
];
$obj = TypeArrConverter::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testArrConverterErr2()
{
$this->expectException(Exception::class);
$rawType = [
'a' => [1, 2, 3],
'b' => [4 => 6, 5 => 2, 6 => 3],
];
$obj = TypeArrConverter::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testArrConverter()
{
$rawType = [
'a' => [1, 2, 3],
'b' => ['1', '2', '3'],
];
$obj = TypeArrConverter::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testMap()
{
$rawType = [
'a' => 1,
'b' => [
'1' => '1',
'2' => null,
'ba' => '3',
],
];
$obj = TypeMap::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testNestedMap()
{
$rawType = [
'a' => 1,
'b' => [
'aaa' => [
'ba1' => 1,
'ba2' => 2,
],
'xed' => null,
'ccc' => [
'ba1' => 3,
'ba2' => 4,
],
],
];
$obj = TypeNestedMap::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testRawName()
{
$rawType = [
'arg_name' => 1,
'vID' => 2,
];
$obj = TypeRawName::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
public function testDateTime()
{
$rawType = [
'a' => '2022-01-01 10:11:22',
'b' => '2022-02-01T12:34:56.1234+09:00',
'c' => '2022-03-01T00:00:00.1234+09:00',
'd' => '2022-04-01',
];
$testValue = [
'a' => '2022-01-01 10:11:22',
'b' => '2022-02-01 12:34:56',
'c' => '2022-02-28 23:00:00.123400',
'd' => '2022-04-01 00:00:00',
];
$obj = TypeDateTime::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($testValue, $testType);
}
public function testDefaultValue()
{
$rawType = [
'c' => 3,
];
$testValue = [
'g' => true,
'a' => null,
'b' => false,
'c' => 3,
'e' => [1, 2, 3],
'd' => '2022-04-01 00:00:00',
'f' => 111,
];
$obj = TypeDefaultValue::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($testValue, $testType);
}
public function testIgnore(){
$rawType = [
'a' => 1,
'b' => 2,
];
$obj = TypeIgnore::fromArray($rawType);
$testType = $obj->toArray();
$this->assertEquals($rawType, $testType);
}
}