Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class SomeClass

### Filter Rectors

If you have a configuration file for Rector including many sets and Rectors, you might want at times to run only a single Rector from them. The `--only` argument allows that, for example :
If you have a configuration file for Rector including many sets and Rectors, you might want at times to run only a single Rector from them. The `--only` argument allows that, for example :

```bash
vendor/bin/rector process --set solid --only "Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector" src/
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode;
* @return Identifier|Name|NullableType|UnionType|null
*/
public function mapToPhpParserNode(Type $type, ?string $kind = null): ?Node;

public function mapToDocString(Type $type, ?Type $parentType = null): string;
}
189 changes: 6 additions & 183 deletions packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,21 @@

namespace Rector\PHPStanStaticTypeMapper;

use Closure;
use PhpParser\Node;
use PhpParser\Node\Identifier;
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\BooleanType;
use PHPStan\Type\CallableType;
use PHPStan\Type\ClosureType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\IterableType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\ResourceType;
use PHPStan\Type\StaticType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use PHPStan\Type\VoidType;
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode;
use Rector\Exception\NotImplementedException;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use Rector\Php\PhpVersionProvider;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PHPStan\Type\SelfObjectType;
use Rector\PHPStan\Type\ShortenedObjectType;
use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterface;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\ValueObject\PhpVersionFeature;

final class PHPStanStaticTypeMapper
{
/**
* @var PhpVersionProvider
*/
private $phpVersionProvider;

/**
* @var TypeMapperInterface[]
*/
Expand All @@ -62,34 +27,18 @@ final class PHPStanStaticTypeMapper
/**
* @param TypeMapperInterface[] $typeMappers
*/
public function __construct(PhpVersionProvider $phpVersionProvider, array $typeMappers)
public function __construct(array $typeMappers)
{
$this->phpVersionProvider = $phpVersionProvider;
$this->typeMappers = $typeMappers;
}

public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
{
if ($type instanceof ArrayType || $type instanceof IterableType) {
$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;
}

// prevents circular dependency
if ($typeMapper instanceof PHPStanStaticTypeMapperAwareInterface) {
$typeMapper->setPHPStanStaticTypeMapper($this);
}

return $typeMapper->mapToPHPStanPhpDocTypeNode($type);
}

Expand All @@ -101,10 +50,6 @@ public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
*/
public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?Node
{
if ($phpStanType instanceof SelfObjectType) {
return new Identifier('self');
}

foreach ($this->typeMappers as $typeMapper) {
// it cannot be is_a for SelfObjectType, because type classes inherit from each other
if (! is_a($phpStanType, $typeMapper->getNodeClass(), true)) {
Expand All @@ -114,20 +59,12 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No
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');
}
Expand All @@ -148,129 +85,15 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No

public function mapToDocString(Type $phpStanType, ?Type $parentType = null): string
{
if ($phpStanType instanceof UnionType || $phpStanType instanceof IntersectionType) {
$stringTypes = [];

foreach ($phpStanType->getTypes() as $unionedType) {
$stringTypes[] = $this->mapToDocString($unionedType);
}

// remove empty values, e.g. void/iterable
$stringTypes = array_unique($stringTypes);
$stringTypes = array_filter($stringTypes);

$joinCharacter = $phpStanType instanceof IntersectionType ? '&' : '|';

return implode($joinCharacter, $stringTypes);
}

if ($phpStanType instanceof AliasedObjectType) {
// no preslash for alias
return $phpStanType->getClassName();
}

if ($phpStanType instanceof ShortenedObjectType) {
return '\\' . $phpStanType->getFullyQualifiedName();
}

if ($phpStanType instanceof FullyQualifiedObjectType) {
// always prefixed with \\
return '\\' . $phpStanType->getClassName();
}

if ($phpStanType instanceof ObjectType) {
if (ClassExistenceStaticHelper::doesClassLikeExist($phpStanType->getClassName())) {
return '\\' . $phpStanType->getClassName();
}

return $phpStanType->getClassName();
}

if ($phpStanType instanceof ObjectWithoutClassType) {
return 'object';
}

if ($phpStanType instanceof ClosureType) {
return '\\' . Closure::class;
}

if ($phpStanType instanceof StringType || $phpStanType instanceof NullType || $phpStanType instanceof IntegerType || $phpStanType instanceof MixedType || $phpStanType instanceof FloatType || $phpStanType instanceof CallableType || $phpStanType instanceof ResourceType) {
return $phpStanType->describe(VerbosityLevel::typeOnly());
}

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);
}

if ($phpStanType instanceof VoidType) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
// the void type is better done in PHP code
return '';
}

// fallback for PHP 7.0 and older, where void type was only in docs
return 'void';
}

if ($phpStanType instanceof IterableType) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
// the void type is better done in PHP code
return '';
}

return 'iterable';
}

if ($phpStanType instanceof BooleanType) {
return 'bool';
}

if ($phpStanType instanceof NeverType) {
return 'mixed';
}

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;
foreach ($this->typeMappers as $typeMapper) {
// it cannot be is_a for SelfObjectType, because type classes inherit from each other
if (! is_a($phpStanType, $typeMapper->getNodeClass(), true)) {
continue;
}

$unionedArrayType[] = new ArrayTypeNode($unionedType);
return $typeMapper->mapToDocString($phpStanType, $parentType);
}

return new AttributeAwareUnionTypeNode($unionedArrayType);
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType));
}
}
Loading