Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ Here are some of the features supported:
- 🔴 Support for `strict_types` configurations
- 🔴 Conditional types
- 🔴 Type aliases
- 🔴 Extensions reflection (SPL, Zip, DS, DOM, etc.)
- 🔴 Template type inference for functions

### 🐞 How reliable is this?
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ parameters:

reportUnmatchedIgnoredErrors: false
ignoreErrors:
# They do have the type specified. I don't know what PHPStan is on about here.
- '#Method GoodPhp\\Reflection\\Reflection\\Methods\\MergedInheritanceMethodReflection::invoke\(\) has parameter \$args with no type specified.#'
- '#Method GoodPhp\\Reflection\\Reflection\\Methods\\MergedInheritanceMethodReflection::invokeLax\(\) has parameter \$args with no type specified.#'
# PHPStan doesn't understand that Collection::values() produces a list
- '#Method .*::.* should return list<(.*)> but returns array<(int, )?\1>.#i'
- '#Parameter [\$|\#].* of .* expects list<(.*)>(|.*)?, (array|non-empty-array)<(int, )?\1> given.#i'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition\MethodDefinition;
use GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition\TypeParameterDefinition;
use GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition\UsedTraitsDefinition;
use GoodPhp\Reflection\Reflection\TypeSource;
use GoodPhp\Reflection\Type\Combinatorial\ExpandedType;
use GoodPhp\Reflection\Type\NamedType;
use GoodPhp\Reflection\Type\PrimitiveType;
Expand All @@ -21,6 +22,7 @@
use GoodPhp\Reflection\Type\Template\TemplateType;
use GoodPhp\Reflection\Type\Template\TemplateTypeVariance;
use GoodPhp\Reflection\Util\Lazy\Lazy;
use IteratorAggregate;
use Traversable;

use function GoodPhp\Reflection\Util\Lazy\lazy;
Expand All @@ -45,6 +47,7 @@ public function __construct()
typeParameters: [],
parameters: [],
returnType: PrimitiveType::integer(),
returnTypeSource: TypeSource::PHP_DOC,
),
]
)),
Expand Down Expand Up @@ -77,10 +80,12 @@ public function __construct()
type: new TemplateType(
name: 'TKey',
),
typeSource: TypeSource::PHP_DOC,
hasDefaultValue: false,
),
],
returnType: PrimitiveType::boolean(),
returnTypeSource: TypeSource::PHP_DOC,
),
new MethodDefinition(
name: 'offsetGet',
Expand All @@ -91,6 +96,7 @@ public function __construct()
type: new TemplateType(
name: 'TKey',
),
typeSource: TypeSource::PHP_DOC,
hasDefaultValue: false,
),
],
Expand All @@ -99,6 +105,7 @@ public function __construct()
name: 'TValue',
)
),
returnTypeSource: TypeSource::PHP_DOC,
),
new MethodDefinition(
name: 'offsetSet',
Expand All @@ -111,17 +118,20 @@ public function __construct()
name: 'TKey',
)
),
typeSource: TypeSource::PHP_DOC,
hasDefaultValue: false,
),
new FunctionParameterDefinition(
name: 'value',
type: new TemplateType(
name: 'TValue',
),
typeSource: TypeSource::PHP_DOC,
hasDefaultValue: false,
),
],
returnType: VoidType::get(),
returnTypeSource: TypeSource::PHP_DOC,
),
new MethodDefinition(
name: 'offsetUnset',
Expand All @@ -132,10 +142,12 @@ public function __construct()
type: new TemplateType(
name: 'TKey',
),
typeSource: TypeSource::PHP_DOC,
hasDefaultValue: false,
),
],
returnType: VoidType::get(),
returnTypeSource: TypeSource::PHP_DOC,
),
]
)),
Expand All @@ -148,7 +160,7 @@ public function __construct()
name: 'TKey',
variadic: false,
upperBound: null,
variance: TemplateTypeVariance::COVARIANT,
variance: TemplateTypeVariance::INVARIANT,
),
new TypeParameterDefinition(
name: 'TValue',
Expand All @@ -169,6 +181,51 @@ public function __construct()
],
methods: []
)),
IteratorAggregate::class => lazy(fn () => new InterfaceTypeDefinition(
qualifiedName: IteratorAggregate::class,
fileName: null,
builtIn: true,
typeParameters: [
new TypeParameterDefinition(
name: 'TKey',
variadic: false,
upperBound: null,
variance: TemplateTypeVariance::INVARIANT,
),
new TypeParameterDefinition(
name: 'TValue',
variadic: false,
upperBound: null,
variance: TemplateTypeVariance::COVARIANT,
),
],
extends: [
new NamedType(Traversable::class, [
new TemplateType(
name: 'TKey',
),
new TemplateType(
name: 'TValue',
),
]),
],
methods: [
new MethodDefinition(
name: 'getIterator',
typeParameters: [],
parameters: [],
returnType: new NamedType(Traversable::class, [
new TemplateType(
name: 'TKey',
),
new TemplateType(
name: 'TValue',
),
]),
returnTypeSource: TypeSource::PHP_DOC,
),
]
)),
Closure::class => lazy(fn () => new ClassTypeDefinition(
qualifiedName: Closure::class,
fileName: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition\UsedTraitAliasDefinition;
use GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition\UsedTraitDefinition;
use GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition\UsedTraitsDefinition;
use GoodPhp\Reflection\Reflection\TypeSource;
use GoodPhp\Reflection\Type\NamedType;
use GoodPhp\Reflection\Type\Template\TemplateTypeVariance;
use GoodPhp\Reflection\Type\Type;
Expand Down Expand Up @@ -70,18 +71,29 @@ class_exists($type), interface_exists($type), trait_exists($type) => $this->forC
};
}

