Skip to content

Commit

Permalink
Fix #59 - Implement intersection types correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
williamdes committed May 7, 2023
1 parent aa6aa56 commit c867ba8
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 103 deletions.
125 changes: 53 additions & 72 deletions src/Parser/NodeVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,7 @@ protected function addFunction(FunctionNode $node, ?string $namespace = null)

$parameter->setVariadic($param->variadic);

$type = $param->type;
$typeStr = $this->typeToString($type);

if (null !== $typeStr) {
$typeArr = [[$typeStr, false]];

if ($param->type instanceof NullableType) {
$typeArr[] = ['null', false];
}

$parameter->setHint($this->resolveHint($typeArr));
}
$this->manageHint($param->type, $parameter);

$function->addParameter($parameter);
}
Expand All @@ -167,18 +156,7 @@ protected function addFunction(FunctionNode $node, ?string $namespace = null)
$function->setModifiersFromTags();
$function->setErrors($errors);

$returnType = $node->getReturnType();
$returnTypeStr = $this->typeToString($returnType);

if (null !== $returnTypeStr) {
$returnTypeArr = [[$returnTypeStr, false]];

if ($returnType instanceof NullableType) {
$returnTypeArr[] = ['null', false];
}

$function->setHint($this->resolveHint($returnTypeArr));
}
$this->manageHint($node->getReturnType(), $function);

$this->context->addFunction($function);

Expand All @@ -188,7 +166,7 @@ protected function addFunction(FunctionNode $node, ?string $namespace = null)
}

/**
* @param \PhpParser\Node\Identifier|\PhpParser\Node\Name|NullableType|UnionType|IntersectionType|null $type Type declaration
* @param \PhpParser\Node\ComplexType|\PhpParser\Node\Identifier|\PhpParser\Node\Name|NullableType|UnionType|IntersectionType|null $type Type declaration
*/
protected function typeToString($type): ?string
{
Expand All @@ -206,9 +184,13 @@ protected function typeToString($type): ?string
} elseif ($type instanceof IntersectionType) {
$typeString = [];
foreach ($type->types as $type) {
$typeString[] = $type->__toString();
$typeAsStr = $type->__toString();
if ($type instanceof FullyQualified && 0 !== strpos($typeAsStr, '\\')) {
$typeAsStr = '\\' . $typeAsStr;
}
$typeString[] = $typeAsStr;
}
$typeString = implode('&', $typeString);
return implode('&', $typeString);
}

if ($typeString === null) {
Expand Down Expand Up @@ -332,18 +314,7 @@ protected function addMethod(ClassMethodNode $node)

$parameter->setVariadic($param->variadic);

$type = $param->type;
$typeStr = $this->typeToString($type);

if (null !== $typeStr) {
$typeArr = [[$typeStr, false]];

if ($param->type instanceof NullableType) {
$typeArr[] = ['null', false];
}

$parameter->setHint($this->resolveHint($typeArr));
}
$this->manageHint($param->type, $parameter);

$method->addParameter($parameter);
}
Expand Down Expand Up @@ -371,18 +342,7 @@ protected function addMethod(ClassMethodNode $node)
$method->setModifiersFromTags();
$method->setErrors($errors);

$returnType = $node->getReturnType();
$returnTypeStr = $this->typeToString($returnType);

if (null !== $returnTypeStr) {
$returnTypeArr = [[$returnTypeStr, false]];

if ($returnType instanceof NullableType) {
$returnTypeArr[] = ['null', false];
}

$method->setHint($this->resolveHint($returnTypeArr));
}
$this->manageHint($node->getReturnType(), $method);

