Skip to content

Commit 50bf167

Browse files
committed
Add support for constant types.
phpstan supports contant definitions and expressions to link to constants.
1 parent 8d57d3d commit 50bf167

File tree

8 files changed

+205
-61
lines changed

8 files changed

+205
-61
lines changed

src/DocBlock/Tags/Factory/TypeFactory.php

+35-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
66

7+
use phpDocumentor\Reflection\FqsenResolver;
78
use phpDocumentor\Reflection\PseudoTypes\ArrayShape;
89
use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem;
10+
use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
11+
use phpDocumentor\Reflection\PseudoTypes\FloatValue;
912
use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
13+
use phpDocumentor\Reflection\PseudoTypes\IntegerValue;
1014
use phpDocumentor\Reflection\PseudoTypes\List_;
15+
use phpDocumentor\Reflection\PseudoTypes\StringValue;
1116
use phpDocumentor\Reflection\Type;
1217
use phpDocumentor\Reflection\TypeResolver;
1318
use phpDocumentor\Reflection\Types\Array_;
@@ -20,6 +25,10 @@
2025
use phpDocumentor\Reflection\Types\Intersection;
2126
use phpDocumentor\Reflection\Types\Nullable;
2227
use phpDocumentor\Reflection\Types\This;
28+
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
29+
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
30+
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
31+
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
2332
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
2433
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
2534
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
@@ -48,10 +57,12 @@
4857
final class TypeFactory
4958
{
5059
private TypeResolver $resolver;
60+
private FqsenResolver $fqsenResolver;
5161

52-
public function __construct(TypeResolver $resolver)
62+
public function __construct(TypeResolver $resolver, FqsenResolver $fqsenResolver)
5363
{
5464
$this->resolver = $resolver;
65+
$this->fqsenResolver = $fqsenResolver;
5566
}
5667

5768
public function createType(?TypeNode $type, ?Context $context): ?Type
@@ -82,7 +93,7 @@ public function createType(?TypeNode $type, ?Context $context): ?Type
8293
return $this->createFromCallable($type, $context);
8394

8495
case ConstTypeNode::class:
85-
return null;
96+
return $this->createFromConst($type, $context);
8697

8798
case GenericTypeNode::class:
8899
return $this->createFromGeneric($type, $context);
@@ -144,12 +155,12 @@ private function createFromGeneric(GenericTypeNode $type, ?Context $context): Ty
144155

145156
case 'class-string':
146157
return new ClassString(
147-
$this->createType($type->genericTypes[0], $context)->getFqsen()
158+
$this->fqsenResolver->resolve((string) $type->genericTypes[0], $context)
148159
);
149160

150161
case 'interface-string':
151162
return new InterfaceString(
152-
$this->createType($type->genericTypes[0], $context)->getFqsen()
163+
$this->fqsenResolver->resolve((string) $type->genericTypes[0], $context)
153164
);
154165

155166
case 'list':
@@ -180,4 +191,24 @@ private function createFromCallable(CallableTypeNode $type, ?Context $context):
180191
{
181192
return new Callable_();
182193
}
194+
195+
private function createFromConst(ConstTypeNode $type, ?Context $context): ?Type
196+
{
197+
switch (get_class($type->constExpr)) {
198+
case ConstExprIntegerNode::class:
199+
return new IntegerValue((int) $type->constExpr->value);
200+
201+
case ConstExprFloatNode::class:
202+
return new FloatValue((float) $type->constExpr->value);
203+
204+
case ConstExprStringNode::class:
205+
return new StringValue($type->constExpr->value);
206+
207+
case ConstFetchNode::class:
208+
return new ConstExpression(
209+
$this->fqsenResolver->resolve($type->constExpr->className, $context),
210+
$type->constExpr->name
211+
);
212+
}
213+
}
183214
}

src/DocBlockFactory.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public static function createInstance(array $additionalTags = []): self
7070
$tagFactory = new StandardTagFactory($fqsenResolver);
7171
$descriptionFactory = new DescriptionFactory($tagFactory);
7272
$typeResolver = new TypeResolver($fqsenResolver);
73-
$typeFactory = new TypeFactory($typeResolver);
73+
$typeFactory = new TypeFactory($typeResolver, $fqsenResolver);
7474

