Skip to content

Commit dd309e5

Browse files
committed
Added ability to create empty data of dto
1 parent f4c2735 commit dd309e5

File tree

11 files changed

+349
-17
lines changed

11 files changed

+349
-17
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@
2929
},
3030
"scripts": {
3131
"ci-test": "XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite=ci --configuration phpunit.xml",
32-
"phpstan": "vendor/bin/phpstan analyse --configuration phpstan.neon"
32+
"phpstan": "vendor/bin/phpstan analyse --configuration phpstan.neon --memory-limit=256M"
3333
}
3434
}

src/Concerns/BaseData.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ trait BaseData
1616
{
1717
use HasNormalizers;
1818

19+
/**
20+
* @throws DataCreationException
21+
*/
1922
final public static function create(mixed ...$args): static
2023
{
2124
if (array_any(
@@ -124,13 +127,7 @@ protected static function instanceWithConstructorCallFrom(ClassContext $context,
124127
$args[$paramName] = $propertyContext->deserializeFrom($value);
125128
}
126129

127-
$instance = $context->newInstanceWithConstructorCall(...$args);
128-
129-
if (!$instance instanceof static) {
130-
throw new DeserializeException('Could not create instance of ' . static::class);
131-
}
132-
133-
return $instance;
130+
return $context->newInstanceWithConstructorCall(...$args);
134131
}
135132

136133
/**

src/Concerns/EmptyData.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Nuxtifyts\PhpDto\Concerns;
4+
5+
use Nuxtifyts\PhpDto\Contexts\ClassContext;
6+
use Nuxtifyts\PhpDto\Exceptions\DataCreationException;
7+
use ReflectionClass;
8+
use Throwable;
9+
10+
trait EmptyData
11+
{
12+
/**
13+
* @throws DataCreationException
14+
*/
15+
public static function empty(): static
16+
{
17+
try {
18+
/** @var ClassContext<static> $classContext */
19+
$classContext = ClassContext::getInstance(new ReflectionClass(static::class));
20+
21+
return $classContext->emptyValue();
22+
} catch (Throwable $t) {
23+
throw DataCreationException::unableToCreateEmptyInstance(static::class, $t);
24+
}
25+
}
26+
}

src/Contexts/ClassContext.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
namespace Nuxtifyts\PhpDto\Contexts;
44

5+
use Nuxtifyts\PhpDto\Data;
6+
use Nuxtifyts\PhpDto\Exceptions\DataCreationException;
57
use Nuxtifyts\PhpDto\Exceptions\UnsupportedTypeException;
6-
use ReflectionClass;
78
use ReflectionException;
89
use ReflectionParameter;
10+
use ReflectionClass;
911

1012
/**
11-
* @template T of object
13+
* @template T of Data
1214
*/
1315
class ClassContext
1416
{
@@ -101,9 +103,36 @@ public function newInstanceWithoutConstructor(): mixed
101103

102104
/**
103105
* @throws ReflectionException
106+
*
107+
* @return T
104108
*/
105109
public function newInstanceWithConstructorCall(mixed ...$args): mixed
106110
{
107111
return $this->reflection->newInstance(...$args);
108112
}
113+
114+
/**
115+
* @return T
116+
*
117+
* @throws ReflectionException
118+
* @throws UnsupportedTypeException
119+
* @throws DataCreationException
120+
*/
121+
public function emptyValue(): mixed
122+
{
123+
/** @var array<string, mixed> $args */
124+
$args = [];
125+
126+
foreach ($this->constructorParams as $paramName) {
127+
$propertyContext = $this->properties[$paramName] ?? null;
128+
129+
if (!$propertyContext) {
130+
throw DataCreationException::invalidProperty();
131+
}
132+
133+
$args[$paramName] = $propertyContext->emptyValue();
134+
}
135+
136+
return $this->newInstanceWithConstructorCall(...$args);
137+
}
109138
}

src/Contexts/PropertyContext.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
namespace Nuxtifyts\PhpDto\Contexts;
44

5+
use BackedEnum;
6+
use Nuxtifyts\PhpDto\Exceptions\DataCreationException;
7+
use UnitEnum;
58
use Nuxtifyts\PhpDto\Attributes\Property\Aliases;
69
use Nuxtifyts\PhpDto\Attributes\Property\CipherTarget;
710
use Nuxtifyts\PhpDto\Attributes\Property\Computed;
811
use Nuxtifyts\PhpDto\Attributes\Property\DefaultsTo;
912
use Nuxtifyts\PhpDto\Attributes\Property\WithRefiner;
13+
use Nuxtifyts\PhpDto\Data;
1014
use Nuxtifyts\PhpDto\DataCiphers\CipherConfig;
1115
use Nuxtifyts\PhpDto\DataRefiners\DataRefiner;
1216
use Nuxtifyts\PhpDto\Enums\Property\Type;
@@ -18,8 +22,12 @@
1822
use Nuxtifyts\PhpDto\Serializers\Serializer;
1923
use Nuxtifyts\PhpDto\Support\Traits\HasSerializers;
2024
use Nuxtifyts\PhpDto\Support\Traits\HasTypes;
25+
use DateTimeInterface;
26+
use ReflectionEnum;
27+
use ReflectionException;
2128
use ReflectionProperty;
2229
use ReflectionAttribute;
30+
use ReflectionClass;
2331
use Exception;
2432

2533
class PropertyContext
@@ -225,4 +233,61 @@ public function serializeFrom(object $object): array
225233
throw new SerializeException('Could not serialize value for property: ' . $this->propertyName);
226234
}
227235
}
236+
237+
/**
238+
* @throws UnsupportedTypeException
239+
* @throws ReflectionException
240+
* @throws DataCreationException
241+
*/
242+
public function emptyValue(): mixed
243+
{
244+
if ($this->isNullable) {
245+
return null;
246+
}
247+
248+
if (! $typeContext = $this->typeContexts[0] ?? null) {
249+
throw UnsupportedTypeException::emptyType();
250+
}
251+
252+
switch (true) {
253+
case $typeContext->type === Type::STRING:
254+
return '';
255+
256+
case $typeContext->type === Type::INT:
257+
return 0;
258+
259+
case $typeContext->type === Type::FLOAT:
260+
return 0.0;
261+
262+
case $typeContext->type === Type::BOOLEAN:
263+
return false;
264+
265+
case $typeContext->type === Type::ARRAY:
266+
return [];
267+
268+
case $typeContext->type === Type::DATA:
269+
/** @var null|ReflectionClass<Data> $reflection */
270+
$reflection = $typeContext->reflection;
271+
272+
return !$reflection
273+
? throw UnsupportedTypeException::invalidReflection()
274+
: ClassContext::getInstance($reflection)->emptyValue();
275+
276+
case $typeContext->type === Type::BACKED_ENUM:
277+
/** @var null|ReflectionEnum<UnitEnum|BackedEnum> $reflection */
278+
$reflection = $typeContext->reflection;
279+
280+
return $reflection instanceof ReflectionEnum && $reflection->isBacked()
281+
? $reflection->getCases()[0]->getValue()
282+
: throw UnsupportedTypeException::invalidReflection();
283+
284+
default:
285+
/** @var null|DateTimeInterface $dateTime */
286+
$dateTime = $typeContext->reflection?->newInstance();
287+
288+
return $dateTime instanceof DateTimeInterface
289+
? $dateTime
290+
: throw UnsupportedTypeException::invalidReflection();
291+
}
292+
}
228293
}

