Skip to content

Commit 8b357e7

Browse files
author
Яценко Андрей
committed
add recursive casting
1 parent 61d6a57 commit 8b357e7

File tree

3 files changed

+143
-6
lines changed

3 files changed

+143
-6
lines changed

README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,69 @@ object(\LoginDTO)[298]
7070
public string 'email' => string 'test' (length=4)
7171
public string 'password' => string '123456' (length=6)
7272
```
73+
74+
## :scroll: **Recursive casting**
75+
76+
```php
77+
namespace DTO;
78+
79+
class ProductDTO
80+
{
81+
public int $id;
82+
public string $name;
83+
}
84+
```
85+
```php
86+
namespace DTO;
87+
88+
class UserDTO
89+
{
90+
public int $id;
91+
public string $email;
92+
public string $balance;
93+
}
94+
```
95+
If you have a DTO array, then you must specify the full path to the class in phpdoc `array<\DTO\ProductDTO>`.
96+
97+
This is done in order to know exactly which instance you need to create. Because Reflection does not provide out-of-the-box functionality for getting a `use *`. In addition to the `use *`, an alias can be prescribed, and this will be more difficult to track.
98+
```php
99+
class PurchaseDTO
100+
{
101+
/** @var array<\DTO\ProductDTO> $products Product list */
102+
public array $products;
103+
104+
/** @var \DTO\UserDTO $user */
105+
public UserDTO $user;
106+
}
107+
```
108+
109+
```php
110+
$data = [
111+
'products' => [
112+
['id' => 1, 'name' => 'phone',],
113+
['id' => 2, 'name' => 'bread',],
114+
],
115+
'user' => ['id' => 1, 'email' => 'test@test.com', 'balance' => 10012.23,],
116+
];
117+
$purcheseDTO = plainToClass(PurchaseDTO::class, $data);
118+
var_dump($purcheseDTO);
119+
```
120+
121+
```php
122+
object(PurchaseDTO)[345]
123+
public array 'products' =>
124+
array (size=2)
125+
0 =>
126+
object(ProductDTO)[1558]
127+
public int 'id' => int 1
128+
public string 'name' => string 'phone' (length=5)
129+
1 =>
130+
object(ProductDTO)[1563]
131+
public int 'id' => int 2
132+
public string 'name' => string 'bread' (length=5)
133+
public UserDTO 'user' =>
134+
object(UserDTO)[1559]
135+
public int 'id' => int 1
136+
public string 'email' => string 'test@test.com' (length=13)
137+
public float 'balance' => float 10012.23
138+
```

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "yzen.dev/plain-to-class",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Class-transformer function to transform our object into a typed object",
55
"minimum-stability": "dev",
66
"prefer-stable": true,
@@ -21,7 +21,7 @@
2121
]
2222
},
2323
"require": {
24-
"php": "^5.5|^7.0|^8.0"
24+
"php": "7.4"
2525
},
2626
"support": {
2727
"issues": "https://github.com/yzen-dev/plain-to-class/issues",

plainToClass.php

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,88 @@
99
* @param $args
1010
*
1111
* @return T
12+
* @throws ReflectionException
1213
*/
1314
function plainToClass($class, $args)
1415
{
1516
$instance = new $class;
1617
if ($args !== null) {
17-
if (method_exists($class,'plainToClass')){
18+
if (method_exists($class, 'plainToClass')) {
1819
return $class::plainToClass($args);
1920
}
20-
foreach ($args as $key => $val) {
21-
if (property_exists($instance, $key)) {
22-
$instance->{$key} = $val;
21+
$refInstance = new ReflectionClass($class);
22+
if (is_object($args)) {
23+
$refArgsObject = new ReflectionObject($args);
24+
foreach ($refInstance->getProperties() as $item) {
25+
if ($refArgsObject->hasProperty($item->name)) {
26+
$scalarTypes = ['int', 'float', 'string', 'bool'];
27+
28+
$propertyClass = $refInstance->getProperty($item->name);
29+
$propertyClassType = $refInstance->getProperty($item->name)->getType()->getName();
30+
31+
$propertyArgs = $refArgsObject->getProperty($item->name);
32+
$propertyArgsType = getType($args->{$item->name});
33+
34+
## if scalar type
35+
if (in_array($propertyClassType, $scalarTypes) && in_array($propertyArgsType, $scalarTypes)) {
36+
$instance->{$item->name} = $args->{$item->name};
37+
continue;
38+
}
39+
40+
if ($propertyClassType === 'array' && $propertyArgsType === 'array') {
41+
if ($propertyClass->getDocComment()) {
42+
preg_match('/array<([a-zA-Z\d\\\]+)>/m', $propertyClass->getDocComment(), $docType);
43+
$docType = $docType[1] ?? null;
44+
45+
if ($docType && class_exists($docType)) {
46+
foreach ($args->{$item->name} as $el) {
47+
$instance->{$item->name}[] = plainToClass($docType, $el);
48+
}
49+
}
50+
}
51+
continue;
52+
}
53+
54+
if ($propertyClassType && class_exists($propertyClassType)) {
55+
$instance->{$item->name} = plainToClass($propertyClassType, $args->{$item->name});
56+
continue;
57+
}
58+
$instance->{$item->name} = $args->{$item->name};
59+
}
60+
}
61+
} else {
62+
foreach ($refInstance->getProperties() as $item) {
63+
if (array_key_exists($item->name, $args)) {
64+
$scalarTypes = ['int', 'float', 'string', 'bool'];
65+
66+
$propertyClass = $refInstance->getProperty($item->name);
67+
$propertyClassType = $refInstance->getProperty($item->name)->getType()->getName();
68+
## if scalar type
69+
if (in_array($propertyClassType, $scalarTypes)) {
70+
$instance->{$item->name} = $args[$item->name];
71+
continue;
72+
}
73+
74+
if ($propertyClassType === 'array') {
75+
if ($propertyClass->getDocComment()) {
76+
preg_match('/array<([a-zA-Z\d\\\]+)>/m', $propertyClass->getDocComment(), $docType);
77+
$docType = $docType[1] ?? null;
78+
79+
if ($docType && class_exists($docType)) {
80+
foreach ($args[$item->name] as $el) {
81+
$instance->{$item->name}[] = plainToClass($docType, $el);
82+
}
83+
}
84+
}
85+
continue;
86+
}
87+
88+
if ($propertyClassType && class_exists($propertyClassType)) {
89+
$instance->{$item->name} = plainToClass($propertyClassType, $args[$item->name]);
90+
continue;
91+
}
92+
$instance->{$item->name} = $args[$item->name];
93+
}
2394
}
2495
}
2596
}

0 commit comments

Comments
 (0)