Skip to content

Commit 8cbe610

Browse files
authored
Add support for generics in Static_ and Self_ (#228)
* Add support for generics in `Static_` and `Self_` * Add tests for iterable with generics * Fix code style * Reusing the `createTypesByTypeNodes` method
1 parent 696c4b1 commit 8cbe610

File tree

6 files changed

+213
-34
lines changed

6 files changed

+213
-34
lines changed

src/TypeResolver.php

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -434,16 +434,7 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ
434434
return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]);
435435

436436
case 'iterable':
437-
return new Iterable_(
438-
...array_reverse(
439-
array_map(
440-
function (TypeNode $genericType) use ($context): Type {
441-
return $this->createType($genericType, $context);
442-
},
443-
$type->genericTypes
444-
)
445-
)
446-
);
437+
return new Iterable_(...array_reverse($this->createTypesByTypeNodes($type->genericTypes, $context)));
447438

448439
case 'key-of':
449440
return new KeyOf($this->createType($type->genericTypes[0], $context));
@@ -452,18 +443,17 @@ function (TypeNode $genericType) use ($context): Type {
452443
return new ValueOf($this->createType($type->genericTypes[0], $context));
453444

454445
case 'int-mask':
455-
return new IntMask(
456-
...array_map(
457-
function (TypeNode $genericType) use ($context): Type {
458-
return $this->createType($genericType, $context);
459-
},
460-
$type->genericTypes
461-
)
462-
);
446+
return new IntMask(...$this->createTypesByTypeNodes($type->genericTypes, $context));
463447

464448
case 'int-mask-of':
465449
return new IntMaskOf($this->createType($type->genericTypes[0], $context));
466450

451+
case 'static':
452+
return new Static_(...$this->createTypesByTypeNodes($type->genericTypes, $context));
453+
454+
case 'self':
455+
return new Self_(...$this->createTypesByTypeNodes($type->genericTypes, $context));
456+
467457
default:
468458
$collectionType = $this->createType($type->type, $context);
469459
if ($collectionType instanceof Object_ === false) {
@@ -472,14 +462,7 @@ function (TypeNode $genericType) use ($context): Type {
472462

473463
return new Collection(
474464
$collectionType->getFqsen(),
475-
...array_reverse(
476-
array_map(
477-
function (TypeNode $genericType) use ($context): Type {
478-
return $this->createType($genericType, $context);
479-
},
480-
$type->genericTypes
481-
)
482-
)
465+
...array_reverse($this->createTypesByTypeNodes($type->genericTypes, $context))
483466
);
484467
}
485468
}
@@ -645,14 +628,7 @@ private function resolveTypedObject(string $type, ?Context $context = null): Obj
645628
/** @param TypeNode[] $typeNodes */
646629
private function createArray(array $typeNodes, Context $context): Array_
647630
{
648-
$types = array_reverse(
649-
array_map(
650-
function (TypeNode $node) use ($context): Type {
651-
return $this->createType($node, $context);
652-
},
653-
$typeNodes
654-
)
655-
);
631+
$types = array_reverse($this->createTypesByTypeNodes($typeNodes, $context));
656632

657633
if (isset($types[1]) === false) {
658634
return new Array_(...$types);
@@ -727,4 +703,19 @@ private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Co
727703

728704
return $type;
729705
}
706+
707+
/**
708+
* @param TypeNode[] $nodes
709+
*
710+
* @return Type[]
711+
*/
712+
private function createTypesByTypeNodes(array $nodes, Context $context): array
713+
{
714+
return array_map(
715+
function (TypeNode $node) use ($context): Type {
716+
return $this->createType($node, $context);
717+
},
718+
$nodes
719+
);
720+
}
730721
}

src/Types/Self_.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
use phpDocumentor\Reflection\Type;
1717

18+
use function implode;
19+
1820
/**
1921
* Value Object representing the 'self' type.
2022
*
@@ -24,11 +26,31 @@
2426
*/
2527
final class Self_ implements Type
2628
{
29+
/** @var Type[] */
30+
private $genericTypes;
31+
32+
public function __construct(Type ...$genericTypes)
33+
{
34+
$this->genericTypes = $genericTypes;
35+
}
36+
37+
/**
38+
* @return Type[]
39+
*/
40+
public function getGenericTypes(): array
41+
{
42+
return $this->genericTypes;
43+
}
44+
2745
/**
2846
* Returns a rendered output of the Type as it would be used in a DocBlock.
2947
*/
3048
public function __toString(): string
3149
{
50+
if ($this->genericTypes) {
51+
return 'self<' . implode(', ', $this->genericTypes) . '>';
52+
}
53+
3254
return 'self';
3355
}
3456
}

src/Types/Static_.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
use phpDocumentor\Reflection\Type;
1717

18+
use function implode;
19+
1820
/**
1921
* Value Object representing the 'static' type.
2022
*
@@ -29,11 +31,31 @@
2931
*/
3032
final class Static_ implements Type
3133
{
34+
/** @var Type[] */
35+
private $genericTypes;
36+
37+
public function __construct(Type ...$genericTypes)
38+
{
39+
$this->genericTypes = $genericTypes;
40+
}
41+
42+
/**
43+
* @return Type[]
44+
*/
45+
public function getGenericTypes(): array
46+
{
47+
return $this->genericTypes;
48+
}
49+
3250
/**
3351
* Returns a rendered output of the Type as it would be used in a DocBlock.
3452
*/
3553
public function __toString(): string
3654
{
55+
if ($this->genericTypes) {
56+
return 'static<' . implode(', ', $this->genericTypes) . '>';
57+
}
58+
3759
return 'static';
3860
}
3961
}

tests/unit/TypeResolverTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,10 @@ public function typeProvider(): array
980980
new Integer()
981981
),
982982
],
983+
[
984+
'static',
985+
new Static_(),
986+
],
983987
[
984988
'self',
985989
new Self_(),
@@ -1109,6 +1113,26 @@ public function genericsProvider(): array
11091113
'int-mask-of<Foo::INT_*>',
11101114
new IntMaskOf(new ConstExpression(new Object_(new Fqsen('\\phpDocumentor\\Foo')), 'INT_*')),
11111115
],
1116+
[
1117+
'iterable<int, string>',
1118+
new Iterable_(new String_(), new Integer()),
1119+
],
1120+
[
1121+
'static<FirstClass, SecondClass, ThirdClass>',
1122+
new Static_(
1123+
new Object_(new Fqsen('\\phpDocumentor\\FirstClass')),
1124+
new Object_(new Fqsen('\\phpDocumentor\\SecondClass')),
1125+
new Object_(new Fqsen('\\phpDocumentor\\ThirdClass')),
1126+
),
1127+
],
1128+
[
1129+
'self<FirstClass, SecondClass, ThirdClass>',
1130+
new Self_(
1131+
new Object_(new Fqsen('\\phpDocumentor\\FirstClass')),
1132+
new Object_(new Fqsen('\\phpDocumentor\\SecondClass')),
1133+
new Object_(new Fqsen('\\phpDocumentor\\ThirdClass')),
1134+
),
1135+
],
11121136
];
11131137
}
11141138