/**
* @return array{ Type|null, TypeSource|null }
*/
public function map(
ReflectionType|string|null $nativeType,
?TypeNode $phpDocType,
TypeContext $context
): ?Type {
if (!$nativeType && !$phpDocType) {
return null;
): array {
if ($phpDocType) {
return [
$this->phpDocTypeMapper->map($phpDocType, $context),
TypeSource::PHP_DOC,
];
}

return $phpDocType ?
$this->phpDocTypeMapper->map($phpDocType, $context) :
$this->nativeTypeMapper->map($nativeType, $context);
if ($nativeType) {
return [
$this->nativeTypeMapper->map($nativeType, $context),
TypeSource::NATIVE,
];
}

return [null, null];
}

/**
Expand Down Expand Up @@ -217,13 +229,16 @@ private function properties(ReflectionClass $reflection, TypeContext $context):
$phpDocType = $constructorPhpDoc->firstParamTagValue($property->getName())?->type;
}

[$type, $typeSource] = $this->map(
$property->getType(),
$phpDocType,
$context
);

return new PropertyDefinition(
name: $property->getName(),
type: $this->map(
$property->getType(),
$phpDocType,
$context
),
type: $type,
typeSource: $typeSource,
hasDefaultValue: $property->hasDefaultValue(),
isPromoted: $property->isPromoted(),
);
Expand Down Expand Up @@ -251,15 +266,18 @@ private function methods(ReflectionClass $reflection, TypeContext $context): arr
$this->lazyTypeParameters($phpDoc, $context)
);

[$returnType, $returnTypeSource] = $this->map(
$method->getReturnType(),
$phpDocType,
$context,
);

return new MethodDefinition(
name: $method->getName(),
typeParameters: $this->typeParameters($phpDoc, $context),
parameters: $this->functionParameters($method, $phpDoc, $context),
returnType: $this->map(
$method->getReturnType(),
$phpDocType,
$context,
)
returnType: $returnType,
returnTypeSource: $returnTypeSource,
);
})
->values()
Expand Down Expand Up @@ -329,13 +347,16 @@ private function functionParameters(ReflectionMethod $reflection, ParsedPhpDoc $
->map(function (ReflectionParameter $parameter) use ($context, $phpDoc) {
$paramTag = $phpDoc->firstParamTagValue($parameter->getName());

[$type, $typeSource] = $this->map(
$parameter->getType(),
$paramTag?->type,
$context
);

return new FunctionParameterDefinition(
name: $parameter->getName(),
type: $this->map(
$parameter->getType(),
$paramTag?->type,
$context
),
type: $type,
typeSource: $typeSource,
hasDefaultValue: $parameter->isDefaultValueAvailable(),
);
})
Expand All @@ -357,7 +378,7 @@ private function parent(ReflectionClass $reflection, ParsedPhpDoc $phpDoc, TypeC
fn (ExtendsTagValueNode $node) => $parentClass === $this->typeAliasResolver->resolve($node->type->type->name, $context->fileClassLikeContext)
);

$type = $this->map(
[$type] = $this->map(
$parentClass,
$tagValue?->type,
$context
Expand Down Expand Up @@ -389,7 +410,7 @@ private function interfaces(ReflectionClass $reflection, ParsedPhpDoc $phpDoc, T
fn (ExtendsTagValueNode $node) => $className === $this->typeAliasResolver->resolve($node->type->type->name, $context->fileClassLikeContext)
);

$type = $this->map(
[$type] = $this->map(
$className,
$tagValue?->type,
$context
Expand Down Expand Up @@ -435,7 +456,7 @@ private function traits(ReflectionClass $reflection, TypeContext $context): Used
fn (UsesTagValueNode $node) => $qualifiedName === $this->typeAliasResolver->resolve($node->type->type->name, $context->fileClassLikeContext)
);

$type = $this->map(
[$type] = $this->map(
$qualifiedName,
$tagValue?->type,
$context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

namespace GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition;

use GoodPhp\Reflection\Reflection\TypeSource;
use GoodPhp\Reflection\Type\Type;

final class FunctionParameterDefinition
{
public function __construct(
public readonly string $name,
public readonly ?Type $type,
public readonly ?TypeSource $typeSource,
public readonly bool $hasDefaultValue,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition;

use GoodPhp\Reflection\Reflection\TypeSource;
use GoodPhp\Reflection\Type\Type;

final class MethodDefinition
Expand All @@ -15,5 +16,6 @@ public function __construct(
public readonly array $typeParameters,
public readonly array $parameters,
public readonly ?Type $returnType,
public readonly ?TypeSource $returnTypeSource,
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

namespace GoodPhp\Reflection\NativePHPDoc\Definition\TypeDefinition;

use GoodPhp\Reflection\Reflection\TypeSource;
use GoodPhp\Reflection\Type\Type;

final class PropertyDefinition
{
public function __construct(
public readonly string $name,
public readonly ?Type $type,
public readonly ?TypeSource $typeSource,
public readonly bool $hasDefaultValue,
public readonly bool $isPromoted,
) {}
Expand Down
12 changes: 8 additions & 4 deletions src/NativePHPDoc/DefinitionProviderReflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use GoodPhp\Reflection\NativePHPDoc\Reflection\NpdInterfaceReflection;
use GoodPhp\Reflection\NativePHPDoc\Reflection\NpdSpecialTypeReflection;
use GoodPhp\Reflection\NativePHPDoc\Reflection\NpdTraitReflection;
use GoodPhp\Reflection\Reflection\ClassMemberInheritanceResolver;
use GoodPhp\Reflection\Reflection\TypeReflection;
use GoodPhp\Reflection\Reflector;
use GoodPhp\Reflection\Type\NamedType;
Expand All @@ -26,10 +27,13 @@ final class DefinitionProviderReflector implements Reflector
{
private readonly TypeComparator $typeComparator;

private readonly ClassMemberInheritanceResolver $classMemberInheritanceResolver;

public function __construct(
private readonly DefinitionProvider $definitionProvider,
) {
$this->typeComparator = new TypeComparator($this);
$this->classMemberInheritanceResolver = new ClassMemberInheritanceResolver();
}

public function typeComparator(): TypeComparator
Expand All @@ -54,10 +58,10 @@ public function forNamedType(NamedType $type): TypeReflection
};

return match (true) {
$definition instanceof ClassTypeDefinition => new NpdClassReflection($definition, $resolvedTypeParameterMap, $this),
$definition instanceof InterfaceTypeDefinition => new NpdInterfaceReflection($definition, $resolvedTypeParameterMap, $this),
$definition instanceof TraitTypeDefinition => new NpdTraitReflection($definition, $resolvedTypeParameterMap, $this),
$definition instanceof EnumTypeDefinition => new NpdEnumReflection($definition, $this),
$definition instanceof ClassTypeDefinition => new NpdClassReflection($definition, $resolvedTypeParameterMap, $this, $this->classMemberInheritanceResolver),
$definition instanceof InterfaceTypeDefinition => new NpdInterfaceReflection($definition, $resolvedTypeParameterMap, $this, $this->classMemberInheritanceResolver),
$definition instanceof TraitTypeDefinition => new NpdTraitReflection($definition, $resolvedTypeParameterMap, $this, $this->classMemberInheritanceResolver),
$definition instanceof EnumTypeDefinition => new NpdEnumReflection($definition, $this, $this->classMemberInheritanceResolver),
$definition instanceof SpecialTypeDefinition => new NpdSpecialTypeReflection($definition, $resolvedTypeParameterMap),
default => throw new InvalidArgumentException('Unsupported definition of type ' . $definition::class . ' given.')
};
Expand Down
Loading