Passed
Push — master ( 161cd2...024c4a )
by Shinji
02:48
created

PhpDocTypeReader::getVarTypes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
eloc 8
c 2
b 1
f 1
dl 0
loc 13
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
/**
4
 * This file is part of the sj-i/phpdoc-type-reader package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace PhpDocTypeReader;
15
16
use PhpDocTypeReader\Context\IdentifierContext;
17
use PhpDocTypeReader\Type\BoolType;
18
use PhpDocTypeReader\Type\FloatType;
19
use PhpDocTypeReader\Type\GenericType;
20
use PhpDocTypeReader\Type\IntType;
21
use PhpDocTypeReader\Type\ObjectType;
22
use PhpDocTypeReader\Type\StringType;
23
use PhpDocTypeReader\Type\Type;
24
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
25
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
26
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
27
use PHPStan\PhpDocParser\Lexer\Lexer;
28
use PHPStan\PhpDocParser\Parser\ConstExprParser;
29
use PHPStan\PhpDocParser\Parser\PhpDocParser;
30
use PHPStan\PhpDocParser\Parser\TokenIterator;
31
use PHPStan\PhpDocParser\Parser\TypeParser;
32
33
final class PhpDocTypeReader
34
{
35
    private PhpDocParser $php_doc_parser;
36
    private Lexer $lexer;
37
38
    public function __construct(
39
        ?PhpDocParser $php_doc_parser = null,
40
        ?Lexer $lexer = null
41
    ) {
42
        if (is_null($php_doc_parser)) {
43
            $const_expr_parser = new ConstExprParser();
44
            $php_doc_parser = new PhpDocParser(
45
                new TypeParser($const_expr_parser),
46
                $const_expr_parser
47
            );
48
        }
49
        if (is_null($lexer)) {
50
            $lexer = new Lexer();
51
        }
52
        $this->php_doc_parser = $php_doc_parser;
53
        $this->lexer = $lexer;
54
    }
55
56
    public function getVarTypes(string $doc_comment, IdentifierContext $identifier_context): Type
57
    {
58
        $tokens = $this->lexer->tokenize($doc_comment);
59
        $token_iterator = new TokenIterator($tokens);
60
        $php_doc_node = $this->php_doc_parser->parse($token_iterator);
61
        $var_tag_values = $php_doc_node->getVarTagValues();
62
63
        if (count($var_tag_values) < 1) {
64
            throw new \LogicException('cannot find @var');
65
        }
66
67
        $var_tag = current($var_tag_values);
68
        return $this->getTypeFromNodeType($var_tag->type, $identifier_context);
69
    }
70
71
    /**
72
     * @return array<string, Type>
73
     */
74
    public function getParamTypes(string $doc_comment, IdentifierContext $identifier_context): array
75
    {
76
        $tokens = $this->lexer->tokenize($doc_comment);
77
        $token_iterator = new TokenIterator($tokens);
78
        $php_doc_node = $this->php_doc_parser->parse($token_iterator);
79
        $param_tag_values = $php_doc_node->getParamTagValues();
80
81
        if (count($param_tag_values) < 1) {
82
            throw new \LogicException('cannot find @param');
83
        }
84
85
        $result = [];
86
        foreach ($param_tag_values as $param_tag_value) {
87
            $result[ltrim($param_tag_value->parameterName, '$')] = $this->getTypeFromNodeType(
88
                $param_tag_value->type,
89
                $identifier_context
90
            );
91
        }
92
        return $result;
93
    }
94
95
    private function getTypeFromNodeType(TypeNode $type_node, IdentifierContext $identifier_context): Type
96
    {
97
        if ($type_node instanceof IdentifierTypeNode) {
98
            switch ($type_node->name) {
99
                case 'int':
100
                    return new IntType();
101
                case 'string':
102
                    return new StringType();
103
                case 'float':
104
                    return new FloatType();
105
                case 'bool':
106
                    return new BoolType();
107
                default:
108
                    return new ObjectType(
109
                        $this->tryGetClassNameFromIdentifier($type_node, $identifier_context)
110
                    );
111
            }
112
        }
113
        if ($type_node instanceof GenericTypeNode) {
114
            return new GenericType(
115
                new ObjectType($this->tryGetClassNameFromIdentifier($type_node->type, $identifier_context)),
116
                array_map(
117
                    fn ($type) => $this->getTypeFromNodeType($type, $identifier_context),
118
                    $type_node->genericTypes
119
                )
120
            );
121
        }
122
        /** @psalm-suppress ForbiddenCode */
123
        var_dump($type_node);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($type_node) looks like debug code. Are you sure you do not want to remove it?
Loading history...
124
        throw new \LogicException('unsupported type');
125
    }
126
127
    private function tryGetClassNameFromIdentifier(
128
        IdentifierTypeNode $type,
129
        IdentifierContext $identifier_context
130
    ): string {
131
        return $identifier_context->getFqnFromContext($type->name);
132
    }
133
}
134