if ($this->context->getFilter()->acceptMethod($method)) {
$this->context->getClass()->addMethod($method);
Expand Down Expand Up @@ -417,6 +377,14 @@ protected function addTagFromCommentToMethod(
if (is_array($firstTagFound)) {
$hint = $firstTagFound[0];
$hintDescription = $firstTagFound[1] ?? null;
if (is_array($hint) && isset($hint[0]) && stripos($hint[0][0] ?? '', '&') !== false) {// Detect intersection type
$methodOrFunctionOrProperty->setIntersectionType(true);
$intersectionParts = explode('&', $hint[0][0]);
$hint = [];
foreach ($intersectionParts as $part) {
$hint[] = [$part, false];
}
}
$methodOrFunctionOrProperty->setHint(is_array($hint) ? $this->resolveHint($hint) : $hint);
if ($hintDescription !== null) {
if (is_string($hintDescription)) {
Expand Down Expand Up @@ -458,36 +426,21 @@ protected function addProperty(PropertyNode $node)
}

/**
* @return array<int,PropertyReflection|string[]>
* @phpstan-return array{PropertyReflection,string[]}
* @param \PhpParser\Node\ComplexType|\PhpParser\Node\Identifier|\PhpParser\Node\Name|NullableType|UnionType|IntersectionType|null $type Type declaration
* @param MethodReflection|FunctionReflection|ParameterReflection|PropertyReflection $object
*/
protected function getPropertyReflectionFromParserProperty(PropertyNode $node, PropertyProperty $prop): array
protected function manageHint($type, Reflection $object): void
{
$property = new PropertyReflection($prop->name->toString(), $prop->getLine());
$property->setModifiers($node->flags);

$property->setDefault($prop->default);

$docComment = $node->getDocComment();
$docComment = $docComment === null ? null : $docComment->__toString();
$comment = $this->context->getDocBlockParser()->parse($docComment, $this->context, $property);
$property->setDocComment($docComment);
$property->setShortDesc($comment->getShortDesc());
$property->setLongDesc($comment->getLongDesc());
$property->setSee($this->resolveSee($comment->getTag('see')));

$type = $node->type;

if ($type instanceof IntersectionType) {
$property->setIntersectionType(true);
$object->setIntersectionType(true);

$typeArr = [];
foreach ($type->types as $type) {
$typeStr = $this->typeToString($type);
$typeArr[] = [$typeStr, false];
}

$property->setHint($this->resolveHint($typeArr));
$object->setHint($this->resolveHint($typeArr));
} else {
$typeStr = $this->typeToString($type);

Expand All @@ -497,9 +450,31 @@ protected function getPropertyReflectionFromParserProperty(PropertyNode $node, P
if ($type instanceof NullableType) {
$typeArr[] = ['null', false];
}
$property->setHint($this->resolveHint($typeArr));
$object->setHint($this->resolveHint($typeArr));
}
}
}

/**
* @return array<int,PropertyReflection|string[]>
* @phpstan-return array{PropertyReflection,string[]}
*/
protected function getPropertyReflectionFromParserProperty(PropertyNode $node, PropertyProperty $prop): array
{
$property = new PropertyReflection($prop->name->toString(), $prop->getLine());
$property->setModifiers($node->flags);

$property->setDefault($prop->default);

$docComment = $node->getDocComment();
$docComment = $docComment === null ? null : $docComment->__toString();
$comment = $this->context->getDocBlockParser()->parse($docComment, $this->context, $property);
$property->setDocComment($docComment);
$property->setShortDesc($comment->getShortDesc());
$property->setLongDesc($comment->getLongDesc());
$property->setSee($this->resolveSee($comment->getTag('see')));

$this->manageHint($node->type, $property);

if ($errors = $comment->getErrors()) {
$property->setErrors($errors);
Expand Down Expand Up @@ -643,6 +618,9 @@ protected function updateMethodParametersFromTags(Reflection $method, array $tag
return $errors;
}

/**
* @phpstan-param $hints array{0: string, 1: bool}
*/
protected function resolveHint(array $hints): array
{
foreach ($hints as $i => $hint) {
Expand All @@ -652,6 +630,9 @@ protected function resolveHint(array $hints): array
return $hints;
}

/**
* @phpstan-param $alias array{0: string, 1: bool}
*/
protected function resolveAlias($alias)
{
// not a class
Expand Down
17 changes: 17 additions & 0 deletions src/Reflection/FunctionReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class FunctionReflection extends Reflection
/** @var array<string,ParameterReflection> */
protected $parameters = [];
protected $byRef;
/** @var bool */
protected $isIntersectionType = false;
protected $project;
/** @var string|null */
protected $file = null;
Expand All @@ -43,6 +45,16 @@ public function isByRef()
return $this->byRef;
}

public function setIntersectionType(bool $boolean): void
{
$this->isIntersectionType = $boolean;
}

public function isIntersectionType(): bool
{
return $this->isIntersectionType;
}

/**
* @return Project
*/
Expand Down Expand Up @@ -202,6 +214,7 @@ public function toArray()
'tags' => $this->tags,
'modifiers' => $this->modifiers,
'is_by_ref' => $this->byRef,
'is_intersection_type' => $this->isIntersectionType(),
'exceptions' => $this->exceptions,
'errors' => $this->errors,
'parameters' => array_map(
Expand Down Expand Up @@ -233,6 +246,10 @@ public static function fromArray(Project $project, array $array)
$method->relativeFilePath = $array['relative_file'] ?? '';// New in 5.5.0
$method->fromCache = true;

if (isset($array['is_intersection_type'])) {// New in 5.5.3
$method->setIntersectionType($array['is_intersection_type']);
}

foreach ($array['parameters'] as $parameter) {
$method->addParameter(ParameterReflection::fromArray($project, $parameter));
}
Expand Down
39 changes: 27 additions & 12 deletions src/Reflection/MethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ class MethodReflection extends Reflection
protected $class;
protected $parameters = [];
protected $byRef;
protected $exceptions = [];
/** @var bool */
protected $isIntersectionType = false;
protected $exceptions = [];

public function __toString()
{
Expand All @@ -38,6 +40,16 @@ public function isByRef()
return $this->byRef;
}

public function setIntersectionType(bool $boolean): void
{
$this->isIntersectionType = $boolean;
}

public function isIntersectionType(): bool
{
return $this->isIntersectionType;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -135,6 +147,7 @@ public function toArray()
'see' => $this->see,
'modifiers' => $this->modifiers,
'is_by_ref' => $this->byRef,
'is_intersection_type' => $this->isIntersectionType(),
'exceptions' => $this->exceptions,
'errors' => $this->errors,
'parameters' => array_map(
Expand All @@ -151,17 +164,19 @@ static function ($parameter) {
*/
public static function fromArray(Project $project, array $array)
{
$method = new self($array['name'], $array['line']);
$method->shortDesc = $array['short_desc'];
$method->longDesc = $array['long_desc'];
$method->hint = $array['hint'];
$method->hintDesc = $array['hint_desc'];
$method->tags = $array['tags'];
$method->modifiers = $array['modifiers'];
$method->byRef = $array['is_by_ref'];
$method->exceptions = $array['exceptions'];
$method->errors = $array['errors'];
$method->see = $array['see'] ?? [];// New in 5.4.0
$method = new self($array['name'], $array['line']);
$method->shortDesc = $array['short_desc'];
$method->longDesc = $array['long_desc'];
$method->hint = $array['hint'];
$method->hintDesc = $array['hint_desc'];
$method->tags = $array['tags'];
$method->modifiers = $array['modifiers'];
$method->byRef = $array['is_by_ref'];
$method->exceptions = $array['exceptions'];
$method->errors = $array['errors'];
$method->see = $array['see'] ?? [];// New in 5.4.0
$method->isIntersectionType = $array['is_intersection_type'] ?? false;// New in 5.5.3


foreach ($array['parameters'] as $parameter) {
$method->addParameter(ParameterReflection::fromArray($project, $parameter));
Expand Down
34 changes: 24 additions & 10 deletions src/Reflection/ParameterReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class ParameterReflection extends Reflection
protected $byRef;
protected $default;
protected $variadic;
/** @var bool */
protected $isIntersectionType = false;

public function __toString()
{
Expand All @@ -38,6 +40,16 @@ public function getClass()
return $this->method->getClass();
}

public function setIntersectionType(bool $boolean): void
{
$this->isIntersectionType = $boolean;
}

public function isIntersectionType(): bool
{
return $this->isIntersectionType;
}

public function setByRef($boolean)
{
$this->byRef = $boolean;
Expand Down Expand Up @@ -134,6 +146,7 @@ public function toArray()
'variadic' => $this->variadic,
'is_by_ref' => $this->byRef,
'is_read_only' => $this->isReadOnly(),
'is_intersection_type' => $this->isIntersectionType(),
];
}

Expand All @@ -142,16 +155,17 @@ public function toArray()
*/
public static function fromArray(Project $project, array $array)
{
$parameter = new self($array['name'], $array['line']);
$parameter->shortDesc = $array['short_desc'];
$parameter->longDesc = $array['long_desc'];
$parameter->hint = $array['hint'];
$parameter->tags = $array['tags'];
$parameter->modifiers = $array['modifiers'];
$parameter->default = $array['default'];
$parameter->variadic = $array['variadic'];
$parameter->byRef = $array['is_by_ref'];
$parameter->isReadOnly = $array['is_read_only'] ?? false;// New in 5.4.0
$parameter = new self($array['name'], $array['line']);
$parameter->shortDesc = $array['short_desc'];
$parameter->longDesc = $array['long_desc'];
$parameter->hint = $array['hint'];
$parameter->tags = $array['tags'];
$parameter->modifiers = $array['modifiers'];
$parameter->default = $array['default'];
$parameter->variadic = $array['variadic'];
$parameter->byRef = $array['is_by_ref'];
$parameter->isReadOnly = $array['is_read_only'] ?? false;// New in 5.4.0
$parameter->isIntersectionType = $array['is_intersection_type'] ?? false;// New in 5.5.3

return $parameter;
}
Expand Down
Loading

0 comments on commit c867ba8

Please sign in to comment.