diff --git a/src/PseudoTypes/AbstractConditional.php b/src/PseudoTypes/AbstractConditional.php new file mode 100644 index 0000000..b23e362 --- /dev/null +++ b/src/PseudoTypes/AbstractConditional.php @@ -0,0 +1,71 @@ +target = $target; + $this->if = $if; + $this->else = $else; + $this->negated = $negated; + } + + public function getTarget(): Type + { + return $this->target; + } + + public function getIf(): Type + { + return $this->if; + } + + public function getElse(): Type + { + return $this->else; + } + + public function getNegated(): bool + { + return $this->negated; + } + + public function underlyingType(): Type + { + return new Mixed_(); + } +} diff --git a/src/PseudoTypes/ArrayShape.php b/src/PseudoTypes/ArrayShape.php index e0d0c73..81ae109 100644 --- a/src/PseudoTypes/ArrayShape.php +++ b/src/PseudoTypes/ArrayShape.php @@ -22,7 +22,7 @@ use function implode; /** @psalm-immutable */ -class ArrayShape implements PseudoType +class ArrayShape extends Array_ implements PseudoType { /** @var ArrayShapeItem[] */ private $items; diff --git a/src/PseudoTypes/ClosedResource.php b/src/PseudoTypes/ClosedResource.php new file mode 100644 index 0000000..3658ec1 --- /dev/null +++ b/src/PseudoTypes/ClosedResource.php @@ -0,0 +1,39 @@ +subject = $subject; + } + + public function getSubject(): Type + { + return $this->subject; + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + return sprintf( + '(%s %s %s ? %s : %s)', + $this->subject, + $this->negated ? 'is not' : 'is', + $this->target, + $this->if, + $this->else + ); + } +} diff --git a/src/PseudoTypes/ConditionalParameter.php b/src/PseudoTypes/ConditionalParameter.php new file mode 100644 index 0000000..b41eaec --- /dev/null +++ b/src/PseudoTypes/ConditionalParameter.php @@ -0,0 +1,51 @@ +parameter = $parameter; + } + + public function getParameterName(): string + { + return $this->parameter; + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + return sprintf( + '(%s %s %s ? %s : %s)', + $this->parameter, + $this->negated ? 'is not' : 'is', + $this->target, + $this->if, + $this->else + ); + } +} diff --git a/src/PseudoTypes/ConstExpression.php b/src/PseudoTypes/ConstExpression.php index 7334933..a03612a 100644 --- a/src/PseudoTypes/ConstExpression.php +++ b/src/PseudoTypes/ConstExpression.php @@ -20,7 +20,7 @@ use function sprintf; /** @psalm-immutable */ -final class ConstExpression implements PseudoType +final class ConstExpression extends Mixed_ implements PseudoType { /** @var Type */ private $owner; diff --git a/src/PseudoTypes/FloatValue.php b/src/PseudoTypes/FloatValue.php index 0dbbce0..c2b206a 100644 --- a/src/PseudoTypes/FloatValue.php +++ b/src/PseudoTypes/FloatValue.php @@ -18,7 +18,7 @@ use phpDocumentor\Reflection\Types\Float_; /** @psalm-immutable */ -class FloatValue implements PseudoType +final class FloatValue extends Float_ implements PseudoType { /** @var float */ private $value; diff --git a/src/PseudoTypes/IntMask.php b/src/PseudoTypes/IntMask.php new file mode 100644 index 0000000..ba1f975 --- /dev/null +++ b/src/PseudoTypes/IntMask.php @@ -0,0 +1,42 @@ +'; + } +} diff --git a/src/PseudoTypes/IntMaskOf.php b/src/PseudoTypes/IntMaskOf.php new file mode 100644 index 0000000..08130c7 --- /dev/null +++ b/src/PseudoTypes/IntMaskOf.php @@ -0,0 +1,42 @@ +'; + } +} diff --git a/src/PseudoTypes/IntegerValue.php b/src/PseudoTypes/IntegerValue.php index 51f0d34..4cc68de 100644 --- a/src/PseudoTypes/IntegerValue.php +++ b/src/PseudoTypes/IntegerValue.php @@ -18,7 +18,7 @@ use phpDocumentor\Reflection\Types\Integer; /** @psalm-immutable */ -final class IntegerValue implements PseudoType +final class IntegerValue extends Integer implements PseudoType { /** @var int */ private $value; diff --git a/src/PseudoTypes/KeyOf.php b/src/PseudoTypes/KeyOf.php new file mode 100644 index 0000000..89a7635 --- /dev/null +++ b/src/PseudoTypes/KeyOf.php @@ -0,0 +1,48 @@ +keyType = $keyType; + } + + public function getKeyType(): Type + { + return $this->keyType; + } + + public function underlyingType(): Type + { + return new Mixed_(); + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + return 'key-of<' . $this->keyType . '>'; + } +} diff --git a/src/PseudoTypes/ObjectShape.php b/src/PseudoTypes/ObjectShape.php index 0acd931..a02beb2 100644 --- a/src/PseudoTypes/ObjectShape.php +++ b/src/PseudoTypes/ObjectShape.php @@ -11,7 +11,7 @@ use function implode; /** @psalm-immutable */ -final class ObjectShape implements PseudoType +final class ObjectShape extends Object_ implements PseudoType { /** @var ObjectShapeItem[] */ private $items; diff --git a/src/PseudoTypes/OffsetAccess.php b/src/PseudoTypes/OffsetAccess.php new file mode 100644 index 0000000..6b2587d --- /dev/null +++ b/src/PseudoTypes/OffsetAccess.php @@ -0,0 +1,66 @@ +valueType = $valueType; + $this->offsetType = $offsetType; + } + + public function getValueType(): Type + { + return $this->valueType; + } + + public function getOffsetType(): Type + { + return $this->offsetType; + } + + public function underlyingType(): Type + { + return new Mixed_(); + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + if ( + $this->valueType instanceof Callable_ + || $this->valueType instanceof Nullable + ) { + return '(' . $this->valueType . ')[' . $this->offsetType . ']'; + } + + return $this->valueType . '[' . $this->offsetType . ']'; + } +} diff --git a/src/PseudoTypes/OpenResource.php b/src/PseudoTypes/OpenResource.php new file mode 100644 index 0000000..35f292a --- /dev/null +++ b/src/PseudoTypes/OpenResource.php @@ -0,0 +1,39 @@ +fqsen . '>'; + } +} diff --git a/src/PseudoTypes/PropertiesOf.php b/src/PseudoTypes/PropertiesOf.php new file mode 100644 index 0000000..6543368 --- /dev/null +++ b/src/PseudoTypes/PropertiesOf.php @@ -0,0 +1,55 @@ +fqsen = $fqsen; + } + + /** + * Returns the FQSEN associated with this object. + */ + public function getFqsen(): ?Fqsen + { + return $this->fqsen; + } + + public function underlyingType(): Type + { + return new Array_(); + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + return 'properties-of<' . (string) $this->fqsen . '>'; + } +} diff --git a/src/PseudoTypes/ProtectedPropertiesOf.php b/src/PseudoTypes/ProtectedPropertiesOf.php new file mode 100644 index 0000000..de06a81 --- /dev/null +++ b/src/PseudoTypes/ProtectedPropertiesOf.php @@ -0,0 +1,36 @@ +fqsen . '>'; + } +} diff --git a/src/PseudoTypes/PublicPropertiesOf.php b/src/PseudoTypes/PublicPropertiesOf.php new file mode 100644 index 0000000..91060d6 --- /dev/null +++ b/src/PseudoTypes/PublicPropertiesOf.php @@ -0,0 +1,36 @@ +fqsen . '>'; + } +} diff --git a/src/PseudoTypes/ShapeItem.php b/src/PseudoTypes/ShapeItem.php index cb86b92..a378f6e 100644 --- a/src/PseudoTypes/ShapeItem.php +++ b/src/PseudoTypes/ShapeItem.php @@ -13,8 +13,10 @@ abstract class ShapeItem { /** @var string|null */ private $key; + /** @var Type */ private $value; + /** @var bool */ private $optional; diff --git a/src/PseudoTypes/StringValue.php b/src/PseudoTypes/StringValue.php index 98e1046..971ff7a 100644 --- a/src/PseudoTypes/StringValue.php +++ b/src/PseudoTypes/StringValue.php @@ -20,7 +20,7 @@ use function sprintf; /** @psalm-immutable */ -class StringValue implements PseudoType +final class StringValue extends String_ implements PseudoType { /** @var string */ private $value; diff --git a/src/PseudoTypes/ValueOf.php b/src/PseudoTypes/ValueOf.php new file mode 100644 index 0000000..c0a2e97 --- /dev/null +++ b/src/PseudoTypes/ValueOf.php @@ -0,0 +1,48 @@ +valueType = $valueType; + } + + public function getValueType(): Type + { + return $this->valueType; + } + + public function underlyingType(): Type + { + return new Mixed_(); + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + return 'value-of<' . $this->valueType . '>'; + } +} diff --git a/src/TypeResolver.php b/src/TypeResolver.php index a820fee..cab4655 100644 --- a/src/TypeResolver.php +++ b/src/TypeResolver.php @@ -13,105 +13,119 @@ namespace phpDocumentor\Reflection; -use Doctrine\Deprecations\Deprecation; +use phpDocumentor\Reflection\PseudoTypes\ClosedResource; +use phpDocumentor\Reflection\PseudoTypes\IntMaskOf; +use phpDocumentor\Reflection\PseudoTypes\KeyOf; +use phpDocumentor\Reflection\PseudoTypes\OpenResource; +use phpDocumentor\Reflection\PseudoTypes\PrivatePropertiesOf; +use phpDocumentor\Reflection\PseudoTypes\PropertiesOf; +use phpDocumentor\Reflection\PseudoTypes\ProtectedPropertiesOf; +use phpDocumentor\Reflection\PseudoTypes\PublicPropertiesOf; +use phpDocumentor\Reflection\PseudoTypes\ValueOf; +use RuntimeException; use InvalidArgumentException; -use phpDocumentor\Reflection\PseudoTypes\ArrayShape; -use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem; -use phpDocumentor\Reflection\PseudoTypes\CallableString; -use phpDocumentor\Reflection\PseudoTypes\ConstExpression; -use phpDocumentor\Reflection\PseudoTypes\False_; -use phpDocumentor\Reflection\PseudoTypes\FloatValue; -use phpDocumentor\Reflection\PseudoTypes\HtmlEscapedString; -use phpDocumentor\Reflection\PseudoTypes\IntegerRange; -use phpDocumentor\Reflection\PseudoTypes\IntegerValue; -use phpDocumentor\Reflection\PseudoTypes\List_; -use phpDocumentor\Reflection\PseudoTypes\ListShape; -use phpDocumentor\Reflection\PseudoTypes\ListShapeItem; -use phpDocumentor\Reflection\PseudoTypes\LiteralString; -use phpDocumentor\Reflection\PseudoTypes\LowercaseString; -use phpDocumentor\Reflection\PseudoTypes\NegativeInteger; -use phpDocumentor\Reflection\PseudoTypes\NonEmptyArray; -use phpDocumentor\Reflection\PseudoTypes\NonEmptyList; -use phpDocumentor\Reflection\PseudoTypes\NonEmptyLowercaseString; -use phpDocumentor\Reflection\PseudoTypes\NonEmptyString; -use phpDocumentor\Reflection\PseudoTypes\Numeric_; -use phpDocumentor\Reflection\PseudoTypes\NumericString; -use phpDocumentor\Reflection\PseudoTypes\ObjectShape; -use phpDocumentor\Reflection\PseudoTypes\ObjectShapeItem; -use phpDocumentor\Reflection\PseudoTypes\PositiveInteger; -use phpDocumentor\Reflection\PseudoTypes\StringValue; -use phpDocumentor\Reflection\PseudoTypes\TraitString; -use phpDocumentor\Reflection\PseudoTypes\True_; -use phpDocumentor\Reflection\Types\AggregatedType; +use phpDocumentor\Reflection\Type; +use PHPStan\PhpDocParser\Lexer\Lexer; +use Doctrine\Deprecations\Deprecation; +use phpDocumentor\Reflection\Types\This; +use phpDocumentor\Reflection\Types\Null_; +use phpDocumentor\Reflection\Types\Self_; +use phpDocumentor\Reflection\Types\Void_; use phpDocumentor\Reflection\Types\Array_; -use phpDocumentor\Reflection\Types\ArrayKey; -use phpDocumentor\Reflection\Types\Boolean; -use phpDocumentor\Reflection\Types\Callable_; -use phpDocumentor\Reflection\Types\CallableParameter; -use phpDocumentor\Reflection\Types\ClassString; -use phpDocumentor\Reflection\Types\Collection; -use phpDocumentor\Reflection\Types\Compound; -use phpDocumentor\Reflection\Types\Context; -use phpDocumentor\Reflection\Types\Expression; use phpDocumentor\Reflection\Types\Float_; -use phpDocumentor\Reflection\Types\Integer; -use phpDocumentor\Reflection\Types\InterfaceString; -use phpDocumentor\Reflection\Types\Intersection; -use phpDocumentor\Reflection\Types\Iterable_; use phpDocumentor\Reflection\Types\Mixed_; use phpDocumentor\Reflection\Types\Never_; -use phpDocumentor\Reflection\Types\Null_; -use phpDocumentor\Reflection\Types\Nullable; +use phpDocumentor\Reflection\Types\Scalar; +use phpDocumentor\Reflection\Types\Boolean; +use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\Integer; use phpDocumentor\Reflection\Types\Object_; use phpDocumentor\Reflection\Types\Parent_; -use phpDocumentor\Reflection\Types\Resource_; -use phpDocumentor\Reflection\Types\Scalar; -use phpDocumentor\Reflection\Types\Self_; use phpDocumentor\Reflection\Types\Static_; use phpDocumentor\Reflection\Types\String_; -use phpDocumentor\Reflection\Types\This; -use phpDocumentor\Reflection\Types\Void_; -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\TypeNode; +use PHPStan\PhpDocParser\Parser\TypeParser; +use phpDocumentor\Reflection\Types\ArrayKey; +use phpDocumentor\Reflection\Types\Compound; +use phpDocumentor\Reflection\Types\Nullable; +use phpDocumentor\Reflection\Types\Callable_; +use phpDocumentor\Reflection\Types\Iterable_; +use phpDocumentor\Reflection\Types\Resource_; +use phpDocumentor\Reflection\Types\Collection; +use phpDocumentor\Reflection\Types\Expression; +use PHPStan\PhpDocParser\Parser\TokenIterator; +use phpDocumentor\Reflection\PseudoTypes\List_; +use phpDocumentor\Reflection\PseudoTypes\True_; +use phpDocumentor\Reflection\Types\ClassString; +use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; +use phpDocumentor\Reflection\PseudoTypes\False_; +use phpDocumentor\Reflection\Types\Intersection; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; -use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; -use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; -use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode; -use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode; use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode; +use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; +use PHPStan\PhpDocParser\Parser\ConstExprParser; +use PHPStan\PhpDocParser\Parser\ParserException; +use phpDocumentor\Reflection\PseudoTypes\IntMask; +use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; +use phpDocumentor\Reflection\PseudoTypes\Numeric_; +use phpDocumentor\Reflection\Types\AggregatedType; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; -use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; -use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; +use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode; +use phpDocumentor\Reflection\PseudoTypes\ListShape; +use phpDocumentor\Reflection\Types\InterfaceString; +use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; +use phpDocumentor\Reflection\PseudoTypes\ArrayShape; +use phpDocumentor\Reflection\PseudoTypes\FloatValue; +use phpDocumentor\Reflection\PseudoTypes\Conditional; +use phpDocumentor\Reflection\PseudoTypes\ObjectShape; +use phpDocumentor\Reflection\PseudoTypes\StringValue; +use phpDocumentor\Reflection\PseudoTypes\TraitString; +use phpDocumentor\Reflection\Types\CallableParameter; +use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode; +use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use phpDocumentor\Reflection\PseudoTypes\IntegerRange; +use phpDocumentor\Reflection\PseudoTypes\IntegerValue; +use phpDocumentor\Reflection\PseudoTypes\NonEmptyList; +use phpDocumentor\Reflection\PseudoTypes\OffsetAccess; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; +use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeNode; use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode; -use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode; +use phpDocumentor\Reflection\PseudoTypes\ListShapeItem; +use phpDocumentor\Reflection\PseudoTypes\LiteralString; +use phpDocumentor\Reflection\PseudoTypes\NonEmptyArray; +use phpDocumentor\Reflection\PseudoTypes\NumericString; +use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode; -use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode; -use PHPStan\PhpDocParser\Ast\Type\TypeNode; -use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; -use PHPStan\PhpDocParser\Lexer\Lexer; -use PHPStan\PhpDocParser\Parser\ConstExprParser; -use PHPStan\PhpDocParser\Parser\ParserException; -use PHPStan\PhpDocParser\Parser\TokenIterator; -use PHPStan\PhpDocParser\Parser\TypeParser; -use RuntimeException; +use phpDocumentor\Reflection\PseudoTypes\ArrayShapeItem; +use phpDocumentor\Reflection\PseudoTypes\CallableString; +use phpDocumentor\Reflection\PseudoTypes\NonEmptyString; +use phpDocumentor\Reflection\PseudoTypes\ConstExpression; +use phpDocumentor\Reflection\PseudoTypes\LowercaseString; +use phpDocumentor\Reflection\PseudoTypes\NegativeInteger; +use phpDocumentor\Reflection\PseudoTypes\ObjectShapeItem; +use phpDocumentor\Reflection\PseudoTypes\PositiveInteger; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode; +use phpDocumentor\Reflection\PseudoTypes\HtmlEscapedString; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; +use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; +use phpDocumentor\Reflection\PseudoTypes\ConditionalParameter; +use phpDocumentor\Reflection\PseudoTypes\NonEmptyLowercaseString; -use function array_filter; -use function array_key_exists; +use PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode; +use function trim; +use function strpos; +use function sprintf; +use function in_array; use function array_map; -use function array_reverse; -use function class_exists; -use function class_implements; use function get_class; -use function in_array; -use function sprintf; -use function strpos; use function strtolower; -use function trim; +use function array_filter; +use function class_exists; +use function array_reverse; +use function array_key_exists; +use function class_implements; final class TypeResolver { @@ -148,6 +162,8 @@ final class TypeResolver 'array-key' => ArrayKey::class, 'non-empty-array' => NonEmptyArray::class, 'resource' => Resource_::class, + 'closed-resource' => ClosedResource::class, + 'open-resource' => OpenResource::class, 'void' => Void_::class, 'null' => Null_::class, 'scalar' => Scalar::class, @@ -274,6 +290,7 @@ function (ArrayShapeItemNode $item) use ($context): ListShapeItem { default: throw new RuntimeException('Unsupported array shape kind'); } + // no break case ObjectShapeNode::class: return new ObjectShape( ...array_map( @@ -343,8 +360,29 @@ function (TypeNode $nestedType) use ($context): Type { return new This(); case ConditionalTypeNode::class: + return new Conditional( + $this->createType($type->subjectType, $context), + $this->createType($type->targetType, $context), + $this->createType($type->if, $context), + $this->createType($type->else, $context), + $type->negated + ); + case ConditionalTypeForParameterNode::class: + return new ConditionalParameter( + $type->parameterName, + $this->createType($type->targetType, $context), + $this->createType($type->if, $context), + $this->createType($type->else, $context), + $type->negated + ); + case OffsetAccessTypeNode::class: + $offset = $this->createType($type->offset, $context); + $type = $this->createType($type->type, $context); + + return new OffsetAccess($type, $offset); + default: return new Mixed_(); } @@ -380,6 +418,54 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ $subType->getFqsen() ); + case 'properties-of' : + $subType = $this->createType($type->genericTypes[0], $context); + if (!$subType instanceof Object_ || $subType->getFqsen() === null) { + throw new RuntimeException( + $subType . ' is not a class' + ); + } + + return new PropertiesOf( + $subType->getFqsen() + ); + + case 'public-properties-of' : + $subType = $this->createType($type->genericTypes[0], $context); + if (!$subType instanceof Object_ || $subType->getFqsen() === null) { + throw new RuntimeException( + $subType . ' is not a class' + ); + } + + return new PublicPropertiesOf( + $subType->getFqsen() + ); + + case 'protected-properties-of' : + $subType = $this->createType($type->genericTypes[0], $context); + if (!$subType instanceof Object_ || $subType->getFqsen() === null) { + throw new RuntimeException( + $subType . ' is not a class' + ); + } + + return new ProtectedPropertiesOf( + $subType->getFqsen() + ); + + case 'private-properties-of' : + $subType = $this->createType($type->genericTypes[0], $context); + if (!$subType instanceof Object_ || $subType->getFqsen() === null) { + throw new RuntimeException( + $subType . ' is not a class' + ); + } + + return new PrivatePropertiesOf( + $subType->getFqsen() + ); + case 'list': return new List_( $this->createType($type->genericTypes[0], $context) @@ -390,6 +476,16 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ $this->createType($type->genericTypes[0], $context) ); + case 'key-of': + return new KeyOf( + $this->createType($type->genericTypes[0], $context) + ); + + case 'value-of': + return new ValueOf( + $this->createType($type->genericTypes[0], $context) + ); + case 'int': if (isset($type->genericTypes[1]) === false) { throw new RuntimeException('int has not the correct format'); @@ -397,6 +493,26 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]); + case 'int-mask': + return new IntMask( + array_map( + function (TypeNode $genericType) use ($context): Type { + return $this->createType($genericType, $context); + }, + $type->genericTypes + ) + ); + + case 'int-mask-of': + return new IntMaskOf( + array_map( + function (TypeNode $genericType) use ($context): Type { + return $this->createType($genericType, $context); + }, + $type->genericTypes + ) + ); + case 'iterable': return new Iterable_( ...array_reverse( diff --git a/src/Types/AbstractList.php b/src/Types/AbstractList.php index ee8ace8..a0f79d2 100644 --- a/src/Types/AbstractList.php +++ b/src/Types/AbstractList.php @@ -61,6 +61,14 @@ public function getValueType(): Type return $this->valueType; } + /** + * Returns key is a list + */ + public function isListKey(): bool + { + return $this->keyType !== null; + } + /** * Returns a rendered output of the Type as it would be used in a DocBlock. */ diff --git a/src/Types/ContextFactory.php b/src/Types/ContextFactory.php index 2018ff5..446781f 100644 --- a/src/Types/ContextFactory.php +++ b/src/Types/ContextFactory.php @@ -14,39 +14,40 @@ namespace phpDocumentor\Reflection\Types; use ArrayIterator; -use InvalidArgumentException; +use RuntimeException; +use Reflector; use ReflectionClass; -use ReflectionClassConstant; use ReflectionMethod; -use ReflectionParameter; +use ReflectionFunction; use ReflectionProperty; -use Reflector; -use RuntimeException; +use ReflectionParameter; +use ReflectionAttribute; +use ReflectionClassConstant; +use InvalidArgumentException; use UnexpectedValueException; +use const T_AS; +use const T_USE; +use const T_TRAIT; +use const T_CLASS; +use const T_STRING; +use const T_NAMESPACE; +use const T_CURLY_OPEN; +use const T_NS_SEPARATOR; +use const T_NAME_QUALIFIED; +use const T_NAME_FULLY_QUALIFIED; +use const T_DOLLAR_OPEN_CURLY_BRACES; +use function trim; use function define; +use function substr; use function defined; -use function file_exists; -use function file_get_contents; -use function get_class; +use function strrpos; use function in_array; +use function get_class; use function is_string; -use function strrpos; -use function substr; +use function file_exists; use function token_get_all; -use function trim; - -use const T_AS; -use const T_CLASS; -use const T_CURLY_OPEN; -use const T_DOLLAR_OPEN_CURLY_BRACES; -use const T_NAME_FULLY_QUALIFIED; -use const T_NAME_QUALIFIED; -use const T_NAMESPACE; -use const T_NS_SEPARATOR; -use const T_STRING; -use const T_TRAIT; -use const T_USE; +use function file_get_contents; if (!defined('T_NAME_QUALIFIED')) { define('T_NAME_QUALIFIED', 10001); @@ -103,6 +104,14 @@ public function createFromReflector(Reflector $reflector): Context return $this->createFromReflectionClassConstant($reflector); } + if ($reflector instanceof ReflectionFunction) { + return $this->createFromReflectionFunction($reflector); + } + + if ($reflector instanceof ReflectionAttribute) { + return $this->createFromReflectionAttribute($reflector); + } + throw new UnexpectedValueException('Unhandled \Reflector instance given: ' . get_class($reflector)); } @@ -159,6 +168,31 @@ private function createFromReflectionClass(ReflectionClass $class): Context return new Context($namespace, []); } + private function createFromReflectionFunction(ReflectionFunction $function): Context + { + $fileName = $function->getFileName(); + $namespace = $function->getNamespaceName(); + + if (is_string($fileName) && file_exists($fileName)) { + $contents = file_get_contents($fileName); + if ($contents === false) { + throw new RuntimeException('Unable to read file "' . $fileName . '"'); + } + + return $this->createForNamespace($namespace, $contents); + } + + return new Context($namespace, []); + } + + private function createFromReflectionAttribute(ReflectionAttribute $attribute): Context + { + $name = $attribute->getName(); + $class = new ReflectionClass($name); + + return $this->createFromReflectionClass($class); + } + /** * Build a Context for a namespace in the provided file contents. * diff --git a/src/Types/Mixed_.php b/src/Types/Mixed_.php index 56d1b6d..908f45e 100644 --- a/src/Types/Mixed_.php +++ b/src/Types/Mixed_.php @@ -20,7 +20,7 @@ * * @psalm-immutable */ -final class Mixed_ implements Type +class Mixed_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock. diff --git a/src/Types/Object_.php b/src/Types/Object_.php index 90dee57..87e33d4 100644 --- a/src/Types/Object_.php +++ b/src/Types/Object_.php @@ -28,7 +28,7 @@ * * @psalm-immutable */ -final class Object_ implements Type +class Object_ implements Type { /** @var Fqsen|null */ private $fqsen; diff --git a/src/Types/Resource_.php b/src/Types/Resource_.php index 1998ee0..46ad777 100644 --- a/src/Types/Resource_.php +++ b/src/Types/Resource_.php @@ -20,7 +20,7 @@ * * @psalm-immutable */ -final class Resource_ implements Type +class Resource_ implements Type { /** * Returns a rendered output of the Type as it would be used in a DocBlock.