Failed Conditions
Push — type ( 257e93...5997a7 )
by Michael
02:15
created

PHPStanTypeParser::parse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Annotations\TypeParser;
6
7
use Doctrine\Annotations\Metadata\Type\BooleanType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\BooleanType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Doctrine\Annotations\Metadata\Type\FloatType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\FloatType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Doctrine\Annotations\Metadata\Type\IntegerType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\IntegerType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Doctrine\Annotations\Metadata\Type\IntersectionType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Met...a\Type\IntersectionType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Doctrine\Annotations\Metadata\Type\ListType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\ListType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Doctrine\Annotations\Metadata\Type\MapType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\MapType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Doctrine\Annotations\Metadata\Type\MixedType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\MixedType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Doctrine\Annotations\Metadata\Type\NullType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\NullType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Doctrine\Annotations\Metadata\Type\ObjectType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\ObjectType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use Doctrine\Annotations\Metadata\Type\StringType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\StringType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Doctrine\Annotations\Metadata\Type\Type;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\Type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Doctrine\Annotations\Metadata\Type\UnionType;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Metadata\Type\UnionType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Doctrine\Annotations\Parser\Ast\Reference;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Parser\Ast\Reference was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use Doctrine\Annotations\Parser\Reference\ReferenceResolver;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Par...rence\ReferenceResolver was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Doctrine\Annotations\Parser\Scope;
0 ignored issues
show
Bug introduced by
The type Doctrine\Annotations\Parser\Scope was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\GenericTypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\NullableTypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\TypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Ast\Type\UnionTypeNode was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
30
use PHPStan\PhpDocParser\Lexer\Lexer;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Lexer\Lexer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
use PHPStan\PhpDocParser\Parser\PhpDocParser;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Parser\PhpDocParser was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
use PHPStan\PhpDocParser\Parser\TokenIterator;
0 ignored issues
show
Bug introduced by
The type PHPStan\PhpDocParser\Parser\TokenIterator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
use function array_map;
34
use function assert;
35
use function count;
36
use function ltrim;
37
use function strcasecmp;
38
use function strtolower;
39
40
final class PHPStanTypeParser implements TypeParser
41
{
42
    /** @var Lexer */
43
    private $lexer;
44
45
    /** @var PhpDocParser */
46
    private $phpDocParser;
47
48
    /** @var ReferenceResolver */
49
    private $referenceResolver;
50
51
    public function __construct(Lexer $lexer, PhpDocParser $phpDocParser, ReferenceResolver $referenceResolver)
52
    {
53
        $this->lexer             = $lexer;
54
        $this->phpDocParser      = $phpDocParser;
55
        $this->referenceResolver = $referenceResolver;
56
    }
57
58
    public function parsePropertyType(string $docBlock, Scope $scope) : Type
59
    {
60
        $tags = $this->parse($docBlock)->getVarTagValues();
61
62
        assert(count($tags) <= 1, 'multiple @var tags not allowed');
63
64
        if (count($tags) === 0) {
65
            return new MixedType();
66
        }
67
68
        return $this->resolveType($tags[0]->type, $scope);
69
    }
70
71
    private function parse(string $docBlock) : PhpDocNode
72
    {
73
        return $this->phpDocParser->parse(new TokenIterator($this->lexer->tokenize($docBlock)));
74
    }
75
76
    private function resolveType(TypeNode $typeNode, Scope $scope) : Type
77
    {
78
        if ($typeNode instanceof IdentifierTypeNode && strcasecmp($typeNode->name, 'null') === 0) {
79
            return new NullType();
80
        }
81
82
        if ($typeNode instanceof NullableTypeNode) {
83
            return new UnionType($this->resolveType($typeNode->type, $scope), new NullType());
84
        }
85
86
        if ($typeNode instanceof UnionTypeNode) {
87
            return new UnionType(
88
                ...array_map(
89
                    function (TypeNode $type) use ($scope) : Type {
90
                        return $this->resolveType($type, $scope);
91
                    },
92
                    $typeNode->types
93
                )
94
            );
95
        }
96
97
        if ($typeNode instanceof IntersectionTypeNode) {
98
            return new IntersectionType(
99
                ...array_map(
100
                    function (TypeNode $type) use ($scope) : Type {
101
                        return $this->resolveType($type, $scope);
102
                    },
103
                    $typeNode->types
104
                )
105
            );
106
        }
107
108
        if ($typeNode instanceof ArrayTypeNode) {
109
            return $this->resolveArrayTypeNode($typeNode, $scope);
110
        }
111
112
        if ($typeNode instanceof GenericTypeNode) {
113
            return $this->resolveGenericNode($typeNode, $scope);
114
        }
115
116
        assert($typeNode instanceof IdentifierTypeNode, 'Unsupported node type');
117
118
        return $this->resolveIdentifierNode($typeNode, $scope);
119
    }
120
121
    private function resolveGenericNode(GenericTypeNode $typeNode, Scope $scope) : Type
122
    {
123
        assert(
124
            strcasecmp($typeNode->type->name, 'array') === 0
125
            || strcasecmp($typeNode->type->name, 'iterable') === 0
126
        );
127
128
        if (count($typeNode->genericTypes) === 1) {
129
            return new ListType($this->resolveType($typeNode->genericTypes[0], $scope));
130
        }
131
132
        if (count($typeNode->genericTypes) === 2) {
133
            assert($typeNode->genericTypes[0] instanceof IdentifierTypeNode);
134
135
            return new MapType(
136
                $this->resolveIdentifierNode($typeNode->genericTypes[0], $scope),
137
                $this->resolveType($typeNode->genericTypes[1], $scope)
138
            );
139
        }
140
141
        assert(false, '>2 generic type args');
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Doctrine\Annotations\Metadata\Type\Type. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
142
    }
143
144
    private function resolveArrayTypeNode(ArrayTypeNode $typeNode, Scope $scope) : Type
145
    {
146
        return new ListType($this->resolveType($typeNode->type, $scope));
147
    }
148
149
    private function resolveIdentifierNode(TypeNode $typeNode, Scope $scope) : Type
150
    {
151
        assert($typeNode instanceof IdentifierTypeNode);
152
153
        $canonicalName = strtolower($typeNode->name);
154
155
        switch ($canonicalName) {
156
            case 'bool':
157
            case 'boolean':
158
                return new BooleanType();
159
            case 'int':
160
            case 'integer':
161
                return new IntegerType();
162
            case 'float':
163
            case 'double':
164
            case 'real':
165
                return new FloatType();
166
            case 'string':
167
                return new StringType();
168
            case 'array':
169
                return new MapType(new UnionType(new IntegerType(), new StringType()), new MixedType());
170
            case 'mixed':
171
                return new MixedType(); // TODO not really a scalar
172
        }
173
174
        $fullyQualified = $typeNode->name[0] === '\\';
175
        return new ObjectType(
176
            $this->referenceResolver->resolve(
177
                new Reference(
178
                    $fullyQualified ? ltrim($typeNode->name, '\\') : $typeNode->name,
179
                    $fullyQualified
180
                ),
181
                $scope
182
            )
183
        );
184
    }
185
}
186