Cleanup and var implementation

This commit is contained in:
Jaapio
2022-10-28 16:27:10 +02:00
parent b66b6dc644
commit 25d696587f
12 changed files with 259 additions and 45 deletions
+4 -4
View File
@@ -76,7 +76,7 @@ final class StandardTagFactory implements TagFactory
public const REGEX_TAGNAME = '[\w\-\_\\\\:]+'; public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';
/** /**
* @var array<class-string<Tag>> An array with a tag as a key, and an * @var array<class-string<Tag>|Factory> An array with a tag as a key, and an
* FQCN to a class that handles it as an array value. * FQCN to a class that handles it as an array value.
*/ */
private $tagHandlerMappings = [ private $tagHandlerMappings = [
@@ -177,7 +177,7 @@ final class StandardTagFactory implements TagFactory
} }
if (is_object($handler)) { if (is_object($handler)) {
Assert::implementsInterface($handler, TagFactory::class); Assert::implementsInterface($handler, Factory::class);
$this->tagHandlerMappings[$tagName] = $handler; $this->tagHandlerMappings[$tagName] = $handler;
return; return;
@@ -283,12 +283,12 @@ final class StandardTagFactory implements TagFactory
} }
} }
$parameterName = $parameter->getName();
if (isset($locator[$typeHint])) { if (isset($locator[$typeHint])) {
$arguments[] = $locator[$typeHint]; $arguments[$parameterName] = $locator[$typeHint];
continue; continue;
} }
$parameterName = $parameter->getName();
if (isset($locator[$parameterName])) { if (isset($locator[$parameterName])) {
$arguments[$parameterName] = $locator[$parameterName]; $arguments[$parameterName] = $locator[$parameterName];
continue; continue;
+1 -1
View File
@@ -10,7 +10,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
interface PHPStanFactory interface PHPStanFactory
{ {
public function create(PhpDocTagNode $node, Context $context): Tag; public function create(PhpDocTagNode $node, ?Context $context): Tag;
public function supports(PhpDocTagNode $node, ?Context $context): bool; public function supports(PhpDocTagNode $node, ?Context $context): bool;
} }
+1 -1
View File
@@ -28,7 +28,7 @@ final class ParamFactory implements PHPStanFactory
$this->descriptionFactory = $descriptionFactory; $this->descriptionFactory = $descriptionFactory;
} }
public function create(PhpDocTagNode $node, Context $context): Tag public function create(PhpDocTagNode $node, ?Context $context): Tag
{ {
$tagValue = $node->value; $tagValue = $node->value;
Assert::isInstanceOf($tagValue, ParamTagValueNode::class); Assert::isInstanceOf($tagValue, ParamTagValueNode::class);
+21 -13
View File
@@ -44,7 +44,7 @@ use function strtolower;
/** /**
* @internal This class is not part of the BC promise of this library. * @internal This class is not part of the BC promise of this library.
*/ */
class TypeFactory final class TypeFactory
{ {
private TypeResolver $resolver; private TypeResolver $resolver;
@@ -53,7 +53,7 @@ class TypeFactory
$this->resolver = $resolver; $this->resolver = $resolver;
} }
public function createType(TypeNode $type, Context $context): ?Type public function createType(TypeNode $type, ?Context $context): ?Type
{ {
switch (get_class($type)) { switch (get_class($type)) {
case ArrayTypeNode::class: case ArrayTypeNode::class:
@@ -77,6 +77,7 @@ class TypeFactory
return $this->createFromCallable($type, $context); return $this->createFromCallable($type, $context);
case ConstTypeNode::class: case ConstTypeNode::class:
return null;
case GenericTypeNode::class: case GenericTypeNode::class:
return $this->createFromGeneric($type, $context); return $this->createFromGeneric($type, $context);
@@ -85,22 +86,29 @@ class TypeFactory
case IntersectionTypeNode::class: case IntersectionTypeNode::class:
return new Intersection( return new Intersection(
array_map( array_filter(
fn (TypeNode $nestedType) => $this->createType($nestedType, $context), array_map(
$type->types fn (TypeNode $nestedType) => $this->createType($nestedType, $context),
$type->types
)
) )
); );
case NullableTypeNode::class: case NullableTypeNode::class:
return new Nullable( $nestedType = $this->createType($type->type, $context);
$this->createType($type->type, $context) if ($nestedType === null) {
); return null;
}
return new Nullable($nestedType);
case UnionTypeNode::class: case UnionTypeNode::class:
return new Compound( return new Compound(
array_map( array_filter(
fn (TypeNode $nestedType) => $this->createType($nestedType, $context), array_map(
$type->types fn (TypeNode $nestedType) => $this->createType($nestedType, $context),
$type->types
)
) )
); );
@@ -115,7 +123,7 @@ class TypeFactory
} }
} }
private function createFromGeneric(GenericTypeNode $type, Context $context): Type private function createFromGeneric(GenericTypeNode $type, ?Context $context): Type
{ {
switch (strtolower($type->type->name)) { switch (strtolower($type->type->name)) {
case 'array': case 'array':
@@ -162,7 +170,7 @@ class TypeFactory
} }
} }
private function createFromCallable(CallableTypeNode $type, Context $context): Callable_ private function createFromCallable(CallableTypeNode $type, ?Context $context): Callable_
{ {
return new Callable_(); return new Callable_();
} }
+47
View File
@@ -0,0 +1,47 @@
<?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\Var_;
use phpDocumentor\Reflection\Types\Context;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use Webmozart\Assert\Assert;
use function trim;
/**
* @internal This class is not part of the BC promise of this library.
*/
final class VarFactory 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, VarTagValueNode::class);
return new Var_(
trim($tagValue->variableName, '$'),
$this->typeFactory->createType($tagValue->type, $context),
$this->descriptionFactory->create($tagValue->description, $context)
);
}
public function supports(PhpDocTagNode $node, ?Context $context): bool
{
return $node->value instanceof VarTagValueNode;
}
}
+6 -5
View File
@@ -42,13 +42,13 @@ final class DocBlockFactory implements DocBlockFactoryInterface
/** @var DocBlock\DescriptionFactory */ /** @var DocBlock\DescriptionFactory */
private $descriptionFactory; private $descriptionFactory;
/** @var Factory */ /** @var TagFactory */
private $tagFactory; private $tagFactory;
/** /**
* Initializes this factory with the required subcontractors. * Initializes this factory with the required subcontractors.
*/ */
public function __construct(DescriptionFactory $descriptionFactory, Factory $tagFactory) public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory)
{ {
$this->descriptionFactory = $descriptionFactory; $this->descriptionFactory = $descriptionFactory;
$this->tagFactory = $tagFactory; $this->tagFactory = $tagFactory;
@@ -57,7 +57,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
/** /**
* Factory method for easy instantiation. * Factory method for easy instantiation.
* *
* @param array<string, class-string<Tag>|TagFactory> $additionalTags * @param array<string, class-string<Tag>|Factory> $additionalTags
*/ */
public static function createInstance(array $additionalTags = []): self public static function createInstance(array $additionalTags = []): self
{ {
@@ -74,6 +74,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
$tagFactory->addService($descriptionFactory); $tagFactory->addService($descriptionFactory);
$tagFactory->addService($typeResolver); $tagFactory->addService($typeResolver);
$tagFactory->registerTagHandler('param', $phpstanTagFactory); $tagFactory->registerTagHandler('param', $phpstanTagFactory);
$tagFactory->registerTagHandler('var', $phpstanTagFactory);
$docBlockFactory = new self($descriptionFactory, $tagFactory); $docBlockFactory = new self($descriptionFactory, $tagFactory);
foreach ($additionalTags as $tagName => $tagHandler) { foreach ($additionalTags as $tagName => $tagHandler) {
@@ -122,9 +123,9 @@ final class DocBlockFactory implements DocBlockFactoryInterface
} }
/** /**
* @param class-string<Tag> $handler * @param class-string<Tag>|Factory $handler
*/ */
public function registerTagHandler(string $tagName, string $handler): void public function registerTagHandler(string $tagName, $handler): void
{ {
$this->tagFactory->registerTagHandler($tagName, $handler); $this->tagFactory->registerTagHandler($tagName, $handler);
} }
+4 -3
View File
@@ -6,6 +6,7 @@ namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\Type; use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Mixed_;
use function sprintf; use function sprintf;
final class ArrayShapeItem final class ArrayShapeItem
@@ -14,10 +15,10 @@ final class ArrayShapeItem
private Type $value; private Type $value;
private bool $optional; private bool $optional;
public function __construct(?string $key, Type $value, bool $optional) public function __construct(?string $key, ?Type $value, bool $optional)
{ {
$this->key = $key; $this->key = $key;
$this->value = $value; $this->value = $value ?? new Mixed_();
$this->optional = $optional; $this->optional = $optional;
} }
@@ -36,7 +37,7 @@ final class ArrayShapeItem
return $this->optional; return $this->optional;
} }
public function __toString() public function __toString(): string
{ {
if ($this->key !== null) { if ($this->key !== null) {
return sprintf( return sprintf(
+2 -18
View File
@@ -14,34 +14,18 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection\Assets; namespace phpDocumentor\Reflection\Assets;
use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\TagFactory; use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\Types\Context; use phpDocumentor\Reflection\Types\Context;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
class CustomTagFactory implements TagFactory class CustomTagFactory implements Factory
{ {
public $class; public $class;
public function addParameter(string $name, $value): void
{
// TODO: Implement addParameter() method.
}
public function create(string $tagLine, ?Context $context = null, CustomServiceClass $class = null): Tag public function create(string $tagLine, ?Context $context = null, CustomServiceClass $class = null): Tag
{ {
$this->class = $class; $this->class = $class;
return new Generic('custom'); return new Generic('custom');
} }
public function addService(object $service): void
{
// TODO: Implement addService() method.
}
public function registerTagHandler(string $tagName, string $handler): void
{
// TODO: Implement registerTagHandler() method.
}
} }
@@ -0,0 +1,46 @@
<?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\Param;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\String_;
final class ParamFactoryTest extends TagFactoryTestCase
{
/**
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::__construct
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::create
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::supports
*/
public function testParamIsCreated(): void
{
$ast = $this->parseTag('@param string $var');
$factory = new ParamFactory($this->giveTypeFactory(), $this->givenDescriptionFactory());
$context = new Context('global');
self::assertTrue($factory->supports($ast, $context));
self::assertEquals(
new Param(
'var',
new String_(),
false,
new Description(''),
false
),
$factory->create($ast, $context)
);
}
}
@@ -0,0 +1,62 @@
<?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\Factory;
use Mockery as m;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\TypeResolver;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPUnit\Framework\TestCase;
abstract class TagFactoryTestCase extends TestCase
{
public function parseTag(string $tag): PhpDocTagNode
{
$lexer = new Lexer();
$tokens = $lexer->tokenize($tag);
$constParser = new ConstExprParser();
return (new PhpDocParser(new TypeParser($constParser), $constParser))->parseTag(new TokenIterator($tokens));
}
public function giveTypeFactory(): TypeFactory
{
return new TypeFactory(new TypeResolver(new FqsenResolver()));
}
public function givenDescriptionFactory(): DescriptionFactory
{
$factory = m::mock(DescriptionFactory::class);
$factory->shouldReceive('create')->andReturn(new Description(''));
return $factory;
}
/**
* Call Mockery::close after each test.
*
* @after
*/
public function closeMockery(): void
{
m::close();
}
}
@@ -2,6 +2,15 @@
declare(strict_types=1); 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; namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\Fqsen;
@@ -56,6 +65,9 @@ final class TypeFactoryTest extends TestCase
self::assertEquals($expected, $actual); self::assertEquals($expected, $actual);
} }
/**
* @return array<array{0: string, 1: Type}>
*/
public function typeProvider(): array public function typeProvider(): array
{ {
return [ return [
@@ -126,6 +138,9 @@ final class TypeFactoryTest extends TestCase
]; ];
} }
/**
* @return array<array{0: string, 1: Type}>
*/
public function genericsProvider(): array public function genericsProvider(): array
{ {
return [ return [
@@ -169,6 +184,9 @@ final class TypeFactoryTest extends TestCase
]; ];
} }
/**
* @return array<array{0: string, 1: Type}>
*/
public function callableProvider(): array public function callableProvider(): array
{ {
return [ return [
@@ -195,6 +213,9 @@ final class TypeFactoryTest extends TestCase
]; ];
} }
/**
* @return array<array{0: string, 1: Type}>
*/
public function constExpressions(): array public function constExpressions(): array
{ {
return [ return [
@@ -0,0 +1,44 @@
<?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\Var_;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\String_;
final class VarFactoryTest extends TagFactoryTestCase
{
/**
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::__construct
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::create
* @covers \phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory::supports
*/
public function testParamIsCreated(): void
{
$ast = $this->parseTag('@var string $var');
$factory = new VarFactory($this->giveTypeFactory(), $this->givenDescriptionFactory());
$context = new Context('global');
self::assertTrue($factory->supports($ast, $context));
self::assertEquals(
new Var_(
'var',
new String_(),
new Description('')
),
$factory->create($ast, $context)
);
}
}