decopule ArrayTypeMapper and TypeWithClassNameTypeMapper

This commit is contained in:
TomasVotruba 2020-01-15 14:51:15 +01:00
parent 71cdc8fe40
commit ccc63bbb75
3 changed files with 180 additions and 70 deletions

View File

@ -10,17 +10,11 @@ use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\NullableType;
use PhpParser\Node\UnionType as PhpParserUnionType;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\Type\ArrayType;
use PHPStan\Type\StaticType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode;
use Rector\Exception\NotImplementedException;
use Rector\PHPStan\Type\SelfObjectType;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
final class PHPStanStaticTypeMapper
@ -40,16 +34,6 @@ final class PHPStanStaticTypeMapper
public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
{
// @todo move to ArrayTypeMapper
if ($type instanceof ArrayType) {
$itemTypeNode = $this->mapToPHPStanPhpDocTypeNode($type->getItemType());
if ($itemTypeNode instanceof UnionTypeNode) {
return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode);
}
return new ArrayTypeNode($itemTypeNode);
}
foreach ($this->typeMappers as $typeMapper) {
if (! is_a($type, $typeMapper->getNodeClass(), true)) {
continue;
@ -75,20 +59,12 @@ final class PHPStanStaticTypeMapper
return $typeMapper->mapToPhpParserNode($phpStanType, $kind);
}
if ($phpStanType instanceof ArrayType) {
return new Identifier('array');
}
if ($phpStanType instanceof StaticType) {
return null;
}
if ($phpStanType instanceof TypeWithClassName) {
$lowerCasedClassName = strtolower($phpStanType->getClassName());
if ($lowerCasedClassName === 'callable') {
return new Identifier('callable');
}
if ($lowerCasedClassName === 'self') {
return new Identifier('self');
}
@ -118,52 +94,6 @@ final class PHPStanStaticTypeMapper
return $typeMapper->mapToDocString($phpStanType, $parentType);
}
if ($phpStanType instanceof ArrayType) {
if ($phpStanType->getItemType() instanceof UnionType) {
$unionedTypesAsString = [];
foreach ($phpStanType->getItemType()->getTypes() as $unionedArrayItemType) {
$unionedTypesAsString[] = $this->mapToDocString($unionedArrayItemType, $phpStanType) . '[]';
}
$unionedTypesAsString = array_values($unionedTypesAsString);
$unionedTypesAsString = array_unique($unionedTypesAsString);
return implode('|', $unionedTypesAsString);
}
$docString = $this->mapToDocString($phpStanType->getItemType(), $parentType);
// @todo improve this
$docStringTypes = explode('|', $docString);
$docStringTypes = array_filter($docStringTypes);
foreach ($docStringTypes as $key => $docStringType) {
$docStringTypes[$key] = $docStringType . '[]';
}
return implode('|', $docStringTypes);
}
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType));
}
private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes(
UnionTypeNode $unionTypeNode
): AttributeAwareUnionTypeNode {
$unionedArrayType = [];
foreach ($unionTypeNode->types as $unionedType) {
if ($unionedType instanceof UnionTypeNode) {
foreach ($unionedType->types as $key => $subUnionedType) {
$unionedType->types[$key] = new ArrayTypeNode($subUnionedType);
}
$unionedArrayType[] = $unionedType;
continue;
}
$unionedArrayType[] = new ArrayTypeNode($unionedType);
}
return new AttributeAwareUnionTypeNode($unionedArrayType);
}
}

View File

@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
final class ArrayTypeMapper implements TypeMapperInterface
{
/**
* @var PHPStanStaticTypeMapper
*/
private $phpStanStaticTypeMapper;
public function getNodeClass(): string
{
return ArrayType::class;
}
/**
* @param ArrayType $type
*/
public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
{
$itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getItemType());
if ($itemTypeNode instanceof UnionTypeNode) {
return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode);
}
return new ArrayTypeNode($itemTypeNode);
}
/**
* @param ArrayType $type
*/
public function mapToPhpParserNode(Type $type, ?string $kind = null): ?Node
{
return new Identifier('array');
}
/**
* @param ArrayType $type
*/
public function mapToDocString(Type $type, ?Type $parentType = null): string
{
$itemType = $type->getItemType();
if ($itemType instanceof UnionType) {
return $this->mapArrayUnionTypeToDocString($type, $itemType);
}
$docString = $this->phpStanStaticTypeMapper->mapToDocString($type->getItemType(), $parentType);
// @todo improve this
$docStringTypes = explode('|', $docString);
$docStringTypes = array_filter($docStringTypes);
foreach ($docStringTypes as $key => $docStringType) {
$docStringTypes[$key] = $docStringType . '[]';
}
return implode('|', $docStringTypes);
}
/**
* @required
*/
public function autowireArrayTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
{
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
}
private function mapArrayUnionTypeToDocString(ArrayType $arrayType, UnionType $unionType): string
{
$unionedTypesAsString = [];
foreach ($unionType->getTypes() as $unionedArrayItemType) {
$unionedTypesAsString[] = $this->phpStanStaticTypeMapper->mapToDocString(
$unionedArrayItemType,
$arrayType
) . '[]';
}
$unionedTypesAsString = array_values($unionedTypesAsString);
$unionedTypesAsString = array_unique($unionedTypesAsString);
return implode('|', $unionedTypesAsString);
}
/**
* @todo improve
*/
private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes(
UnionTypeNode $unionTypeNode
): AttributeAwareUnionTypeNode {
$unionedArrayType = [];
foreach ($unionTypeNode->types as $unionedType) {
if ($unionedType instanceof UnionTypeNode) {
foreach ($unionedType->types as $key => $subUnionedType) {
$unionedType->types[$key] = new ArrayTypeNode($subUnionedType);
}
$unionedArrayType[] = $unionedType;
continue;
}
$unionedArrayType[] = new ArrayTypeNode($unionedType);
}
return new AttributeAwareUnionTypeNode($unionedArrayType);
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
use Rector\Php\PhpVersionProvider;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\ValueObject\PhpVersionFeature;
final class TypeWithClassNameTypeMapper implements TypeMapperInterface
{
/**
* @var PhpVersionProvider
*/
private $phpVersionProvider;
public function __construct(PhpVersionProvider $phpVersionProvider)
{
$this->phpVersionProvider = $phpVersionProvider;
}
public function getNodeClass(): string
{
return StringType::class;
}
/**
* @param StringType $type
*/
public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
{
return new IdentifierTypeNode('string');
}
public function mapToPhpParserNode(Type $type, ?string $kind = null): ?Node
{
if (! $this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
return null;
}
return new Identifier('string');
}
public function mapToDocString(Type $type, ?Type $parentType = null): string
{
return $type->describe(VerbosityLevel::typeOnly());
}
}