Skip to content

Commit 906ed87

Browse files
committed
Fixes and tests
1 parent e8aba3c commit 906ed87

20 files changed

+360
-189
lines changed

.idea/.gitignore

Lines changed: 0 additions & 8 deletions
This file was deleted.

.idea/array-to-class-mapper.iml

Lines changed: 0 additions & 8 deletions
This file was deleted.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

.idea/misc.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

.idea/modules.xml

Lines changed: 0 additions & 8 deletions
This file was deleted.

.idea/php.xml

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/DocBlock/DocBlockParser.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,38 @@ public function getType(string $docblock): ?DocblockType
1616
return null;
1717
}
1818

19-
$isSingle = strpos($matches[1], '[]') !== false;
20-
$name = str_replace(['[', ']'], '', $matches[1]);
19+
$isSingle = strpos($matches[1], '[]') === false;
20+
$types = explode('|', str_replace(['[', ']'], '', $matches[1]));
21+
$isNullable = false;
2122

22-
return new DocblockType($isSingle, $name);
23+
foreach ($types as $index => $type) {
24+
$type = trim($type);
25+
if ($type === 'null') {
26+
$isNullable = true;
27+
unset($types[$index]);
28+
}
29+
else {
30+
$types[$index] = $type;
31+
}
32+
}
33+
34+
$chosenType = reset($types);
35+
$isBuiltIn = $this->isBuiltInType($chosenType);
36+
37+
return new DocblockType($chosenType, $isSingle, $isBuiltIn, $isNullable);
38+
}
39+
40+
private function isBuiltInType(string $type): bool
41+
{
42+
return in_array(
43+
$type,
44+
[
45+
'bool',
46+
'int',
47+
'float',
48+
'string',
49+
'array',
50+
]
51+
);
2352
}
2453
}

src/DocBlock/Entity/DocBlockType.php

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@
55

66
class DocblockType
77
{
8+
private string $name;
9+
810
private bool $isSingle;
911

10-
private string $name;
12+
private bool $isBuiltIn;
13+
14+
private ?bool $isNullable;
1115

12-
public function __construct(bool $isSingle, string $name)
16+
public function __construct(string $name, bool $isSingle, bool $isBuiltInt, ?bool $isNullable = null)
1317
{
14-
$this->isSingle = $isSingle;
15-
$this->name = $name;
18+
$this->name = $name;
19+
$this->isSingle = $isSingle;
20+
$this->isBuiltIn = $isBuiltInt;
21+
$this->isNullable = $isNullable;
1622
}
1723

1824
public function isSingle(): bool
@@ -22,20 +28,17 @@ public function isSingle(): bool
2228

2329
public function isBuiltIn(): bool
2430
{
25-
return in_array(
26-
$this->name,
27-
[
28-
'boolean', 'bool',
29-
'integer', 'int',
30-
'double', 'float',
31-
'string',
32-
'array'
33-
]
34-
);
31+
return $this->isBuiltIn;
32+
}
33+
34+
public function isNullable(): ?bool
35+
{
36+
return $this->isNullable;
3537
}
3638

3739
public function getName(): string
3840
{
3941
return $this->name;
4042
}
43+
4144
}

src/Mapper.php

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ public function __construct(DocBlockParser $docBlockParser)
2222

2323
public function addCustomMapper(string $type, Closure $mapper)
2424
{
25+
$type = $this->formatCustomTypeName($type);
26+
2527
$this->customMappers[$type] = $mapper;
2628
}
2729

