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 @@ -325,7 +325,7 @@ final class MyFirstRector extends AbstractRector
public function refactor(Node $node): ?Node
{
// we only care about "set*" method names
if (! $this->isName($node, 'set*')) {
if (! $this->isName($node->name, 'set*')) {
// return null to skip it
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion docs/NodesOverview.md
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ const SOME_CLASS_CONSTANT = 'default value';
#### `PhpParser\Node\Stmt\ClassMethod`

```php
function someMethod()
public function someMethod()
{
}
```
Expand Down
1 change: 1 addition & 0 deletions packages/PHPStanStaticTypeMapper/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ services:
Rector\PHPStanStaticTypeMapper\:
resource: '../src'
exclude:
- '../src/ValueObject/*'
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Rector\PHPStanStaticTypeMapper\Contract;

use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;

interface PHPStanStaticTypeMapperAwareInterface
{
public function setPHPStanStaticTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Rector\PHPStanStaticTypeMapper\Contract;

use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\UnionType;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Type;

interface TypeMapperInterface
{
public function getNodeClass(): string;

public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode;

/**
* @return Identifier|Name|NullableType|UnionType|null
*/
public function mapToPhpParserNode(Type $type, ?string $kind = null): ?Node;
}
186 changes: 46 additions & 140 deletions packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
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\IdentifierTypeNode;
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\ClassStringType;
use PHPStan\Type\ClosureType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
Expand All @@ -30,10 +30,10 @@
use PHPStan\Type\ResourceType;
use PHPStan\Type\StaticType;
use PHPStan\Type\StringType;
use PHPStan\Type\ThisType;
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;
Expand All @@ -42,9 +42,10 @@
use Rector\Php\PhpVersionProvider;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PHPStan\Type\ParentStaticType;
use Rector\PHPStan\Type\SelfObjectType;
use Rector\PHPStan\Type\ShortenedObjectType;
use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterface;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\ValueObject\PhpVersionFeature;
use Traversable;

Expand All @@ -55,30 +56,24 @@ final class PHPStanStaticTypeMapper
*/
private $phpVersionProvider;

public function __construct(PhpVersionProvider $phpVersionProvider)
/**
* @var TypeMapperInterface[]
*/
private $typeMappers = [];

/**
* @param TypeMapperInterface[] $typeMappers
*/
public function __construct(PhpVersionProvider $phpVersionProvider, array $typeMappers)
{
$this->phpVersionProvider = $phpVersionProvider;
$this->typeMappers = $typeMappers;
}

public function mapToPHPStanPhpDocTypeNode(Type $phpStanType)
public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
{
if ($phpStanType instanceof MixedType) {
return new IdentifierTypeNode('mixed');
}

if ($phpStanType instanceof UnionType) {
$unionTypesNodes = [];
foreach ($phpStanType->getTypes() as $unionedType) {
$unionTypesNodes[] = $this->mapToPHPStanPhpDocTypeNode($unionedType);
}

$unionTypesNodes = array_unique($unionTypesNodes);

return new AttributeAwareUnionTypeNode($unionTypesNodes);
}

if ($phpStanType instanceof ArrayType || $phpStanType instanceof IterableType) {
$itemTypeNode = $this->mapToPHPStanPhpDocTypeNode($phpStanType->getItemType());
if ($type instanceof ArrayType || $type instanceof IterableType) {
$itemTypeNode = $this->mapToPHPStanPhpDocTypeNode($type->getItemType());

if ($itemTypeNode instanceof UnionTypeNode) {
return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode);
Expand All @@ -87,39 +82,20 @@ public function mapToPHPStanPhpDocTypeNode(Type $phpStanType)
return new ArrayTypeNode($itemTypeNode);
}

if ($phpStanType instanceof IntegerType) {
return new IdentifierTypeNode('int');
}

if ($phpStanType instanceof ClassStringType) {
return new IdentifierTypeNode('class-string');
}

if ($phpStanType instanceof StringType) {
return new IdentifierTypeNode('string');
}

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

if ($phpStanType instanceof FloatType) {
return new IdentifierTypeNode('float');
}

if ($phpStanType instanceof ObjectType) {
return new IdentifierTypeNode('\\' . $phpStanType->getClassName());
}
foreach ($this->typeMappers as $typeMapper) {
if (! is_a($type, $typeMapper->getNodeClass(), true)) {
continue;
}

if ($phpStanType instanceof NullType) {
return new IdentifierTypeNode('null');
}
// prevents circular dependency
if ($typeMapper instanceof PHPStanStaticTypeMapperAwareInterface) {
$typeMapper->setPHPStanStaticTypeMapper($this);
}

if ($phpStanType instanceof NeverType) {
return new IdentifierTypeNode('mixed');
return $typeMapper->mapToPHPStanPhpDocTypeNode($type);
}

throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType));
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($type));
}

/**
Expand All @@ -144,54 +120,19 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No
return new Identifier('self');
}

if ($phpStanType instanceof IntegerType) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
return new Identifier('int');
}

return null;
}

if ($phpStanType instanceof StringType) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
return new Identifier('string');
}

return null;
}

if ($phpStanType instanceof BooleanType) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
return new Identifier('bool');
}

return null;
}

if ($phpStanType instanceof FloatType) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
return new Identifier('float');
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;
}

return null;
return $typeMapper->mapToPhpParserNode($phpStanType);
}

if ($phpStanType instanceof ArrayType) {
return new Identifier('array');
}

if ($phpStanType instanceof IterableType) {
return new Identifier('iterable');
}

if ($phpStanType instanceof ThisType) {
return new Identifier('self');
}

if ($phpStanType instanceof ParentStaticType) {
return new Identifier('parent');
}

if ($phpStanType instanceof StaticType) {
return null;
}
Expand All @@ -204,14 +145,6 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No
return new Identifier('callable');
}

if ($phpStanType instanceof ShortenedObjectType) {
return new Name\FullyQualified($phpStanType->getFullyQualifiedName());
}

if ($phpStanType instanceof AliasedObjectType) {
return new Name($phpStanType->getClassName());
}

if ($phpStanType instanceof TypeWithClassName) {
$lowerCasedClassName = strtolower($phpStanType->getClassName());
if ($lowerCasedClassName === 'callable') {
Expand All @@ -230,7 +163,7 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No
return null;
}

return new Name\FullyQualified($phpStanType->getClassName());
return new FullyQualified($phpStanType->getClassName());
}

if ($phpStanType instanceof UnionType) {
Expand Down Expand Up @@ -263,11 +196,8 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No
return new NullableType($nullabledTypeNode);
}

if ($phpStanType instanceof NeverType ||
$phpStanType instanceof VoidType ||
$phpStanType instanceof MixedType ||
$phpStanType instanceof ResourceType ||
$phpStanType instanceof NullType
if ($phpStanType instanceof VoidType ||
$phpStanType instanceof ResourceType
) {
return null;
}
Expand Down Expand Up @@ -331,16 +261,8 @@ public function mapToDocString(Type $phpStanType, ?Type $parentType = null): str
return '\\' . Closure::class;
}

if ($phpStanType instanceof StringType) {
return 'string';
}

if ($phpStanType instanceof IntegerType) {
return 'int';
}

if ($phpStanType instanceof NullType) {
return 'null';
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) {
Expand Down Expand Up @@ -369,16 +291,8 @@ public function mapToDocString(Type $phpStanType, ?Type $parentType = null): str
return implode('|', $docStringTypes);
}

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

if ($phpStanType instanceof FloatType) {
return 'float';
}

if ($phpStanType instanceof VoidType) {
if ($this->phpVersionProvider->isAtLeast('7.1')) {
if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) {
// the void type is better done in PHP code
return '';
}
Expand All @@ -387,29 +301,21 @@ public function mapToDocString(Type $phpStanType, ?Type $parentType = null): str
return 'void';
}

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

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

return 'iterable';
}

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

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

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

throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType));
Expand Down Expand Up @@ -456,7 +362,7 @@ private function matchTypeForNullableUnionType(UnionType $unionType): ?Type
}

/**
* @return Name|Name\FullyQualified|PhpParserUnionType|null
* @return Name|FullyQualified|PhpParserUnionType|null
*/
private function matchTypeForUnionedObjectTypes(UnionType $unionType): ?Node
{
Expand All @@ -481,7 +387,7 @@ private function matchTypeForUnionedObjectTypes(UnionType $unionType): ?Node
}
}

return new Name\FullyQualified($unionedType->getClassName());
return new FullyQualified($unionedType->getClassName());
}

return null;
Expand Down
Loading