| 1 |  |  | <?php | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | declare(strict_types=1); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | namespace Doctrine\Annotations\TypeParser; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | use Doctrine\Annotations\Metadata\Type\BooleanType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | use Doctrine\Annotations\Metadata\Type\FloatType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | use Doctrine\Annotations\Metadata\Type\IntegerType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | use Doctrine\Annotations\Metadata\Type\IntersectionType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | use Doctrine\Annotations\Metadata\Type\ListType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | use Doctrine\Annotations\Metadata\Type\MapType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | use Doctrine\Annotations\Metadata\Type\MixedType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | use Doctrine\Annotations\Metadata\Type\NullType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  | use Doctrine\Annotations\Metadata\Type\ObjectType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | use Doctrine\Annotations\Metadata\Type\StringType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  | use Doctrine\Annotations\Metadata\Type\Type; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | use Doctrine\Annotations\Metadata\Type\UnionType; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | use Doctrine\Annotations\Parser\Ast\Reference; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  | use Doctrine\Annotations\Parser\Reference\ReferenceResolver; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  | use Doctrine\Annotations\Parser\Scope; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  | use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  | use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  | use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  | use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  | use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  | use PHPStan\PhpDocParser\Ast\Type\TypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  | use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  | use PHPStan\PhpDocParser\Lexer\Lexer; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | use PHPStan\PhpDocParser\Parser\PhpDocParser; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  | use PHPStan\PhpDocParser\Parser\TokenIterator; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 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 | 30 |  |     public function __construct(Lexer $lexer, PhpDocParser $phpDocParser, ReferenceResolver $referenceResolver) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 | 30 |  |         $this->lexer             = $lexer; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 | 30 |  |         $this->phpDocParser      = $phpDocParser; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 | 30 |  |         $this->referenceResolver = $referenceResolver; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 | 30 |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 | 27 |  |     public function parsePropertyType(string $docBlock, Scope $scope) : Type | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 | 27 |  |         $tags = $this->parse($docBlock)->getVarTagValues(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 | 27 |  |         assert(count($tags) <= 1, 'multiple @var tags not allowed'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 | 27 |  |         if (count($tags) === 0) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 | 3 |  |             return new MixedType(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 | 24 |  |         return $this->resolveType($tags[0]->type, $scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 | 27 |  |     private function parse(string $docBlock) : PhpDocNode | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 | 27 |  |         return $this->phpDocParser->parse(new TokenIterator($this->lexer->tokenize($docBlock))); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 | 24 |  |     private function resolveType(TypeNode $typeNode, Scope $scope) : Type | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 | 24 |  |         if ($typeNode instanceof IdentifierTypeNode && strcasecmp($typeNode->name, 'null') === 0) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 | 8 |  |             return new NullType(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 | 24 |  |         if ($typeNode instanceof NullableTypeNode) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 | 1 |  |             return new UnionType($this->resolveType($typeNode->type, $scope), new NullType()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 | 24 |  |         if ($typeNode instanceof UnionTypeNode) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 | 9 |  |             return new UnionType( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 | 9 |  |                 ...array_map( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |                     function (TypeNode $type) use ($scope) : Type { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 | 9 |  |                         return $this->resolveType($type, $scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 | 9 |  |                     }, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 | 9 |  |                     $typeNode->types | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |                 ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |             ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 | 24 |  |         if ($typeNode instanceof IntersectionTypeNode) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 | 2 |  |             return new IntersectionType( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 | 2 |  |                 ...array_map( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |                     function (TypeNode $type) use ($scope) : Type { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 | 2 |  |                         return $this->resolveType($type, $scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 | 2 |  |                     }, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 | 2 |  |                     $typeNode->types | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |                 ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |             ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 | 24 |  |         if ($typeNode instanceof ArrayTypeNode) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 | 6 |  |             return $this->resolveArrayTypeNode($typeNode, $scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 | 24 |  |         if ($typeNode instanceof GenericTypeNode) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 | 5 |  |             return $this->resolveGenericNode($typeNode, $scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 | 24 |  |         assert($typeNode instanceof IdentifierTypeNode, 'Unsupported node type'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 | 24 |  |         return $this->resolveIdentifierNode($typeNode, $scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 120 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 121 | 5 |  |     private function resolveGenericNode(GenericTypeNode $typeNode, Scope $scope) : Type | 
            
                                                                        
                            
            
                                    
            
            
                | 122 |  |  |     { | 
            
                                                                        
                            
            
                                    
            
            
                | 123 | 5 |  |         assert( | 
            
                                                                        
                            
            
                                    
            
            
                | 124 | 5 |  |             strcasecmp($typeNode->type->name, 'array') === 0 | 
            
                                                                        
                            
            
                                    
            
            
                | 125 | 5 |  |             || strcasecmp($typeNode->type->name, 'iterable') === 0 | 
            
                                                                        
                            
            
                                    
            
            
                | 126 |  |  |         ); | 
            
                                                                        
                            
            
                                    
            
            
                | 127 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 128 | 5 |  |         if (count($typeNode->genericTypes) === 1) { | 
            
                                                                        
                            
            
                                    
            
            
                | 129 | 3 |  |             return new ListType($this->resolveType($typeNode->genericTypes[0], $scope)); | 
            
                                                                        
                            
            
                                    
            
            
                | 130 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 131 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 132 | 3 |  |         if (count($typeNode->genericTypes) === 2) { | 
            
                                                                        
                            
            
                                    
            
            
                | 133 | 3 |  |             assert($typeNode->genericTypes[0] instanceof IdentifierTypeNode); | 
            
                                                                        
                            
            
                                    
            
            
                | 134 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 135 | 3 |  |             return new MapType( | 
            
                                                                        
                            
            
                                    
            
            
                | 136 | 3 |  |                 $this->resolveIdentifierNode($typeNode->genericTypes[0], $scope), | 
            
                                                                        
                            
            
                                    
            
            
                | 137 | 3 |  |                 $this->resolveType($typeNode->genericTypes[1], $scope) | 
            
                                                                        
                            
            
                                    
            
            
                | 138 |  |  |             ); | 
            
                                                                        
                            
            
                                    
            
            
                | 139 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 140 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 141 |  |  |         assert(false, '>2 generic type args'); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 | 6 |  |     private function resolveArrayTypeNode(ArrayTypeNode $typeNode, Scope $scope) : Type | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 | 6 |  |         return new ListType($this->resolveType($typeNode->type, $scope)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 | 24 |  |     private function resolveIdentifierNode(TypeNode $typeNode, Scope $scope) : Type | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 | 24 |  |         assert($typeNode instanceof IdentifierTypeNode); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 | 24 |  |         $canonicalName = strtolower($typeNode->name); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 | 24 |  |         switch ($canonicalName) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 | 24 |  |             case 'bool': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 | 24 |  |             case 'boolean': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 | 1 |  |                 return new BooleanType(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 | 24 |  |             case 'int': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 | 13 |  |             case 'integer': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 | 15 |  |                 return new IntegerType(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 | 13 |  |             case 'float': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 | 13 |  |             case 'double': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 | 12 |  |             case 'real': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 | 3 |  |                 return new FloatType(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 | 11 |  |             case 'string': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 | 5 |  |                 return new StringType(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 | 8 |  |             case 'array': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 | 2 |  |                 return new MapType(new UnionType(new IntegerType(), new StringType()), new MixedType()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 | 7 |  |             case 'mixed': | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 | 2 |  |                 return new MixedType(); // TODO not really a scalar | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 | 6 |  |         $fullyQualified = $typeNode->name[0] === '\\'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 | 6 |  |         return new ObjectType( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 | 6 |  |             $this->referenceResolver->resolve( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 177 | 6 |  |                 new Reference( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 178 | 6 |  |                     $fullyQualified ? ltrim($typeNode->name, '\\') : $typeNode->name, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 179 | 6 |  |                     $fullyQualified | 
            
                                                                                                            
                            
            
                                    
            
            
                | 180 |  |  |                 ), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 181 | 6 |  |                 $scope | 
            
                                                                                                            
                            
            
                                    
            
            
                | 182 |  |  |             ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 183 |  |  |         ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 184 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 185 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 186 |  |  |  | 
            
                        
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: