Add method support

This commit is contained in:
Jaapio
2022-10-28 22:25:08 +02:00
parent cfe9845260
commit e4ac07bc40
6 changed files with 327 additions and 6 deletions
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\Method;
use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\Void_;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use Webmozart\Assert\Assert;
use function array_map;
use function trim;
/**
* @internal This class is not part of the BC promise of this library.
*/
final class MethodFactory implements PHPStanFactory
{
private TypeFactory $typeFactory;
private DescriptionFactory $descriptionFactory;
public function __construct(TypeFactory $typeFactory, DescriptionFactory $descriptionFactory)
{
$this->typeFactory = $typeFactory;
$this->descriptionFactory = $descriptionFactory;
}
public function create(PhpDocTagNode $node, ?Context $context): Tag
{
$tagValue = $node->value;
Assert::isInstanceOf($tagValue, MethodTagValueNode::class);
return new Method(
$tagValue->methodName,
[],
$this->createReturnType($tagValue, $context),
$tagValue->isStatic,
$this->descriptionFactory->create($tagValue->description, $context),
false,
array_map(
function (MethodTagValueParameterNode $param) use ($context) {
return new MethodParameter(
trim($param->parameterName, '$'),
$this->typeFactory->createType($param->type, $context),
$param->isReference,
$param->isVariadic,
(string) $param->defaultValue
);
},
$tagValue->parameters
),
);
}
public function supports(PhpDocTagNode $node, ?Context $context): bool
{
return $node->value instanceof MethodTagValueNode;
}
private function createReturnType(MethodTagValueNode $tagValue, ?Context $context): Type
{
return $this->typeFactory->createType($tagValue->returnType, $context) ?? new Void_();
}
}
+5 -1
View File
@@ -54,8 +54,12 @@ final class TypeFactory
$this->resolver = $resolver; $this->resolver = $resolver;
} }
public function createType(TypeNode $type, ?Context $context): ?Type public function createType(?TypeNode $type, ?Context $context): ?Type
{ {
if ($type === null) {
return null;
}
switch (get_class($type)) { switch (get_class($type)) {
case ArrayTypeNode::class: case ArrayTypeNode::class:
return new Array_( return new Array_(
+51 -5
View File
@@ -24,6 +24,7 @@ use phpDocumentor\Reflection\Types\Void_;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
use function array_keys; use function array_keys;
use function array_map;
use function explode; use function explode;
use function implode; use function implode;
use function is_string; use function is_string;
@@ -63,6 +64,9 @@ final class Method extends BaseTag implements Factory\StaticMethod
/** @var bool */ /** @var bool */
private $returnsReference; private $returnsReference;
/** @var MethodParameter[] */
private array $parameters;
/** /**
* @param array<int, array<string, Type|string>> $arguments * @param array<int, array<string, Type|string>> $arguments
* @phpstan-param array<int, array{name: string, type: Type}|string> $arguments * @phpstan-param array<int, array{name: string, type: Type}|string> $arguments
@@ -73,7 +77,8 @@ final class Method extends BaseTag implements Factory\StaticMethod
?Type $returnType = null, ?Type $returnType = null,
bool $static = false, bool $static = false,
?Description $description = null, ?Description $description = null,
bool $returnsReference = false bool $returnsReference = false,
?array $parameters = null
) { ) {
Assert::stringNotEmpty($methodName); Assert::stringNotEmpty($methodName);
@@ -81,12 +86,15 @@ final class Method extends BaseTag implements Factory\StaticMethod
$returnType = new Void_(); $returnType = new Void_();
} }
$arguments = $this->filterArguments($arguments);
$this->methodName = $methodName; $this->methodName = $methodName;
$this->arguments = $this->filterArguments($arguments); $this->arguments = $arguments;
$this->returnType = $returnType; $this->returnType = $returnType;
$this->isStatic = $static; $this->isStatic = $static;
$this->description = $description; $this->description = $description;
$this->returnsReference = $returnsReference; $this->returnsReference = $returnsReference;
$this->parameters = $parameters ?? $this->fromLegacyArguments($arguments);
} }
public static function create( public static function create(
@@ -190,7 +198,14 @@ final class Method extends BaseTag implements Factory\StaticMethod
} }
} }
return new static($methodName, $arguments, $returnType, $static, $description, $returnsReference); return new static(
$methodName,
$arguments,
$returnType,
$static,
$description,
$returnsReference
);
} }
/** /**
@@ -210,6 +225,12 @@ final class Method extends BaseTag implements Factory\StaticMethod
return $this->arguments; return $this->arguments;
} }
/** @return MethodParameter[] */
public function getParameters(): array
{
return $this->parameters;
}
/** /**
* Checks whether the method tag describes a static method or not. * Checks whether the method tag describes a static method or not.
* *
@@ -233,8 +254,11 @@ final class Method extends BaseTag implements Factory\StaticMethod
public function __toString(): string public function __toString(): string
{ {
$arguments = []; $arguments = [];
foreach ($this->arguments as $argument) { foreach ($this->parameters as $parameter) {
$arguments[] = $argument['type'] . ' $' . $argument['name']; $arguments[] = ($parameter->getType() ?? new Mixed_()) . ' ' .
($parameter->isReference() ? '&' : '') .
($parameter->isVariadic() ? '...' : '') .
'$' . $parameter->getName();
} }
$argumentStr = '(' . implode(', ', $arguments) . ')'; $argumentStr = '(' . implode(', ', $arguments) . ')';
@@ -301,4 +325,26 @@ final class Method extends BaseTag implements Factory\StaticMethod
return $argument; return $argument;
} }
/**
* @param array{name: string, type: Type} $arguments
* @return MethodParameter[]
*/
private function fromLegacyArguments(array $arguments): array
{
trigger_error(
'Create method parameters via legacy format is deprecated add parameters via the constructor',
E_USER_DEPRECATED
);
return array_map(
static function ($arg) {
return new MethodParameter(
$arg['name'],
$arg['type']
);
},
$arguments
);
}
} }
+72
View File
@@ -0,0 +1,72 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\DocBlock\Tags;
use phpDocumentor\Reflection\Type;
final class MethodParameter
{
/** @var Type */
private $type;
/** @var bool */
private $isReference;
/** @var bool */
private $isVariadic;
/** @var string */
private $name;
/** @var string|null */
private $defaultValue;
public function __construct(
string $name,
Type $type,
bool $isReference = false,
bool $isVariadic = false,
?string $defaultValue = null
) {
$this->type = $type;
$this->isReference = $isReference;
$this->isVariadic = $isVariadic;
$this->name = $name;
$this->defaultValue = $defaultValue;
}
public function getName(): string
{
return $this->name;
}
public function getType(): Type
{
return $this->type;
}
public function isReference(): bool
{
return $this->isReference;
}
public function isVariadic(): bool
{
return $this->isVariadic;
}
public function getDefaultValue(): ?string
{
return $this->defaultValue;
}
}
+1
View File
@@ -89,6 +89,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
$tagFactory->registerTagHandler('property', $phpstanTagFactory); $tagFactory->registerTagHandler('property', $phpstanTagFactory);
$tagFactory->registerTagHandler('property-read', $phpstanTagFactory); $tagFactory->registerTagHandler('property-read', $phpstanTagFactory);
$tagFactory->registerTagHandler('property-write', $phpstanTagFactory); $tagFactory->registerTagHandler('property-write', $phpstanTagFactory);
$tagFactory->registerTagHandler('method', $phpstanTagFactory);
$docBlockFactory = new self($descriptionFactory, $tagFactory); $docBlockFactory = new self($descriptionFactory, $tagFactory);
foreach ($additionalTags as $tagName => $tagHandler) { foreach ($additionalTags as $tagName => $tagHandler) {
@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\Tags\Method;
use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\String_;
use phpDocumentor\Reflection\Types\Void_;
final class MethodFactoryTest extends TagFactoryTestCase
{
/**
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory::__construct
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory::create
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory::supports
* @dataProvider tagProvider
*/
public function testIsCreated(string $tagLine, Method $tag): void
{
$ast = $this->parseTag($tagLine);
$factory = new MethodFactory($this->giveTypeFactory(), $this->givenDescriptionFactory());
$context = new Context('global');
self::assertTrue($factory->supports($ast, $context));
self::assertEquals(
$tag,
$factory->create($ast, $context)
);
}
/** @return array<array<string|Method>> */
public function tagProvider(): array
{
return [
[
'@method static string myMethod()',
new Method(
'myMethod',
[],
new String_(),
true,
new Description(''),
false,
[]
),
],
[
'@method string myMethod()',
new Method(
'myMethod',
[],
new String_(),
false,
new Description(''),
false,
[]
),
],
[
'@method myMethod()',
new Method(
'myMethod',
[],
new Void_(),
false,
new Description(''),
false,
[]
),
],
[
'@method myMethod(int $a = 1)',
new Method(
'myMethod',
[],
new Void_(),
false,
new Description(''),
false,
[new MethodParameter('a', new Integer(), false, false, '1')]
),
],
[
'@method myMethod(int ...$a)',
new Method(
'myMethod',
[],
new Void_(),
false,
new Description(''),
false,
[new MethodParameter('a', new Integer(), false, true)]
),
],
[
'@method myMethod(int &$a, string $b)',
new Method(
'myMethod',
[],
new Void_(),
false,
new Description(''),
false,
[
new MethodParameter('a', new Integer(), true, false),
new MethodParameter('b', new String_(), false, false),
]
),
],
];
}
}