Skip to content

Commit e2de53a

Browse files
authored
Add support for compound FQSEN to class-string and interface-string (#249)
* Add support for compound FQSEN to `class-string` and `interface-string` * Add tests * Fix CS * Add support for any generics to `class-string` and `interface-string` * Fix CS
1 parent 54ef16e commit e2de53a

File tree

6 files changed

+79
-137
lines changed

6 files changed

+79
-137
lines changed

src/PseudoTypes/ClassString.php

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace phpDocumentor\Reflection\PseudoTypes;
1515

16-
use phpDocumentor\Reflection\Fqsen;
1716
use phpDocumentor\Reflection\PseudoType;
1817
use phpDocumentor\Reflection\Type;
1918
use phpDocumentor\Reflection\Types\String_;
@@ -25,39 +24,33 @@
2524
*/
2625
final class ClassString extends String_ implements PseudoType
2726
{
28-
/** @var Fqsen|null */
29-
private $fqsen;
27+
/** @var Type|null */
28+
private $genericType;
3029

31-
/**
32-
* Initializes this representation of a class string with the given Fqsen.
33-
*/
34-
public function __construct(?Fqsen $fqsen = null)
30+
public function __construct(?Type $genericType = null)
3531
{
36-
$this->fqsen = $fqsen;
32+
$this->genericType = $genericType;
3733
}
3834

3935
public function underlyingType(): Type
4036
{
4137
return new String_();
4238
}
4339

44-
/**
45-
* Returns the FQSEN associated with this object.
46-
*/
47-
public function getFqsen(): ?Fqsen
40+
public function getGenericType(): ?Type
4841
{
49-
return $this->fqsen;
42+
return $this->genericType;
5043
}
5144

5245
/**
5346
* Returns a rendered output of the Type as it would be used in a DocBlock.
5447
*/
5548
public function __toString(): string
5649
{
57-
if ($this->fqsen === null) {
50+
if ($this->genericType === null) {
5851
return 'class-string';
5952
}
6053

61-
return 'class-string<' . (string) $this->fqsen . '>';
54+
return 'class-string<' . (string) $this->genericType . '>';
6255
}
6356
}

src/PseudoTypes/InterfaceString.php

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace phpDocumentor\Reflection\PseudoTypes;
1515

16-
use phpDocumentor\Reflection\Fqsen;
1716
use phpDocumentor\Reflection\PseudoType;
1817
use phpDocumentor\Reflection\Type;
1918
use phpDocumentor\Reflection\Types\String_;
@@ -25,39 +24,33 @@
2524
*/
2625
final class InterfaceString extends String_ implements PseudoType
2726
{
28-
/** @var Fqsen|null */
29-
private $fqsen;
27+
/** @var Type|null */
28+
private $genericType;
3029

31-
/**
32-
* Initializes this representation of a class string with the given Fqsen.
33-
*/
34-
public function __construct(?Fqsen $fqsen = null)
30+
public function __construct(?Type $genericType = null)
3531
{
36-
$this->fqsen = $fqsen;
32+
$this->genericType = $genericType;
3733
}
3834

3935
public function underlyingType(): Type
4036
{
4137
return new String_();
4238
}
4339

44-
/**
45-
* Returns the FQSEN associated with this object.
46-
*/
47-
public function getFqsen(): ?Fqsen
40+
public function getGenericType(): ?Type
4841
{
49-
return $this->fqsen;
42+
return $this->genericType;
5043
}
5144

5245
/**
5346
* Returns a rendered output of the Type as it would be used in a DocBlock.
5447
*/
5548
public function __toString(): string
5649
{
57-
if ($this->fqsen === null) {
50+
if ($this->genericType === null) {
5851
return 'interface-string';
5952
}
6053

61-
return 'interface-string<' . (string) $this->fqsen . '>';
54+
return 'interface-string<' . (string) $this->genericType . '>';
6255
}
6356
}

src/TypeResolver.php

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -399,28 +399,10 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ
399399
return new NonEmptyArray(...$genericTypes);
400400

401401
case 'class-string':
402-
$subType = $this->createType($type->genericTypes[0], $context);
403-
if (!$subType instanceof Object_ || $subType->getFqsen() === null) {
404-
throw new RuntimeException(
405-
$subType . ' is not a class string'
406-
);
407-
}
408-
409-
return new ClassString(
410-
$subType->getFqsen()
411-
);
402+
return new ClassString($this->createType($type->genericTypes[0], $context));
412403

413404
case 'interface-string':
414-
$subType = $this->createType($type->genericTypes[0], $context);
415-
if (!$subType instanceof Object_ || $subType->getFqsen() === null) {
416-
throw new RuntimeException(
417-
$subType . ' is not a class string'
418-
);
419-
}
420-
421-
return new InterfaceString(
422-
$subType->getFqsen()
423-
);
405+
return new InterfaceString($this->createType($type->genericTypes[0], $context));
424406

425407
case 'list':
426408
return new List_(

tests/unit/PseudoTypes/ClassStringTest.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace phpDocumentor\Reflection\PseudoTypes;
1515

1616
use phpDocumentor\Reflection\Fqsen;
17+
use phpDocumentor\Reflection\Types\Compound;
18+
use phpDocumentor\Reflection\Types\Object_;
1719
use PHPUnit\Framework\TestCase;
1820

1921
/**
@@ -25,19 +27,31 @@ class ClassStringTest extends TestCase
2527
* @dataProvider provideClassStrings
2628
* @covers ::__toString
2729
*/
28-
public function testClassStringStringifyCorrectly(ClassString $array, string $expectedString): void
30+
public function testClassStringStringifyCorrectly(ClassString $type, string $expectedString): void
2931
{
30-
$this->assertSame($expectedString, (string) $array);
32+
$this->assertSame($expectedString, (string) $type);
3133
}
3234

3335
/**
34-
* @return mixed[]
36+
* @return array<string, array{ClassString, string}>
3537
*/
3638
public function provideClassStrings(): array
3739
{
3840
return [
3941
'generic class string' => [new ClassString(), 'class-string'],
40-
'typed class string' => [new ClassString(new Fqsen('\Foo\Bar')), 'class-string<\Foo\Bar>'],
42+
'typed class string' => [
43+
new ClassString(new Object_(new Fqsen('\Foo\Bar'))),
44+
'class-string<\Foo\Bar>',
45+
],
46+
'more than one class' => [
47+
new ClassString(
48+
new Compound([
49+
new Object_(new Fqsen('\Foo\Bar')),
50+
new Object_(new Fqsen('\Foo\Barrr')),
51+
])
52+
),
53+
'class-string<\Foo\Bar|\Foo\Barrr>',
54+
],
4155
];
4256
}
4357
}

tests/unit/PseudoTypes/InterfaceStringTest.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace phpDocumentor\Reflection\PseudoTypes;
1515

1616
use phpDocumentor\Reflection\Fqsen;
17+
use phpDocumentor\Reflection\Types\Compound;
18+
use phpDocumentor\Reflection\Types\Object_;
1719
use PHPUnit\Framework\TestCase;
1820

1921
/**
@@ -25,19 +27,31 @@ class InterfaceStringTest extends TestCase
2527
* @dataProvider provideInterfaceStrings
2628
* @covers ::__toString
2729
*/
28-
public function testInterfaceStringStringifyCorrectly(InterfaceString $array, string $expectedString): void
30+
public function testInterfaceStringStringifyCorrectly(InterfaceString $type, string $expectedString): void
2931
{
30-
$this->assertSame($expectedString, (string) $array);
32+
$this->assertSame($expectedString, (string) $type);
3133
}
3234

3335
/**
34-
* @return mixed[]
36+
* @return array<string, array{InterfaceString, string}>
3537
*/
3638
public function provideInterfaceStrings(): array
3739
{
3840
return [
3941
'generic interface string' => [new InterfaceString(), 'interface-string'],
40-
'typed interface string' => [new InterfaceString(new Fqsen('\Foo\Bar')), 'interface-string<\Foo\Bar>'],
42+
'typed interface string' => [
43+
new InterfaceString(new Object_(new Fqsen('\Foo\Bar'))),
44+
'interface-string<\Foo\Bar>',
45+
],
46+
'more than one class' => [
47+
new InterfaceString(
48+
new Compound([
49+
new Object_(new Fqsen('\Foo\Bar')),
50+
new Object_(new Fqsen('\Foo\Barrr')),
51+
])
52+
),
53+
'interface-string<\Foo\Bar|\Foo\Barrr>',
54+
],
4155
];
4256
}
4357
}

tests/unit/TypeResolverTest.php

Lines changed: 25 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -112,54 +112,6 @@ public function testResolvingKeywords(string $keyword, string $expectedClass): v
112112
$this->assertInstanceOf($expectedClass, $resolvedType);
113113
}
114114

115-
/**
116-
* @uses \phpDocumentor\Reflection\Types\Context
117-
* @uses \phpDocumentor\Reflection\Types\Object_
118-
* @uses \phpDocumentor\Reflection\Types\String_
119-
*
120-
* @covers ::__construct
121-
* @covers ::resolve
122-
* @covers ::createType
123-
*
124-
* @dataProvider provideClassStrings
125-
*/
126-
public function testResolvingClassStrings(string $classString, bool $throwsException): void
127-
{
128-
$fixture = new TypeResolver();
129-
130-
if ($throwsException) {
131-
$this->expectException(RuntimeException::class);
132-
}
133-
134-
$resolvedType = $fixture->resolve($classString, new Context(''));
135-
136-
$this->assertInstanceOf(ClassString::class, $resolvedType);
137-
}
138-
139-
/**
140-
* @uses \phpDocumentor\Reflection\Types\Context
141-
* @uses \phpDocumentor\Reflection\Types\Object_
142-
* @uses \phpDocumentor\Reflection\Types\String_
143-
*
144-
* @covers ::__construct
145-
* @covers ::resolve
146-
* @covers ::createType
147-
*
148-
* @dataProvider provideInterfaceStrings
149-
*/
150-
public function testResolvingInterfaceStrings(string $interfaceString, bool $throwsException): void
151-
{
152-
$fixture = new TypeResolver();
153-
154-
if ($throwsException) {
155-
$this->expectException(RuntimeException::class);
156-
}
157-
158-
$resolvedType = $fixture->resolve($interfaceString, new Context(''));
159-
160-
$this->assertInstanceOf(InterfaceString::class, $resolvedType);
161-
}
162-
163115
/**
164116
* @uses \phpDocumentor\Reflection\Types\Context
165117
* @uses \phpDocumentor\Reflection\Types\Object_
@@ -811,34 +763,6 @@ public function provideKeywords(): array
811763
];
812764
}
813765

814-
/**
815-
* Returns a list of class string types and whether they throw an exception.
816-
*
817-
* @return (string|bool)[][]
818-
*/
819-
public function provideClassStrings(): array
820-
{
821-
return [
822-
['class-string<\phpDocumentor\Reflection>', false],
823-
['class-string<\phpDocumentor\Reflection\DocBlock>', false],
824-
['class-string<string>', true],
825-
];
826-
}
827-
828-
/**
829-
* Returns a list of interface string types and whether they throw an exception.
830-
*
831-
* @return (string|bool)[][]
832-
*/
833-
public function provideInterfaceStrings(): array
834-
{
835-
return [
836-
['interface-string<\phpDocumentor\Reflection>', false],
837-
['interface-string<\phpDocumentor\Reflection\DocBlock>', false],
838-
['interface-string<string>', true],
839-
];
840-
}
841-
842766
/**
843767
* Provides a list of FQSENs to test the resolution patterns with.
844768
*
@@ -1122,15 +1046,37 @@ public function genericsProvider(): array
11221046
],
11231047
[
11241048
'class-string',
1125-
new ClassString(null),
1049+
new ClassString(),
11261050
],
11271051
[
11281052
'class-string<Foo>',
1129-
new ClassString(new Fqsen('\\phpDocumentor\\Foo')),
1053+
new ClassString(new Object_(new Fqsen('\\phpDocumentor\\Foo'))),
1054+
],
1055+
[
1056+
'class-string<Foo|Bar>',
1057+
new ClassString(
1058+
new Compound([
1059+
new Object_(new Fqsen('\\phpDocumentor\\Foo')),
1060+
new Object_(new Fqsen('\\phpDocumentor\\Bar')),
1061+
])
1062+
),
1063+
],
1064+
[
1065+
'interface-string',
1066+
new InterfaceString(),
11301067
],
11311068
[
11321069
'interface-string<Foo>',
1133-
new InterfaceString(new Fqsen('\\phpDocumentor\\Foo')),
1070+
new InterfaceString(new Object_(new Fqsen('\\phpDocumentor\\Foo'))),
1071+
],
1072+
[
1073+
'interface-string<Foo|Bar>',
1074+
new InterfaceString(
1075+
new Compound([
1076+
new Object_(new Fqsen('\\phpDocumentor\\Foo')),
1077+
new Object_(new Fqsen('\\phpDocumentor\\Bar')),
1078+
])
1079+
),
11341080
],
11351081
[
11361082
'List<Foo>',

0 commit comments

Comments
 (0)