7575
$phpstanTagFactory = new AbstractPHPStanFactory(
7676
new ParamFactory($typeFactory, $descriptionFactory),

src/PseudoTypes/ConstExpression.php

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use phpDocumentor\Reflection\Fqsen;
8+
use phpDocumentor\Reflection\PseudoType;
9+
use phpDocumentor\Reflection\Type;
10+
use phpDocumentor\Reflection\Types\Mixed_;
11+
12+
use function sprintf;
13+
14+
final class ConstExpression implements PseudoType
15+
{
16+
private Fqsen $owner;
17+
private string $expression;
18+
19+
public function __construct(Fqsen $owner, string $expression)
20+
{
21+
$this->owner = $owner;
22+
$this->expression = $expression;
23+
}
24+
25+
public function getOwner(): Fqsen
26+
{
27+
return $this->owner;
28+
}
29+
30+
public function getExpression(): string
31+
{
32+
return $this->expression;
33+
}
34+
35+
public function underlyingType(): Type
36+
{
37+
return new Mixed_();
38+
}
39+
40+
public function __toString(): string
41+
{
42+
return sprintf('%s::%s', $this->owner, $this->expression);
43+
}
44+
}

src/PseudoTypes/FloatValue.php

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use phpDocumentor\Reflection\PseudoType;
8+
use phpDocumentor\Reflection\Type;
9+
use phpDocumentor\Reflection\Types\Float_;
10+
11+
class FloatValue implements PseudoType
12+
{
13+
private float $value;
14+
15+
public function __construct(float $value)
16+
{
17+
$this->value = $value;
18+
}
19+
20+
public function getValue(): float
21+
{
22+
return $this->value;
23+
}
24+
25+
public function underlyingType(): Type
26+
{
27+
return new Float_();
28+
}
29+
30+
public function __toString(): string
31+
{
32+
return (string) $this->value;
33+
}
34+
}

src/PseudoTypes/IntegerValue.php

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use phpDocumentor\Reflection\PseudoType;
8+
use phpDocumentor\Reflection\Type;
9+
use phpDocumentor\Reflection\Types\Integer;
10+
11+
final class IntegerValue implements PseudoType
12+
{
13+
private int $value;
14+
15+
public function __construct(int $value)
16+
{
17+
$this->value = $value;
18+
}
19+
20+
public function getValue(): int
21+
{
22+
return $this->value;
23+
}
24+
25+
public function underlyingType(): Type
26+
{
27+
return new Integer();
28+
}
29+
30+
public function __toString(): string
31+
{
32+
return (string) $this->value;
33+
}
34+
}

src/PseudoTypes/StringValue.php

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use phpDocumentor\Reflection\PseudoType;
8+
use phpDocumentor\Reflection\Type;
9+
use phpDocumentor\Reflection\Types\Float_;
10+
11+
use function sprintf;
12+
13+
class StringValue implements PseudoType
14+
{
15+
private string $value;
16+
17+
public function __construct(string $value)
18+
{
19+
$this->value = $value;
20+
}
21+
22+
public function getValue(): string
23+
{
24+
return $this->value;
25+
}
26+
27+
public function underlyingType(): Type
28+
{
29+
return new Float_();
30+
}
31+
32+
public function __toString(): string
33+
{
34+
return sprintf('"%s"', $this->value);
35+
}
36+
}

tests/unit/DocBlock/Tags/Factory/TagFactoryTestCase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function parseTag(string $tag): PhpDocTagNode
3939

4040
public function giveTypeFactory(): TypeFactory
4141
{
42-
return new TypeFactory(new TypeResolver(new FqsenResolver()));
42+
return new TypeFactory(new TypeResolver(new FqsenResolver()), new FqsenResolver());
4343
}
4444

4545
public function givenDescriptionFactory(): DescriptionFactory

tests/unit/DocBlock/Tags/Factory/TypeFactoryTest.php

+20-55
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@
1515

1616
use phpDocumentor\Reflection\Fqsen;
1717
use phpDocumentor\Reflection\FqsenResolver;
18+
use phpDocumentor\Reflection\PseudoTypes\ConstExpression;
19+
use phpDocumentor\Reflection\PseudoTypes\FloatValue;
1820
use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
21+
use phpDocumentor\Reflection\PseudoTypes\IntegerValue;
1922
use phpDocumentor\Reflection\PseudoTypes\List_;
23+
use phpDocumentor\Reflection\PseudoTypes\StringValue;
24+
use phpDocumentor\Reflection\PseudoTypes\True_;
2025
use phpDocumentor\Reflection\Type;
2126
use phpDocumentor\Reflection\TypeResolver;
2227
use phpDocumentor\Reflection\Types\Array_;
@@ -50,6 +55,8 @@ final class TypeFactoryTest extends TestCase
5055
* @dataProvider typeProvider
5156
* @dataProvider genericsProvider
5257
* @dataProvider callableProvider
58+
* @dataProvider constExpressions
59+
* @testdox create type from $type
5360
*/
5461
public function testTypeBuilding(string $type, Type $expected): void
5562
{
@@ -58,8 +65,9 @@ public function testTypeBuilding(string $type, Type $expected): void
5865
$constParser = new ConstExprParser();
5966
$parser = new TypeParser($constParser);
6067
$ast = $parser->parse(new TokenIterator($tokens));
68+
$fqsenResolver = new FqsenResolver();
6169

62-
$factory = new TypeFactory(new TypeResolver(new FqsenResolver()));
70+
$factory = new TypeFactory(new TypeResolver($fqsenResolver), $fqsenResolver);
6371
$actual = $factory->createType($ast, new Context('phpDocumentor'));
6472

6573
self::assertEquals($expected, $actual);
@@ -219,72 +227,29 @@ public function callableProvider(): array
219227
public function constExpressions(): array
220228
{
221229
return [
222-
['Foo::FOO_CONSTANT'],
223230
[
224231
'123',
225-
//new ConstTypeNode(new ConstExprIntegerNode('123')),
232+
new IntegerValue(123),
226233
],
227234
[
228-
'123.2',
229-
//new ConstTypeNode(new ConstExprFloatNode('123.2')),
230-
],
231-
[
232-
'"bar"',
233-
//new ConstTypeNode(new ConstExprStringNode('bar')),
234-
],
235-
[
236-
'Foo::FOO_*',
237-
//new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')),
238-
],
239-
[
240-
'Foo::FOO_*BAR',
241-
//new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*BAR')),
242-
],
243-
[
244-
'Foo::*FOO*',
245-
//new ConstTypeNode(new ConstFetchNode('Foo', '*FOO*')),
235+
'true',
236+
new True_(),
246237
],
247238
[
248-
'Foo::A*B*C',
249-
//new ConstTypeNode(new ConstFetchNode('Foo', 'A*B*C')),
250-
],
251-
[
252-
'self::*BAR',
253-
//new ConstTypeNode(new ConstFetchNode('self', '*BAR')),
254-
],
255-
[
256-
'Foo::*',
257-
//new ConstTypeNode(new ConstFetchNode('Foo', '*')),
258-
],
259-
[
260-
'Foo::**',
261-
//new ConstTypeNode(new ConstFetchNode('Foo', '*')), // fails later in PhpDocParser
262-
//Lexer::TOKEN_WILDCARD,
263-
],
264-
[
265-
'Foo::*a',
266-
//new ConstTypeNode(new ConstFetchNode('Foo', '*a')),
239+
'123.2',
240+
new FloatValue(123.2),
267241
],
268242
[
269-
'( "foo" | Foo::FOO_* )',
270-
// new UnionTypeNode([
271-
// new ConstTypeNode(new ConstExprStringNode('foo')),
272-
// new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')),
273-
// ]),
243+
'"bar"',
244+
new StringValue('bar'),
274245
],
275246
[
276-
'DateTimeImmutable::*|DateTime::*',
277-
// new UnionTypeNode([
278-
// new ConstTypeNode(new ConstFetchNode('DateTimeImmutable', '*')),
279-
// new ConstTypeNode(new ConstFetchNode('DateTime', '*')),
280-
// ]),
247+
'Foo::FOO_CONSTANT',
248+
new ConstExpression(new Fqsen('\\phpDocumentor\\Foo'), 'FOO_CONSTANT'),
281249
],
282250
[
283-
'ParameterTier::*|null',
284-
// new UnionTypeNode([
285-
// new ConstTypeNode(new ConstFetchNode('ParameterTier', '*')),
286-
// new IdentifierTypeNode('null'),
287-
// ]),
251+
'Foo::FOO_*',
252+
new ConstExpression(new Fqsen('\\phpDocumentor\\Foo'), 'FOO_*'),
288253
],
289254
];
290255
}

0 commit comments

Comments
 (0)