Skip to content

Commit

Permalink
Full trait support
Browse files Browse the repository at this point in the history
  • Loading branch information
vudaltsov committed Feb 21, 2024
1 parent 18290fa commit 4d8c103
Show file tree
Hide file tree
Showing 21 changed files with 376 additions and 64 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,5 @@ $reflector = TyphoonReflector::build(

## TODO

- [ ] traits
- [ ] class constants
- [ ] functions
2 changes: 1 addition & 1 deletion docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
| `getStartLine()` | ✅️ |
| `getStaticProperties()` | ✅ Via native reflection |
| `getStaticPropertyValue()` | ✅ Via native reflection |
| `getTraitAliases()` |Via native reflection. TODO |
| `getTraitAliases()` | |
| `getTraitNames()` ||
| `getTraits()` ||
| `hasConstant()` | ✅ Via native reflection. TODO |
Expand Down
1 change: 1 addition & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<file name="stubs/PhpParser/Node/Identifier.phpstub"/>
<file name="stubs/PhpParser/Node/IntersectionType.phpstub"/>
<file name="stubs/PhpParser/Node/Name.phpstub"/>
<file name="stubs/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpstub"/>
<file name="stubs/PhpParser/Node/UnionType.phpstub"/>
<file name="stubs/PHPStan/PhpDocParser/Ast/ConstExpr/ConstFetchNode.phpstub"/>
<file name="stubs/PHPStan/PhpDocParser/Ast/PhpDoc/PhpDocTagNode.phpstub"/>
Expand Down
1 change: 0 additions & 1 deletion src/AttributeReflection/AttributeReflections.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
* @internal
* @psalm-internal Typhoon\Reflection
* @template-covariant T of object
* @psalm-suppress PossiblyUnusedProperty
*/
final class AttributeReflections
{
Expand Down
16 changes: 14 additions & 2 deletions src/ClassReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,21 @@ public function getTemplates(): array

public function getTraitAliases(): array
{
$this->loadNative();
$traitAliases = [];

foreach ($this->metadata->traitMethodAliases as $trait => $methodAliases) {
foreach ($methodAliases as $method => $aliases) {
foreach ($aliases as $alias) {
if ($alias->alias === null) {
continue;
}

$traitAliases[$alias->alias] = $trait . '::' . $method;
}
}
}

return parent::getTraitAliases();
return $traitAliases;
}

public function getTraitNames(): array
Expand Down
3 changes: 2 additions & 1 deletion src/Inheritance/MethodInheritanceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/**
* @internal
* @psalm-internal Typhoon\Reflection\Inheritance
* @psalm-import-type Prototype from MethodMetadata
*/
final class MethodInheritanceResolver
{
Expand All @@ -19,7 +20,7 @@ final class MethodInheritanceResolver
private bool $own = false;

/**
* @var ?array{class-string, non-empty-string}
* @var Prototype
*/
private ?array $prototype = null;

Expand Down
38 changes: 29 additions & 9 deletions src/Inheritance/MethodsInheritanceResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* @internal
* @psalm-internal Typhoon\Reflection
* @psalm-import-type ClassMetadataReflector from ClassMetadata
* @psalm-import-type TraitMethodAliases from ClassMetadata
* @psalm-import-type TraitMethodPrecedence from ClassMetadata
*/
final class MethodsInheritanceResolver
{
Expand All @@ -31,28 +33,46 @@ public function __construct(
) {}

/**
* @param iterable<MethodMetadata> $methods
* @param list<MethodMetadata> $methods
*/
public function setOwn(iterable $methods): void
public function setOwn(array $methods): void
{
foreach ($methods as $method) {
$this->method($method->name)->setOwn($method);
}
}

public function addUsed(NamedObjectType ...$types): void
/**
* @param list<NamedObjectType> $types
* @param TraitMethodAliases $traitMethodAliases
* @param TraitMethodPrecedence $traitMethodPrecedence
*/
public function addUsed(array $types, array $traitMethodAliases, array $traitMethodPrecedence): void
{
foreach ($types as $type) {
$class = ($this->classMetadataReflector)($type->class);
$templateResolver = TemplateResolver::create($class->templates, $type->templateArguments);
foreach (array_column($types, null, 'class') as $type) {
$trait = ($this->classMetadataReflector)($type->class);
$templateResolver = TemplateResolver::create($trait->templates, $type->templateArguments);

foreach ($class->resolvedMethods($this->classMetadataReflector) as $method) {
$this->method($method->name)->addUsed($method, $templateResolver);
foreach ($trait->resolvedMethods($this->classMetadataReflector) as $method) {
$name = $method->name;

if (isset($traitMethodPrecedence[$name]) && $traitMethodPrecedence[$name] !== $trait->name) {
continue;
}

foreach ($traitMethodAliases[$trait->name][$name] ?? [] as $alias) {
$this->method($alias->alias ?? $name)->addUsed($method->toAlias($alias), $templateResolver);
}

$this->method($name)->addUsed($method, $templateResolver);
}
}
}

public function addInherited(NamedObjectType ...$types): void
/**
* @param list<NamedObjectType> $types
*/
public function addInherited(array $types): void
{
foreach ($types as $type) {
$class = ($this->classMetadataReflector)($type->class);
Expand Down
1 change: 0 additions & 1 deletion src/Metadata/AttributeMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* @psalm-internal Typhoon\Reflection
* @psalm-immutable
* @template TAttribute of object
* @psalm-suppress PossiblyUnusedProperty
*/
final class AttributeMetadata
{
Expand Down
15 changes: 10 additions & 5 deletions src/Metadata/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
* @psalm-internal Typhoon\Reflection
* @template-covariant T of object
* @template-extends RootMetadata<class-string<T>>
* @psalm-suppress PossiblyUnusedProperty
* @psalm-type ClassMetadataReflector = \Closure(class-string): ClassMetadata
* @psalm-type TraitMethodAliases = array<class-string, non-empty-array<non-empty-string, list<TraitMethodAlias>>>
* @psalm-type TraitMethodPrecedence = array<non-empty-string, class-string>
*/
final class ClassMetadata extends RootMetadata
{
Expand All @@ -32,6 +33,7 @@ final class ClassMetadata extends RootMetadata

/**
* @param class-string<T> $name
* @param int-mask-of<\ReflectionClass::IS_*> $modifiers
* @param non-empty-string|false $extension
* @param non-empty-string|false $file
* @param positive-int|false $startLine
Expand All @@ -40,9 +42,10 @@ final class ClassMetadata extends RootMetadata
* @param list<AttributeMetadata> $attributes
* @param array<non-empty-string, Type> $typeAliases
* @param list<TemplateReflection> $templates
* @param int-mask-of<\ReflectionClass::IS_*> $modifiers
* @param list<NamedObjectType> $interfaceTypes
* @param list<NamedObjectType> $traitTypes
* @param TraitMethodAliases $traitMethodAliases
* @param TraitMethodPrecedence $traitMethodPrecedence
* @param list<PropertyMetadata> $ownProperties
* @param list<MethodMetadata> $ownMethods
*/
Expand All @@ -67,6 +70,8 @@ public function __construct(
public readonly ?NamedObjectType $parentType = null,
public readonly array $interfaceTypes = [],
public readonly array $traitTypes = [],
public readonly array $traitMethodAliases = [],
public readonly array $traitMethodPrecedence = [],
public readonly array $ownProperties = [],
public readonly array $ownMethods = [],
) {
Expand Down Expand Up @@ -129,11 +134,11 @@ public function resolvedMethods(\Closure $classMetadataReflector): array

$resolver = new MethodsInheritanceResolver($this->name, $classMetadataReflector);
$resolver->setOwn($this->ownMethods);
$resolver->addUsed(...$this->traitTypes);
$resolver->addUsed($this->traitTypes, $this->traitMethodAliases, $this->traitMethodPrecedence);
if ($this->parentType !== null) {
$resolver->addInherited($this->parentType);
$resolver->addInherited([$this->parentType]);
}
$resolver->addInherited(...$this->interfaceTypes);
$resolver->addInherited($this->interfaceTypes);

return $this->resolvedMethods = $resolver->resolve();
}
Expand Down
31 changes: 25 additions & 6 deletions src/Metadata/MethodMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
* @internal
* @psalm-internal Typhoon\Reflection
* @psalm-immutable
* @psalm-suppress PossiblyUnusedProperty
* @psalm-type Prototype = ?array{class-string, non-empty-string}
*/
final class MethodMetadata
{
/**
* @var ?array{class-string, non-empty-string}
* @var Prototype
*/
public ?array $prototype = null;

Expand All @@ -33,9 +33,9 @@ final class MethodMetadata
* @param list<AttributeMetadata> $attributes
*/
public function __construct(
public readonly string $name,
public string $name,
public string $class,
public readonly int $modifiers,
public int $modifiers,
public array $parameters,
public TypeMetadata $returnType,
public readonly array $templates = [],
Expand All @@ -51,6 +51,25 @@ public function __construct(
public readonly array $attributes = [],
) {}

public function toAlias(TraitMethodAlias $alias): self
{
$metadata = clone $this;

if ($alias->alias !== null) {
$metadata->name = $alias->alias;
}

if ($alias->visibility !== null) {
$metadata->modifiers = $metadata->modifiers
& ~\ReflectionMethod::IS_PUBLIC
& ~\ReflectionMethod::IS_PROTECTED
& ~\ReflectionMethod::IS_PRIVATE
| $alias->visibility;
}

return $metadata;
}

/**
* @param class-string $class
*/
Expand All @@ -63,9 +82,9 @@ public function withClass(string $class): self
}

/**
* @param array{class-string, non-empty-string} $prototype
* @param Prototype $prototype
*/
public function withPrototype(array $prototype): self
public function withPrototype(?array $prototype): self
{
$metadata = clone $this;
$metadata->prototype = $prototype;
Expand Down
22 changes: 22 additions & 0 deletions src/Metadata/TraitMethodAlias.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Typhoon\Reflection\Metadata;

/**
* @internal
* @psalm-internal Typhoon\Reflection
* @psalm-type Visibility = null|\ReflectionMethod::IS_PUBLIC|\ReflectionMethod::IS_PROTECTED|\ReflectionMethod::IS_PRIVATE
*/
final class TraitMethodAlias
{
/**
* @param Visibility $visibility
* @param ?non-empty-string $alias
*/
public function __construct(
public readonly ?int $visibility = null,
public readonly ?string $alias = null,
) {}
}
1 change: 0 additions & 1 deletion src/NameContext/AnonymousClassName.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* @psalm-internal Typhoon\Reflection
* @psalm-immutable
* @template TObject of object
* @psalm-suppress PossiblyUnusedProperty
*/
final class AnonymousClassName
{
Expand Down
Loading

0 comments on commit 4d8c103

Please sign in to comment.