src/Contexts/TypeContext.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public static function getInstances(PropertyContext $property): array
109109
);
110110
break;
111111
default:
112-
throw UnsupportedTypeException::from($type);
112+
throw UnsupportedTypeException::unknownType($type);
113113
}
114114
}
115115

src/Contracts/EmptyData.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
namespace Nuxtifyts\PhpDto\Contracts;
44

5+
use Nuxtifyts\PhpDto\Exceptions\DataCreationException;
6+
57
interface EmptyData
68
{
9+
/**
10+
* @throws DataCreationException
11+
*/
712
public static function empty(): static;
813
}

src/Data.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
namespace Nuxtifyts\PhpDto;
44

55
use Nuxtifyts\PhpDto\Contracts\BaseData as BaseDataContract;
6+
use Nuxtifyts\PhpDto\Contracts\EmptyData as EmptyDataContract;
67
use Nuxtifyts\PhpDto\Concerns\BaseData;
8+
use Nuxtifyts\PhpDto\Concerns\EmptyData;
79
use Nuxtifyts\PhpDto\Exceptions\SerializeException;
810

9-
abstract readonly class Data implements BaseDataContract
11+
abstract readonly class Data implements
12+
BaseDataContract,
13+
EmptyDataContract
1014
{
1115
use BaseData;
16+
use EmptyData;
1217

1318
/**
1419
* @return array<string, mixed>

src/Exceptions/DataCreationException.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class DataCreationException extends Exception
99
{
1010
protected const int UNABLE_TO_CREATE_INSTANCE = 0;
1111
protected const int INVALID_PROPERTY = 1;
12+
protected const int UNABLE_TO_CREATE_EMPTY_INSTANCE = 2;
1213

1314
public static function unableToCreateInstance(
1415
string $class,
@@ -28,4 +29,15 @@ public static function invalidProperty(): self
2829
code: self::INVALID_PROPERTY
2930
);
3031
}
32+
33+
public static function unableToCreateEmptyInstance(
34+
string $class,
35+
?Throwable $previous = null
36+
): self {
37+
return new self(
38+
message: "Unable to create empty instance of class {$class}",
39+
code: self::UNABLE_TO_CREATE_EMPTY_INSTANCE,
40+
previous: $previous
41+
);
42+
}
3143
}

src/Exceptions/UnsupportedTypeException.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,36 @@
66

77
class UnsupportedTypeException extends Exception
88
{
9-
final protected function __construct(string $message)
9+
protected const int UNKNOWN_TYPE = 0;
10+
protected const int EMPTY_TYPE = 1;
11+
protected const int INVALID_REFLECTION = 2;
12+
protected const int INVALID_TYPE = 3;
13+
14+
public static function unknownType(?string $type = null): self
15+
{
16+
return new self(
17+
'Unknown type' . ($type ? " '{$type}'" : ''),
18+
);
19+
}
20+
21+
public static function emptyType(): self
1022
{
11-
parent::__construct($message);
23+
return new self(
24+
'Got empty type',
25+
);
26+
}
27+
28+
public static function invalidReflection(): self
29+
{
30+
return new self(
31+
'Invalid reflection for type',
32+
);
1233
}
1334

14-
public static function from(string $type): static
35+
public static function invalidType(): self
1536
{
16-
return new static(
17-
"Unknown type '{$type}'"
37+
return new self(
38+
'Invalid type',
1839
);
1940
}
2041
}

0 commit comments

Comments
 (0)