diff --git a/src/DocBlock/Tags/Factory/TypeFactory.php b/src/DocBlock/Tags/Factory/TypeFactory.php index 6cf20a3..2204c2f 100644 --- a/src/DocBlock/Tags/Factory/TypeFactory.php +++ b/src/DocBlock/Tags/Factory/TypeFactory.php @@ -4,10 +4,15 @@ declare(strict_types=1); namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; +use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\PseudoTypes\ArrayShape; use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem; +use phpDocumentor\Reflection\PseudoTypes\ConstExpression; +use phpDocumentor\Reflection\PseudoTypes\FloatValue; use phpDocumentor\Reflection\PseudoTypes\IntegerRange; +use phpDocumentor\Reflection\PseudoTypes\IntegerValue; use phpDocumentor\Reflection\PseudoTypes\List_; +use phpDocumentor\Reflection\PseudoTypes\StringValue; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Array_; @@ -20,6 +25,10 @@ use phpDocumentor\Reflection\Types\InterfaceString; use phpDocumentor\Reflection\Types\Intersection; use phpDocumentor\Reflection\Types\Nullable; use phpDocumentor\Reflection\Types\This; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; @@ -48,10 +57,12 @@ use function strtolower; final class TypeFactory { private TypeResolver $resolver; + private FqsenResolver $fqsenResolver; - public function __construct(TypeResolver $resolver) + public function __construct(TypeResolver $resolver, FqsenResolver $fqsenResolver) { $this->resolver = $resolver; + $this->fqsenResolver = $fqsenResolver; } public function createType(?TypeNode $type, ?Context $context): ?Type @@ -82,7 +93,7 @@ final class TypeFactory return $this->createFromCallable($type, $context); case ConstTypeNode::class: - return null; + return $this->createFromConst($type, $context); case GenericTypeNode::class: return $this->createFromGeneric($type, $context); @@ -144,12 +155,12 @@ final class TypeFactory case 'class-string': return new ClassString( - $this->createType($type->genericTypes[0], $context)->getFqsen() + $this->fqsenResolver->resolve((string) $type->genericTypes[0], $context) ); case 'interface-string': return new InterfaceString( - $this->createType($type->genericTypes[0], $context)->getFqsen() + $this->fqsenResolver->resolve((string) $type->genericTypes[0], $context) ); case 'list': @@ -180,4 +191,24 @@ final class TypeFactory { return new Callable_(); } + + private function createFromConst(ConstTypeNode $type, ?Context $context): ?Type + { + switch (get_class($type->constExpr)) { + case ConstExprIntegerNode::class: + return new IntegerValue((int) $type->constExpr->value); + + case ConstExprFloatNode::class: + return new FloatValue((float) $type->constExpr->value); + + case ConstExprStringNode::class: + return new StringValue($type->constExpr->value); + + case ConstFetchNode::class: + return new ConstExpression( + $this->fqsenResolver->resolve($type->constExpr->className, $context), + $type->constExpr->name + ); + } + } } diff --git a/src/DocBlockFactory.php b/src/DocBlockFactory.php index 4c0d58f..8610dbb 100644 --- a/src/DocBlockFactory.php +++ b/src/DocBlockFactory.php @@ -70,7 +70,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface $tagFactory = new StandardTagFactory($fqsenResolver); $descriptionFactory = new DescriptionFactory($tagFactory); $typeResolver = new TypeResolver($fqsenResolver); - $typeFactory = new TypeFactory($typeResolver); + $typeFactory = new TypeFactory($typeResolver, $fqsenResolver); $phpstanTagFactory = new AbstractPHPStanFactory( new ParamFactory($typeFactory, $descriptionFactory), diff --git a/src/PseudoTypes/ConstExpression.php b/src/PseudoTypes/ConstExpression.php new file mode 100644 index 0000000..b7189ce --- /dev/null +++ b/src/PseudoTypes/ConstExpression.php @@ -0,0 +1,44 @@ +owner = $owner; + $this->expression = $expression; + } + + public function getOwner(): Fqsen + { + return $this->owner; + } + + public function getExpression(): string + { + return $this->expression; + } + + public function underlyingType(): Type + { + return new Mixed_(); + } + + public function __toString(): string + { + return sprintf('%s::%s', $this->owner, $this->expression); + } +} diff --git a/src/PseudoTypes/FloatValue.php b/src/PseudoTypes/FloatValue.php new file mode 100644 index 0000000..07e0156 --- /dev/null +++ b/src/PseudoTypes/FloatValue.php @@ -0,0 +1,34 @@ +value = $value; + } + + public function getValue(): float + { + return $this->value; + } + + public function underlyingType(): Type + { + return new Float_(); + } + + public function __toString(): string + { + return (string) $this->value; + } +} diff --git a/src/PseudoTypes/IntegerValue.php b/src/PseudoTypes/IntegerValue.php new file mode 100644 index 0000000..2a56ad4 --- /dev/null +++ b/src/PseudoTypes/IntegerValue.php @@ -0,0 +1,34 @@ +value = $value; + } + + public function getValue(): int + { + return $this->value; + } + + public function underlyingType(): Type + { + return new Integer(); + } + + public function __toString(): string + { + return (string) $this->value; + } +} diff --git a/src/PseudoTypes/StringValue.php b/src/PseudoTypes/StringValue.php new file mode 100644 index 0000000..f4909cd --- /dev/null +++ b/src/PseudoTypes/StringValue.php @@ -0,0 +1,36 @@ +value = $value; + } + + public function getValue(): string + { + return $this->value; + } + + public function underlyingType(): Type + { + return new Float_(); + } + + public function __toString(): string + { + return sprintf('"%s"', $this->value); + } +} diff --git a/tests/unit/DocBlock/Tags/Factory/TagFactoryTestCase.php b/tests/unit/DocBlock/Tags/Factory/TagFactoryTestCase.php index 625958f..4b2c7be 100644 --- a/tests/unit/DocBlock/Tags/Factory/TagFactoryTestCase.php +++ b/tests/unit/DocBlock/Tags/Factory/TagFactoryTestCase.php @@ -39,7 +39,7 @@ abstract class TagFactoryTestCase extends TestCase public function giveTypeFactory(): TypeFactory { - return new TypeFactory(new TypeResolver(new FqsenResolver())); + return new TypeFactory(new TypeResolver(new FqsenResolver()), new FqsenResolver()); } public function givenDescriptionFactory(): DescriptionFactory diff --git a/tests/unit/DocBlock/Tags/Factory/TypeFactoryTest.php b/tests/unit/DocBlock/Tags/Factory/TypeFactoryTest.php index ffec35a..ca3c282 100644 --- a/tests/unit/DocBlock/Tags/Factory/TypeFactoryTest.php +++ b/tests/unit/DocBlock/Tags/Factory/TypeFactoryTest.php @@ -15,8 +15,13 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\PseudoTypes\ConstExpression; +use phpDocumentor\Reflection\PseudoTypes\FloatValue; use phpDocumentor\Reflection\PseudoTypes\IntegerRange; +use phpDocumentor\Reflection\PseudoTypes\IntegerValue; use phpDocumentor\Reflection\PseudoTypes\List_; +use phpDocumentor\Reflection\PseudoTypes\StringValue; +use phpDocumentor\Reflection\PseudoTypes\True_; use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Array_; @@ -50,6 +55,8 @@ final class TypeFactoryTest extends TestCase * @dataProvider typeProvider * @dataProvider genericsProvider * @dataProvider callableProvider + * @dataProvider constExpressions + * @testdox create type from $type */ public function testTypeBuilding(string $type, Type $expected): void { @@ -58,8 +65,9 @@ final class TypeFactoryTest extends TestCase $constParser = new ConstExprParser(); $parser = new TypeParser($constParser); $ast = $parser->parse(new TokenIterator($tokens)); + $fqsenResolver = new FqsenResolver(); - $factory = new TypeFactory(new TypeResolver(new FqsenResolver())); + $factory = new TypeFactory(new TypeResolver($fqsenResolver), $fqsenResolver); $actual = $factory->createType($ast, new Context('phpDocumentor')); self::assertEquals($expected, $actual); @@ -219,72 +227,29 @@ final class TypeFactoryTest extends TestCase public function constExpressions(): array { return [ - ['Foo::FOO_CONSTANT'], [ '123', - //new ConstTypeNode(new ConstExprIntegerNode('123')), + new IntegerValue(123), + ], + [ + 'true', + new True_(), ], [ '123.2', - //new ConstTypeNode(new ConstExprFloatNode('123.2')), + new FloatValue(123.2), ], [ '"bar"', - //new ConstTypeNode(new ConstExprStringNode('bar')), + new StringValue('bar'), + ], + [ + 'Foo::FOO_CONSTANT', + new ConstExpression(new Fqsen('\\phpDocumentor\\Foo'), 'FOO_CONSTANT'), ], [ 'Foo::FOO_*', - //new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')), - ], - [ - 'Foo::FOO_*BAR', - //new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*BAR')), - ], - [ - 'Foo::*FOO*', - //new ConstTypeNode(new ConstFetchNode('Foo', '*FOO*')), - ], - [ - 'Foo::A*B*C', - //new ConstTypeNode(new ConstFetchNode('Foo', 'A*B*C')), - ], - [ - 'self::*BAR', - //new ConstTypeNode(new ConstFetchNode('self', '*BAR')), - ], - [ - 'Foo::*', - //new ConstTypeNode(new ConstFetchNode('Foo', '*')), - ], - [ - 'Foo::**', - //new ConstTypeNode(new ConstFetchNode('Foo', '*')), // fails later in PhpDocParser - //Lexer::TOKEN_WILDCARD, - ], - [ - 'Foo::*a', - //new ConstTypeNode(new ConstFetchNode('Foo', '*a')), - ], - [ - '( "foo" | Foo::FOO_* )', -// new UnionTypeNode([ -// new ConstTypeNode(new ConstExprStringNode('foo')), -// new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')), -// ]), - ], - [ - 'DateTimeImmutable::*|DateTime::*', -// new UnionTypeNode([ -// new ConstTypeNode(new ConstFetchNode('DateTimeImmutable', '*')), -// new ConstTypeNode(new ConstFetchNode('DateTime', '*')), -// ]), - ], - [ - 'ParameterTier::*|null', -// new UnionTypeNode([ -// new ConstTypeNode(new ConstFetchNode('ParameterTier', '*')), -// new IdentifierTypeNode('null'), -// ]), + new ConstExpression(new Fqsen('\\phpDocumentor\\Foo'), 'FOO_*'), ], ]; }