From 92acc26aa2b9a6380a4ff9dcf78498c6f6cff6a3 Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 20 Jul 2024 20:34:03 +0330 Subject: [PATCH 01/12] Add conditional type --- src/PseudoTypes/AbstractConditional.php | 71 ++++++++++++++++++++++++ src/PseudoTypes/Conditional.php | 51 +++++++++++++++++ src/PseudoTypes/ConditionalParameter.php | 51 +++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 src/PseudoTypes/AbstractConditional.php create mode 100644 src/PseudoTypes/Conditional.php create mode 100644 src/PseudoTypes/ConditionalParameter.php diff --git a/src/PseudoTypes/AbstractConditional.php b/src/PseudoTypes/AbstractConditional.php new file mode 100644 index 0000000..39a6d50 --- /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/Conditional.php b/src/PseudoTypes/Conditional.php new file mode 100644 index 0000000..44e0dec --- /dev/null +++ b/src/PseudoTypes/Conditional.php @@ -0,0 +1,51 @@ +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..7030151 --- /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 + ); + } +} From 5abbe96f5f79789c2dbf1df33bd4c56bf58da31b Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 20 Jul 2024 20:34:40 +0330 Subject: [PATCH 02/12] Add *Of type --- src/PseudoTypes/AggregatedPseudoType.php | 126 ++++++++++++++++++++++ src/PseudoTypes/IntMask.php | 40 +++++++ src/PseudoTypes/IntMaskOf.php | 40 +++++++ src/PseudoTypes/KeyOf.php | 48 +++++++++ src/PseudoTypes/PrivatePropertiesOf.php | 36 +++++++ src/PseudoTypes/PropertiesOf.php | 55 ++++++++++ src/PseudoTypes/ProtectedPropertiesOf.php | 36 +++++++ src/PseudoTypes/PublicPropertiesOf.php | 36 +++++++ src/PseudoTypes/ValueOf.php | 48 +++++++++ 9 files changed, 465 insertions(+) create mode 100644 src/PseudoTypes/AggregatedPseudoType.php create mode 100644 src/PseudoTypes/IntMask.php create mode 100644 src/PseudoTypes/IntMaskOf.php create mode 100644 src/PseudoTypes/KeyOf.php create mode 100644 src/PseudoTypes/PrivatePropertiesOf.php create mode 100644 src/PseudoTypes/PropertiesOf.php create mode 100644 src/PseudoTypes/ProtectedPropertiesOf.php create mode 100644 src/PseudoTypes/PublicPropertiesOf.php create mode 100644 src/PseudoTypes/ValueOf.php diff --git a/src/PseudoTypes/AggregatedPseudoType.php b/src/PseudoTypes/AggregatedPseudoType.php new file mode 100644 index 0000000..4d5c2b1 --- /dev/null +++ b/src/PseudoTypes/AggregatedPseudoType.php @@ -0,0 +1,126 @@ + + */ +abstract class AggregatedPseudoType implements PseudoType, IteratorAggregate +{ + /** + * @psalm-allow-private-mutation + * @var array + */ + private $types = []; + + /** @var string */ + private $token; + + /** + * @param array $types + */ + public function __construct(array $types, string $token) + { + foreach ($types as $type) { + $this->add($type); + } + + $this->token = $token; + } + + /** + * Returns the type at the given index. + */ + public function get(int $index): ?Type + { + if (!$this->has($index)) { + return null; + } + + return $this->types[$index]; + } + + /** + * Tests if this compound type has a type with the given index. + */ + public function has(int $index): bool + { + return array_key_exists($index, $this->types); + } + + /** + * Tests if this compound type contains the given type. + */ + public function contains(Type $type): bool + { + foreach ($this->types as $typePart) { + // if the type is duplicate; do not add it + if ((string) $typePart === (string) $type) { + return true; + } + } + + return false; + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + */ + public function __toString(): string + { + return implode($this->token, $this->types); + } + + /** + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->types); + } + + /** + * @psalm-suppress ImpureMethodCall + */ + private function add(Type $type): void + { + if ($type instanceof static) { + foreach ($type->getIterator() as $subType) { + $this->add($subType); + } + + return; + } + + // if the type is duplicate; do not add it + if ($this->contains($type)) { + return; + } + + $this->types[] = $type; + } +} diff --git a/src/PseudoTypes/IntMask.php b/src/PseudoTypes/IntMask.php new file mode 100644 index 0000000..533a725 --- /dev/null +++ b/src/PseudoTypes/IntMask.php @@ -0,0 +1,40 @@ +'; + } +} diff --git a/src/PseudoTypes/IntMaskOf.php b/src/PseudoTypes/IntMaskOf.php new file mode 100644 index 0000000..f9815a4 --- /dev/null +++ b/src/PseudoTypes/IntMaskOf.php @@ -0,0 +1,40 @@ +'; + } +} diff --git a/src/PseudoTypes/KeyOf.php b/src/PseudoTypes/KeyOf.php new file mode 100644 index 0000000..ebcc4c7 --- /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/PrivatePropertiesOf.php b/src/PseudoTypes/PrivatePropertiesOf.php new file mode 100644 index 0000000..50e663e --- /dev/null +++ b/src/PseudoTypes/PrivatePropertiesOf.php @@ -0,0 +1,36 @@ +fqsen . '>'; + } +} diff --git a/src/PseudoTypes/PropertiesOf.php b/src/PseudoTypes/PropertiesOf.php new file mode 100644 index 0000000..f565945 --- /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/ValueOf.php b/src/PseudoTypes/ValueOf.php new file mode 100644 index 0000000..eca04ba --- /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 . '>'; + } +} From 6d995f7891c5918118b6818bf8a51478e6beebb8 Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 20 Jul 2024 20:34:56 +0330 Subject: [PATCH 03/12] Add close/open-resource type --- src/PseudoTypes/ClosedResource.php | 39 ++++++++++++++++++++++++++++++ src/PseudoTypes/OpenResource.php | 39 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/PseudoTypes/ClosedResource.php create mode 100644 src/PseudoTypes/OpenResource.php diff --git a/src/PseudoTypes/ClosedResource.php b/src/PseudoTypes/ClosedResource.php new file mode 100644 index 0000000..151b46e --- /dev/null +++ b/src/PseudoTypes/ClosedResource.php @@ -0,0 +1,39 @@ + Date: Sat, 20 Jul 2024 20:35:08 +0330 Subject: [PATCH 04/12] Add offsetaccess type --- src/PseudoTypes/OffsetAccess.php | 66 ++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/PseudoTypes/OffsetAccess.php diff --git a/src/PseudoTypes/OffsetAccess.php b/src/PseudoTypes/OffsetAccess.php new file mode 100644 index 0000000..2ba0a62 --- /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 . ']'; + } +} From a32a86f894228afa48dd1a29bc4e4108050f6764 Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 20 Jul 2024 20:35:38 +0330 Subject: [PATCH 05/12] Add method isListKey --- src/Types/AbstractList.php | 8 ++++++++ 1 file changed, 8 insertions(+) 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. */ From 1e56a3d77e0edbf2353ff6d6f7b9c6ed3c555e6a Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 20 Jul 2024 20:35:58 +0330 Subject: [PATCH 06/12] Handle all added types --- src/TypeResolver.php | 275 ++++++++++++++++++++++++++++++------------- 1 file changed, 192 insertions(+), 83 deletions(-) diff --git a/src/TypeResolver.php b/src/TypeResolver.php index a820fee..209d20a 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 function trim; +use function strpos; +use function sprintf; +use RuntimeException; +use function in_array; +use function array_map; +use function get_class; +use function strtolower; +use function array_filter; +use function class_exists; +use function array_reverse; 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 function array_key_exists; +use function class_implements; +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 function array_filter; -use function array_key_exists; -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 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 PHPStan\PhpDocParser\Ast\Type\ConditionalTypeForParameterNode; 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, @@ -344,7 +360,22 @@ function (TypeNode $nestedType) use ($context): Type { case ConditionalTypeNode::class: case ConditionalTypeForParameterNode::class: + [$class, $subject] = $type instanceof ConditionalTypeForParameterNode + ? [ConditionalParameter::class, $type->parameterName] + : [Conditional::class, $this->createType($type->subjectType, $context)]; + + $target = $this->createType($type->targetType, $context); + $if = $this->createType($type->if, $context); + $else = $this->createType($type->else, $context); + + return new $class($subject, $target, $if, $else, $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 +411,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) @@ -389,6 +468,16 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ return new NonEmptyList( $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) { @@ -397,6 +486,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( From 86b7e49ae26d037950b18927ca50df58038d0278 Mon Sep 17 00:00:00 2001 From: AhJ Date: Wed, 24 Jul 2024 09:49:23 +0330 Subject: [PATCH 07/12] Extend PseudoType from Type classes --- src/PseudoTypes/AbstractConditional.php | 2 +- src/PseudoTypes/AggregatedPseudoType.php | 126 ----------------------- src/PseudoTypes/ArrayShape.php | 2 +- src/PseudoTypes/ClosedResource.php | 2 +- src/PseudoTypes/ConstExpression.php | 2 +- src/PseudoTypes/FloatValue.php | 2 +- src/PseudoTypes/IntMask.php | 8 +- src/PseudoTypes/IntMaskOf.php | 8 +- src/PseudoTypes/IntegerValue.php | 2 +- src/PseudoTypes/KeyOf.php | 2 +- src/PseudoTypes/ObjectShape.php | 2 +- src/PseudoTypes/OffsetAccess.php | 2 +- src/PseudoTypes/OpenResource.php | 2 +- src/PseudoTypes/PropertiesOf.php | 2 +- src/PseudoTypes/ShapeItem.php | 2 + src/PseudoTypes/StringValue.php | 2 +- src/PseudoTypes/ValueOf.php | 2 +- src/Types/Mixed_.php | 2 +- src/Types/Object_.php | 2 +- src/Types/Resource_.php | 2 +- 20 files changed, 28 insertions(+), 148 deletions(-) delete mode 100644 src/PseudoTypes/AggregatedPseudoType.php diff --git a/src/PseudoTypes/AbstractConditional.php b/src/PseudoTypes/AbstractConditional.php index 39a6d50..82ccbd9 100644 --- a/src/PseudoTypes/AbstractConditional.php +++ b/src/PseudoTypes/AbstractConditional.php @@ -22,7 +22,7 @@ * * @psalm-immutable */ -abstract class AbstractConditional implements PseudoType +abstract class AbstractConditional extends Mixed_ implements PseudoType { /** @var Type */ private $target; diff --git a/src/PseudoTypes/AggregatedPseudoType.php b/src/PseudoTypes/AggregatedPseudoType.php deleted file mode 100644 index 4d5c2b1..0000000 --- a/src/PseudoTypes/AggregatedPseudoType.php +++ /dev/null @@ -1,126 +0,0 @@ - - */ -abstract class AggregatedPseudoType implements PseudoType, IteratorAggregate -{ - /** - * @psalm-allow-private-mutation - * @var array - */ - private $types = []; - - /** @var string */ - private $token; - - /** - * @param array $types - */ - public function __construct(array $types, string $token) - { - foreach ($types as $type) { - $this->add($type); - } - - $this->token = $token; - } - - /** - * Returns the type at the given index. - */ - public function get(int $index): ?Type - { - if (!$this->has($index)) { - return null; - } - - return $this->types[$index]; - } - - /** - * Tests if this compound type has a type with the given index. - */ - public function has(int $index): bool - { - return array_key_exists($index, $this->types); - } - - /** - * Tests if this compound type contains the given type. - */ - public function contains(Type $type): bool - { - foreach ($this->types as $typePart) { - // if the type is duplicate; do not add it - if ((string) $typePart === (string) $type) { - return true; - } - } - - return false; - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - */ - public function __toString(): string - { - return implode($this->token, $this->types); - } - - /** - * @return ArrayIterator - */ - public function getIterator(): ArrayIterator - { - return new ArrayIterator($this->types); - } - - /** - * @psalm-suppress ImpureMethodCall - */ - private function add(Type $type): void - { - if ($type instanceof static) { - foreach ($type->getIterator() as $subType) { - $this->add($subType); - } - - return; - } - - // if the type is duplicate; do not add it - if ($this->contains($type)) { - return; - } - - $this->types[] = $type; - } -} 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 index 151b46e..3658ec1 100644 --- a/src/PseudoTypes/ClosedResource.php +++ b/src/PseudoTypes/ClosedResource.php @@ -22,7 +22,7 @@ * * @psalm-immutable */ -final class ClosedResource implements PseudoType +final class ClosedResource extends Resource_ implements PseudoType { public function underlyingType(): Type { 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..0c3cc96 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 +class FloatValue extends Float_ implements PseudoType { /** @var float */ private $value; diff --git a/src/PseudoTypes/IntMask.php b/src/PseudoTypes/IntMask.php index 533a725..ba1f975 100644 --- a/src/PseudoTypes/IntMask.php +++ b/src/PseudoTypes/IntMask.php @@ -14,11 +14,13 @@ namespace phpDocumentor\Reflection\PseudoTypes; use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\PseudoType; use phpDocumentor\Reflection\Types\Integer; -use phpDocumentor\Reflection\PseudoTypes\AggregatedPseudoType; +use phpDocumentor\Reflection\Types\Compound; +use phpDocumentor\Reflection\Types\AggregatedType; /** @psalm-immutable */ -final class IntMask extends AggregatedPseudoType +final class IntMask extends AggregatedType implements PseudoType { public function __construct(array $types) { @@ -27,7 +29,7 @@ public function __construct(array $types) public function underlyingType(): Type { - return new Integer(); + return new Compound([new Integer()]); } /** diff --git a/src/PseudoTypes/IntMaskOf.php b/src/PseudoTypes/IntMaskOf.php index f9815a4..08130c7 100644 --- a/src/PseudoTypes/IntMaskOf.php +++ b/src/PseudoTypes/IntMaskOf.php @@ -14,11 +14,13 @@ namespace phpDocumentor\Reflection\PseudoTypes; use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\PseudoType; use phpDocumentor\Reflection\Types\Integer; -use phpDocumentor\Reflection\PseudoTypes\AggregatedPseudoType; +use phpDocumentor\Reflection\Types\Compound; +use phpDocumentor\Reflection\Types\AggregatedType; /** @psalm-immutable */ -final class IntMaskOf extends AggregatedPseudoType +final class IntMaskOf extends AggregatedType implements PseudoType { public function __construct(array $types) { @@ -27,7 +29,7 @@ public function __construct(array $types) public function underlyingType(): Type { - return new Integer(); + return new Compound([new Integer()]); } /** 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 index ebcc4c7..89a7635 100644 --- a/src/PseudoTypes/KeyOf.php +++ b/src/PseudoTypes/KeyOf.php @@ -18,7 +18,7 @@ use phpDocumentor\Reflection\PseudoType; /** @psalm-immutable */ -final class KeyOf implements PseudoType +final class KeyOf extends Mixed_ implements PseudoType { /** @var Type */ protected $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 index 2ba0a62..f752186 100644 --- a/src/PseudoTypes/OffsetAccess.php +++ b/src/PseudoTypes/OffsetAccess.php @@ -20,7 +20,7 @@ use phpDocumentor\Reflection\Types\Nullable; /** @psalm-immutable */ -final class OffsetAccess implements PseudoType +final class OffsetAccess extends Mixed_ implements PseudoType { /** @var Type */ private $valueType; diff --git a/src/PseudoTypes/OpenResource.php b/src/PseudoTypes/OpenResource.php index e279991..35f292a 100644 --- a/src/PseudoTypes/OpenResource.php +++ b/src/PseudoTypes/OpenResource.php @@ -22,7 +22,7 @@ * * @psalm-immutable */ -final class OpenResource implements PseudoType +final class OpenResource extends Resource_ implements PseudoType { public function underlyingType(): Type { diff --git a/src/PseudoTypes/PropertiesOf.php b/src/PseudoTypes/PropertiesOf.php index f565945..6a77f82 100644 --- a/src/PseudoTypes/PropertiesOf.php +++ b/src/PseudoTypes/PropertiesOf.php @@ -19,7 +19,7 @@ use phpDocumentor\Reflection\Types\Array_; /** @psalm-immutable */ -class PropertiesOf implements PseudoType +class PropertiesOf extends Array_ implements PseudoType { /** @var Fqsen|null */ private $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 index eca04ba..c0a2e97 100644 --- a/src/PseudoTypes/ValueOf.php +++ b/src/PseudoTypes/ValueOf.php @@ -18,7 +18,7 @@ use phpDocumentor\Reflection\PseudoType; /** @psalm-immutable */ -final class ValueOf implements PseudoType +final class ValueOf extends Mixed_ implements PseudoType { /** @var Type */ protected $valueType; 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. From 37622a0cc8487ee44c16e4bfc0b311babdaca6c6 Mon Sep 17 00:00:00 2001 From: AhJ Date: Wed, 24 Jul 2024 09:52:36 +0330 Subject: [PATCH 08/12] Extend PseudoType from Type classes --- src/PseudoTypes/FloatValue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PseudoTypes/FloatValue.php b/src/PseudoTypes/FloatValue.php index 0c3cc96..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 extends Float_ implements PseudoType +final class FloatValue extends Float_ implements PseudoType { /** @var float */ private $value; From 91a71d306e04590de04498aa0b1b8f1a1bcd9637 Mon Sep 17 00:00:00 2001 From: AhJ Date: Wed, 24 Jul 2024 09:59:05 +0330 Subject: [PATCH 09/12] Cleanup --- src/TypeResolver.php | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/TypeResolver.php b/src/TypeResolver.php index 209d20a..2d52f3c 100644 --- a/src/TypeResolver.php +++ b/src/TypeResolver.php @@ -359,16 +359,22 @@ function (TypeNode $nestedType) use ($context): Type { return new This(); case ConditionalTypeNode::class: - case ConditionalTypeForParameterNode::class: - [$class, $subject] = $type instanceof ConditionalTypeForParameterNode - ? [ConditionalParameter::class, $type->parameterName] - : [Conditional::class, $this->createType($type->subjectType, $context)]; - - $target = $this->createType($type->targetType, $context); - $if = $this->createType($type->if, $context); - $else = $this->createType($type->else, $context); + 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 + ); - return new $class($subject, $target, $if, $else, $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); From 983ad1220e5d995525e73f3ecca9030565030d42 Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 27 Jul 2024 02:43:05 +0330 Subject: [PATCH 10/12] fixing --- src/PseudoTypes/AbstractConditional.php | 8 ++++---- src/PseudoTypes/PropertiesOf.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PseudoTypes/AbstractConditional.php b/src/PseudoTypes/AbstractConditional.php index 82ccbd9..b23e362 100644 --- a/src/PseudoTypes/AbstractConditional.php +++ b/src/PseudoTypes/AbstractConditional.php @@ -25,16 +25,16 @@ abstract class AbstractConditional extends Mixed_ implements PseudoType { /** @var Type */ - private $target; + protected $target; /** @var Type */ - private $if; + protected $if; /** @var Type */ - private $else; + protected $else; /** @var bool */ - private $negated; + protected $negated; public function __construct(Type $target, Type $if, Type $else, bool $negated) { diff --git a/src/PseudoTypes/PropertiesOf.php b/src/PseudoTypes/PropertiesOf.php index 6a77f82..6543368 100644 --- a/src/PseudoTypes/PropertiesOf.php +++ b/src/PseudoTypes/PropertiesOf.php @@ -22,7 +22,7 @@ class PropertiesOf extends Array_ implements PseudoType { /** @var Fqsen|null */ - private $fqsen; + protected $fqsen; /** * Initializes this representation of a class string with the given Fqsen. From 6d5ac035b7babd5edd61dec95710a23005a8b4e5 Mon Sep 17 00:00:00 2001 From: AhJ Date: Sat, 27 Jul 2024 16:37:05 +0330 Subject: [PATCH 11/12] fix phpcs-fix --- src/PseudoTypes/Conditional.php | 16 ++++++------ src/PseudoTypes/ConditionalParameter.php | 16 ++++++------ src/PseudoTypes/OffsetAccess.php | 14 +++++----- src/TypeResolver.php | 33 ++++++++++++------------ 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/PseudoTypes/Conditional.php b/src/PseudoTypes/Conditional.php index 44e0dec..bb610c5 100644 --- a/src/PseudoTypes/Conditional.php +++ b/src/PseudoTypes/Conditional.php @@ -40,12 +40,12 @@ public function getSubject(): Type public function __toString(): string { return sprintf( - '(%s %s %s ? %s : %s)', - $this->subject, - $this->negated ? 'is not' : 'is', - $this->target, - $this->if, - $this->else - ); - } + '(%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 index 7030151..b41eaec 100644 --- a/src/PseudoTypes/ConditionalParameter.php +++ b/src/PseudoTypes/ConditionalParameter.php @@ -40,12 +40,12 @@ public function getParameterName(): string public function __toString(): string { return sprintf( - '(%s %s %s ? %s : %s)', - $this->parameter, - $this->negated ? 'is not' : 'is', - $this->target, - $this->if, - $this->else - ); - } + '(%s %s %s ? %s : %s)', + $this->parameter, + $this->negated ? 'is not' : 'is', + $this->target, + $this->if, + $this->else + ); + } } diff --git a/src/PseudoTypes/OffsetAccess.php b/src/PseudoTypes/OffsetAccess.php index f752186..6b2587d 100644 --- a/src/PseudoTypes/OffsetAccess.php +++ b/src/PseudoTypes/OffsetAccess.php @@ -54,13 +54,13 @@ public function underlyingType(): Type */ public function __toString(): string { - if ( - $this->valueType instanceof Callable_ - || $this->valueType instanceof Nullable - ) { - return '(' . $this->valueType . ')[' . $this->offsetType . ']'; - } + if ( + $this->valueType instanceof Callable_ + || $this->valueType instanceof Nullable + ) { + return '(' . $this->valueType . ')[' . $this->offsetType . ']'; + } - return $this->valueType . '[' . $this->offsetType . ']'; + return $this->valueType . '[' . $this->offsetType . ']'; } } diff --git a/src/TypeResolver.php b/src/TypeResolver.php index 2d52f3c..cab4655 100644 --- a/src/TypeResolver.php +++ b/src/TypeResolver.php @@ -22,20 +22,8 @@ use phpDocumentor\Reflection\PseudoTypes\ProtectedPropertiesOf; use phpDocumentor\Reflection\PseudoTypes\PublicPropertiesOf; use phpDocumentor\Reflection\PseudoTypes\ValueOf; -use function trim; -use function strpos; -use function sprintf; use RuntimeException; -use function in_array; -use function array_map; -use function get_class; -use function strtolower; -use function array_filter; -use function class_exists; -use function array_reverse; use InvalidArgumentException; -use function array_key_exists; -use function class_implements; use phpDocumentor\Reflection\Type; use PHPStan\PhpDocParser\Lexer\Lexer; use Doctrine\Deprecations\Deprecation; @@ -112,7 +100,6 @@ 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; @@ -125,7 +112,20 @@ use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode; use phpDocumentor\Reflection\PseudoTypes\ConditionalParameter; use phpDocumentor\Reflection\PseudoTypes\NonEmptyLowercaseString; + 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 get_class; +use function strtolower; +use function array_filter; +use function class_exists; +use function array_reverse; +use function array_key_exists; +use function class_implements; final class TypeResolver { @@ -290,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( @@ -464,7 +465,7 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ return new PrivatePropertiesOf( $subType->getFqsen() ); - + case 'list': return new List_( $this->createType($type->genericTypes[0], $context) @@ -474,12 +475,12 @@ private function createFromGeneric(GenericTypeNode $type, Context $context): Typ return new NonEmptyList( $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) From 0956ca3335f8785727e269cf278fa8062b831608 Mon Sep 17 00:00:00 2001 From: AhJ Date: Mon, 29 Jul 2024 15:39:08 +0330 Subject: [PATCH 12/12] Handle more Reflector class --- src/Types/ContextFactory.php | 80 +++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 23 deletions(-) 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. *