tests/unit/Types/SelfTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Types;
6+
7+
use phpDocumentor\Reflection\Fqsen;
8+
use PHPUnit\Framework\TestCase;
9+
10+
/**
11+
* @coversDefaultClass \phpDocumentor\Reflection\Types\Self_
12+
*/
13+
class SelfTest extends TestCase
14+
{
15+
/**
16+
* @covers ::getGenericTypes
17+
*/
18+
public function testCreate(): void
19+
{
20+
$genericTypes = [
21+
new Object_(new Fqsen('\\phpDocumentor\\FirstClass')),
22+
new Object_(new Fqsen('\\phpDocumentor\\SecondClass')),
23+
new Object_(new Fqsen('\\phpDocumentor\\ThirdClass')),
24+
];
25+
26+
$type = new Self_(...$genericTypes);
27+
28+
$this->assertSame($genericTypes, $type->getGenericTypes());
29+
}
30+
31+
/**
32+
* @dataProvider provideToStringData
33+
* @covers ::__toString
34+
*/
35+
public function testToString(string $expectedResult, Self_ $type): void
36+
{
37+
$this->assertSame($expectedResult, (string) $type);
38+
}
39+
40+
/**
41+
* @return array<string, array{string, OffsetAccess}>
42+
*/
43+
public static function provideToStringData(): array
44+
{
45+
return [
46+
'basic' => [
47+
'self',
48+
new Self_(),
49+
],
50+
'with generic' => [
51+
'self<\\phpDocumentor\\FirstClass, \\phpDocumentor\\SecondClass, \\phpDocumentor\\ThirdClass>',
52+
new Self_(
53+
new Object_(new Fqsen('\\phpDocumentor\\FirstClass')),
54+
new Object_(new Fqsen('\\phpDocumentor\\SecondClass')),
55+
new Object_(new Fqsen('\\phpDocumentor\\ThirdClass')),
56+
),
57+
],
58+
];
59+
}
60+
}

tests/unit/Types/StaticTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Types;
6+
7+
use phpDocumentor\Reflection\Fqsen;
8+
use PHPUnit\Framework\TestCase;
9+
10+
/**
11+
* @coversDefaultClass \phpDocumentor\Reflection\Types\Static_
12+
*/
13+
class StaticTest extends TestCase
14+
{
15+
/**
16+
* @covers ::getGenericTypes
17+
*/
18+
public function testCreate(): void
19+
{
20+
$genericTypes = [
21+
new Object_(new Fqsen('\\phpDocumentor\\FirstClass')),
22+
new Object_(new Fqsen('\\phpDocumentor\\SecondClass')),
23+
new Object_(new Fqsen('\\phpDocumentor\\ThirdClass')),
24+
];
25+
26+
$type = new Static_(...$genericTypes);
27+
28+
$this->assertSame($genericTypes, $type->getGenericTypes());
29+
}
30+
31+
/**
32+
* @dataProvider provideToStringData
33+
* @covers ::__toString
34+
*/
35+
public function testToString(string $expectedResult, Static_ $type): void
36+
{
37+
$this->assertSame($expectedResult, (string) $type);
38+
}
39+
40+
/**
41+
* @return array<string, array{string, Static_}>
42+
*/
43+
public static function provideToStringData(): array
44+
{
45+
return [
46+
'basic' => [
47+
'static',
48+
new Static_(),
49+
],
50+
'with generic' => [
51+
'static<\\phpDocumentor\\FirstClass, \\phpDocumentor\\SecondClass, \\phpDocumentor\\ThirdClass>',
52+
new Static_(
53+
new Object_(new Fqsen('\\phpDocumentor\\FirstClass')),
54+
new Object_(new Fqsen('\\phpDocumentor\\SecondClass')),
55+
new Object_(new Fqsen('\\phpDocumentor\\ThirdClass')),
56+
),
57+
],
58+
];
59+
}
60+
}

0 commit comments

Comments
 (0)