28-
public function getCustomerMapper(string $type): ?Closure
30+
public function getCustomMapper(string $type): ?Closure
2931
{
32+
$type = $this->formatCustomTypeName($type);
33+
3034
return empty($this->customMappers[$type])
3135
? null
3236
: $this->customMappers[$type];
@@ -47,32 +51,37 @@ public function map(array $input, string $className)
4751

4852
$property->setAccessible(true);
4953

50-
$docblockType = $this->docBlockParser->getType((string)$property->getDocComment());
54+
$docBlockType = $this->docBlockParser->getType((string)$property->getDocComment());
5155
$type = $property->getType();
5256

53-
if (empty($type) && empty($docblockType)) {
54-
$property->setValue($object, $value);
55-
}
56-
elseif (empty($type) && !empty($docblockType)) {
57-
58-
}
59-
else {
60-
switch (true) {
61-
case $type->isBuiltin() && $type->getName() !== 'array':
62-
case $type->getName() === 'array' && empty($docblockType):
57+
if (!empty($type)) {
58+
if ($type->isBuiltin()) {
59+
if ($type->getName() === 'array') {
60+
if (empty($docBlockType)) {
61+
$property->setValue($object, $value);
62+
} else {
63+
$property->setValue($object, $this->castArray($value, $docBlockType));
64+
}
65+
} else {
6366
$property->setValue($object, $value);
64-
break;
65-
66-
case $type->getName() === 'array' && !empty($docblockType):
67-
$property->setValue($object, $this->castArray($value, $docblockType));
68-
break;
69-
70-
case !$type->isBuiltin():
71-
$property->setValue($object, $this->castCustom($value, $type->getName()));
72-
break;
73-
74-
default:
75-
throw new \Exception('Unhandled case');
67+
}
68+
} else {
69+
$property->setValue($object, $this->castCustom($value, $type->getName()));
70+
}
71+
} else {
72+
if (empty($docBlockType)) {
73+
$property->setValue($object, $value);
74+
} else {
75+
if ($docBlockType->isBuiltIn()) {
76+
if ($docBlockType->getName() === 'array') {
77+
$property->setValue($object, $this->castArray($value, $docBlockType));
78+
} else {
79+
settype($value, $docBlockType->getName());
80+
$property->setValue($object, $value);
81+
}
82+
} else {
83+
$property->setValue($object, $this->castCustom($value, $docBlockType->getName()));
84+
}
7685
}
7786
}
7887
}
@@ -81,27 +90,6 @@ public function map(array $input, string $className)
8190
return $object;
8291
}
8392

84-
private function castByType(bool $isBuiltInType, string $typeName, ?string $docBlockTypeName)
85-
{
86-
switch (true) {
87-
case $isBuiltInType && $typeName !== 'array':
88-
case $typeName === 'array' && empty($docBlockTypeName):
89-
$property->setValue($object, $value);
90-
break;
91-
92-
case $typeName === 'array' && !empty($docBlockTypeName):
93-
$property->setValue($object, $this->castArray($value, $docBlockTypeName));
94-
break;
95-
96-
case !$isBuiltInType:
97-
$property->setValue($object, $this->castCustom($value, $typeName));
98-
break;
99-
100-
default:
101-
throw new \Exception('Unhandled case');
102-
}
103-
}
104-
10593
private function castArray(array $array, DocblockType $docblockType): array
10694
{
10795
$castedArray = [];
@@ -112,8 +100,7 @@ private function castArray(array $array, DocblockType $docblockType): array
112100

113101
$castedArray[] = $castedItem;
114102
}
115-
}
116-
else {
103+
} else {
117104
foreach ($array as $item) {
118105
$castedArray[] = $this->map($item, $docblockType->getName());
119106
}
@@ -124,13 +111,17 @@ private function castArray(array $array, DocblockType $docblockType): array
124111

125112
private function castCustom($value, string $typeName)
126113
{
127-
$customMapper = $this->getCustomerMapper($typeName);
114+
$customMapper = $this->getCustomMapper($typeName);
128115

129116
if (empty($customMapper)) {
130117
return $this->map($value, $typeName);
131-
}
132-
else {
118+
} else {
133119
return $customMapper($value);
134120
}
135121
}
122+
123+
private function formatCustomTypeName(string $type): string
124+
{
125+
return ltrim($type, '\\');
126+
}
136127
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Emul\ArrayToClassMapper\Test\Unit;
5+
6+
use Emul\ArrayToClassMapper\DocBlock\DocBlockParser;
7+
8+
class DocBlockParserTest extends TestCaseAbstract
9+
{
10+
public function testGetTypeWhenEmptyProvided_shouldReturnNull()
11+
{
12+
$parser = new DocBlockParser();
13+
14+
$type = $parser->getType('');
15+
16+
$this->assertNull($type);
17+
}
18+
19+
public function testGetTypeWhenDoesNotContainType_shouldReturnNull()
20+
{
21+
$parser = new DocBlockParser();
22+
23+
$type = $parser->getType('/** @description something */');
24+
25+
$this->assertNull($type);
26+
}
27+
28+
public function builtInTypeProvider(): array
29+
{
30+
return [
31+
['/** @var bool */', 'bool'],
32+
['/** @var int */', 'int'],
33+
['/** @var float */', 'float'],
34+
['/** @var string */', 'string'],
35+
['/** @var array */', 'array'],
36+
];
37+
}
38+
39+
/**
40+
* @dataProvider builtInTypeProvider
41+
*/
42+
public function testGetTypeWhenTypeGiven_shouldReturnProperly(string $docBlock, string $expectedType)
43+
{
44+
$parser = new DocBlockParser();
45+
46+
$type = $parser->getType($docBlock);
47+
48+
$this->assertSame(true, $type->isBuiltIn());
49+
$this->assertSame(true, $type->isSingle());
50+
$this->assertSame(false, $type->isNullable());
51+
$this->assertSame($expectedType, $type->getName());
52+
}
53+
54+
public function nullableProvider(): array
55+
{
56+
return [
57+
'after type' => ['/** @var int|null */', 'int']
58+
];
59+
}
60+
61+
/**
62+
* @dataProvider nullableProvider
63+
*/
64+
public function testGetTypeWhenNullableTypeGiven_shouldReturnProperly(string $docBlock, string $expectedType)
65+
{
66+
$parser = new DocBlockParser();
67+
68+
$type = $parser->getType($docBlock);
69+
70+
$this->assertSame(true, $type->isBuiltIn());
71+
$this->assertSame(true, $type->isSingle());
72+
$this->assertSame(true, $type->isNullable());
73+
$this->assertSame($expectedType, $type->getName());
74+
}
75+
76+
public function testGetTypeWhenMultiLine_shouldParseProperly()
77+
{
78+
$parser = new DocBlockParser();
79+
80+
$type = $parser->getType('
81+
/**
82+
* @customProperty value
83+
* @var bool|null
84+
*/
85+
');
86+
87+
$this->assertSame(true, $type->isBuiltIn());
88+
$this->assertSame(true, $type->isSingle());
89+
$this->assertSame(true, $type->isNullable());
90+
$this->assertSame('bool', $type->getName());
91+
}
92+
}

0 commit comments

Comments
 (0)