Skip to content

Commit 2977ede

Browse files
committed
Rename to PHPStanTypeParser
1 parent 6c95a37 commit 2977ede

16 files changed

Lines changed: 256 additions & 114 deletions

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Typhoon PHPStan PHPDoc Parser Bridge
1+
# Typhoon PHPStan Type Parser
22

3-
[![PHP Version Requirement](https://img.shields.io/packagist/dependency-v/typhoon/phpstan-phpdoc-parser-bridge/php)](https://packagist.org/packages/typhoon/phpstan-phpdoc-parser-bridge)
4-
[![GitHub Release](https://img.shields.io/github/v/release/typhoon-php/phpstan-phpdoc-parser-bridge)](https://github.com/typhoon-php/phpstan-phpdoc-parser-bridge/releases)
5-
[![Code Coverage](https://codecov.io/gh/typhoon-php/phpstan-phpdoc-parser-bridge/branch/0.1.x/graph/badge.svg)](https://codecov.io/gh/typhoon-php/phpstan-phpdoc-parser-bridge/tree/0.1.x)
6-
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Ftyphoon-php%2Fphpstan-phpdoc-parser-bridge%2F0.1.x)](https://dashboard.stryker-mutator.io/reports/github.com/typhoon-php/phpstan-phpdoc-parser-bridge/0.1.x)
3+
[![PHP Version Requirement](https://img.shields.io/packagist/dependency-v/typhoon/phpstan-type-parser/php)](https://packagist.org/packages/typhoon/phpstan-type-parser)
4+
[![GitHub Release](https://img.shields.io/github/v/release/typhoon-php/phpstan-type-parser)](https://github.com/typhoon-php/phpstan-type-parser/releases)
5+
[![Code Coverage](https://codecov.io/gh/typhoon-php/phpstan-type-parser/branch/0.1.x/graph/badge.svg)](https://codecov.io/gh/typhoon-php/phpstan-type-parser/tree/0.1.x)
6+
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Ftyphoon-php%2Fphpstan-type-parser%2F0.1.x)](https://dashboard.stryker-mutator.io/reports/github.com/typhoon-php/phpstan-type-parser/0.1.x)
77

88
## Installation
99

1010
```shell
11-
composer require typhoon/phpstan-phpdoc-parser-bridge
11+
composer require typhoon/phpstan-type-parser
1212
```

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "typhoon/phpstan-phpdoc-parser-bridge",
3-
"description": "Typhoon PHPStan PHPDoc Parser Bridge",
2+
"name": "typhoon/phpstan-type-parser",
3+
"description": "Typhoon PHPStan Type Parser",
44
"license": "MIT",
55
"type": "library",
66
"authors": [
@@ -25,12 +25,12 @@
2525
},
2626
"autoload": {
2727
"psr-4": {
28-
"Typhoon\\PHPStanPhpDocParserBridge\\": "src/"
28+
"Typhoon\\PHPStanTypeParser\\": "src/"
2929
}
3030
},
3131
"autoload-dev": {
3232
"psr-4": {
33-
"Typhoon\\PHPStanPhpDocParserBridge\\": "tests/"
33+
"Typhoon\\PHPStanTypeParser\\": "tests/"
3434
}
3535
},
3636
"config": {

composer.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/CustomTypeParser.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Typhoon\PHPStanTypeParser;
6+
7+
use Typhoon\Type\Type;
8+
9+
/**
10+
* @api
11+
*/
12+
interface CustomTypeParser
13+
{
14+
/**
15+
* @param non-empty-string $unresolvedName
16+
* @param list<Type> $typeArguments
17+
*/
18+
public function parseCustomType(string $unresolvedName, array $typeArguments, TypeContext $context): ?Type;
19+
}

src/CustomTypeParsers.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Typhoon\PHPStanTypeParser;
6+
7+
use Typhoon\Type\Type;
8+
9+
/**
10+
* @api
11+
*/
12+
final class CustomTypeParsers implements CustomTypeParser
13+
{
14+
/**
15+
* @param iterable<CustomTypeParser> $customTypeParsers
16+
*/
17+
public function __construct(
18+
private readonly iterable $customTypeParsers = [],
19+
) {}
20+
21+
public function parseCustomType(string $unresolvedName, array $typeArguments, TypeContext $context): ?Type
22+
{
23+
foreach ($this->customTypeParsers as $customTypeParser) {
24+
$type = $customTypeParser->parseCustomType($unresolvedName, $typeArguments, $context);
25+
26+
if ($type !== null) {
27+
return $type;
28+
}
29+
}
30+
31+
return null;
32+
}
33+
}
Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Typhoon\PHPStanPhpDocParserBridge\Internal;
5+
namespace Typhoon\PHPStanTypeParser\Internal;
66

77
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
88
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
@@ -17,9 +17,10 @@
1717
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
1818
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
1919
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
20+
use Typhoon\PHPStanTypeParser\CustomTypeParser;
21+
use Typhoon\PHPStanTypeParser\TypeContext;
2022
use Typhoon\Type\Type;
2123
use function Typhoon\Type\andT;
22-
use function Typhoon\Type\diffT;
2324
use function Typhoon\Type\floatRangeT;
2425
use function Typhoon\Type\floatT;
2526
use function Typhoon\Type\intRangeT;
@@ -48,34 +49,39 @@
4849

4950
/**
5051
* @internal
51-
* @psalm-internal Typhoon\PHPStanPhpDocParserBridge
52+
* @psalm-internal Typhoon\PHPStanTypeParser
5253
*/
53-
final class TypeConverter
54+
final class ContextualTypeParser
5455
{
55-
public function convert(TypeNode $node): Type
56+
public function __construct(
57+
private readonly CustomTypeParser $customTypeParser,
58+
private readonly TypeContext $context,
59+
) {}
60+
61+
public function parseTypeNode(TypeNode $node): Type
5662
{
5763
if ($node instanceof NullableTypeNode) {
58-
return nullOrT($this->convert($node->type));
64+
return nullOrT($this->parseTypeNode($node->type));
5965
}
6066

6167
if ($node instanceof ConstTypeNode) {
62-
return $this->reflectConstExpr($node);
68+
return $this->parseConstExpr($node);
6369
}
6470

6571
if ($node instanceof IdentifierTypeNode) {
66-
return $this->reflectIdentifier($node->name);
72+
return $this->parseIdentifier($node->name);
6773
}
6874

6975
if ($node instanceof GenericTypeNode) {
70-
return $this->reflectIdentifier($node->type->name, $node->genericTypes);
76+
return $this->parseIdentifier($node->type->name, $node->genericTypes);
7177
}
7278

7379
if ($node instanceof UnionTypeNode) {
74-
return orT(...array_map($this->convert(...), $node->types));
80+
return orT(...array_map($this->parseTypeNode(...), $node->types));
7581
}
7682

7783
if ($node instanceof IntersectionTypeNode) {
78-
return andT(...array_map($this->convert(...), $node->types));
84+
return andT(...array_map($this->parseTypeNode(...), $node->types));
7985
}
8086

8187
throw new \LogicException(\sprintf('`%s` is not supported', $node::class));
@@ -85,8 +91,10 @@ public function convert(TypeNode $node): Type
8591
* @param non-empty-string $name
8692
* @param list<TypeNode> $genericNodes
8793
*/
88-
private function reflectIdentifier(string $name, array $genericNodes = []): Type
94+
private function parseIdentifier(string $name, array $genericNodes = []): Type
8995
{
96+
$typeArguments = fn(): array => array_map($this->parseTypeNode(...), $genericNodes);
97+
9098
return match ($name) {
9199
'never' => neverT,
92100
'void' => voidT,
@@ -102,8 +110,8 @@ private function reflectIdentifier(string $name, array $genericNodes = []): Type
102110
'int', 'integer' => match (\count($genericNodes)) {
103111
0 => intT,
104112
2 => intRangeT(
105-
min: $this->reflectRangeLimit($genericNodes[0], 'min', float: false),
106-
max: $this->reflectRangeLimit($genericNodes[1], 'max', float: false),
113+
min: $this->parseRangeLimit($genericNodes[0], 'min', float: false),
114+
max: $this->parseRangeLimit($genericNodes[1], 'max', float: false),
107115
),
108116
default => throw new \LogicException(\sprintf(
109117
'int range type should have 2 type arguments, got %d',
@@ -113,30 +121,30 @@ private function reflectIdentifier(string $name, array $genericNodes = []): Type
113121
'float' => match (\count($genericNodes)) {
114122
0 => floatT,
115123
2 => floatRangeT(
116-
min: $this->reflectRangeLimit($genericNodes[0], 'min', float: true),
117-
max: $this->reflectRangeLimit($genericNodes[1], 'max', float: true),
124+
min: $this->parseRangeLimit($genericNodes[0], 'min', float: true),
125+
max: $this->parseRangeLimit($genericNodes[1], 'max', float: true),
118126
),
119127
default => throw new \LogicException(\sprintf(
120128
'float range type should have 2 type arguments, got %d',
121129
\count($genericNodes),
122130
))
123131
},
124-
'diff' => diffT($this->convert($genericNodes[0]), $this->convert($genericNodes[1])),
125132
'string' => stringT,
126133
'non-empty-string' => nonEmptyStringT,
127134
'resource' => resourceT,
128135
'array-key' => arrayKeyT,
129136
'scalar' => scalarT,
130137
'mixed' => mixedT,
131-
default => throw new \LogicException(),
138+
default => $this->customTypeParser->parseCustomType($name, $typeArguments(), $this->context)
139+
?? throw new \LogicException(\sprintf('Unknown identifier `%s`', $name)),
132140
};
133141
}
134142

135143
/**
136144
* @param 'min'|'max' $name
137145
* @return ?numeric-string
138146
*/
139-
private function reflectRangeLimit(TypeNode $type, string $name, bool $float): ?string
147+
private function parseRangeLimit(TypeNode $type, string $name, bool $float): ?string
140148
{
141149
if ($type instanceof IdentifierTypeNode) {
142150
if ($type->name === $name) {
@@ -163,7 +171,7 @@ private function reflectRangeLimit(TypeNode $type, string $name, bool $float): ?
163171
throw new \LogicException();
164172
}
165173

166-
private function reflectConstExpr(ConstTypeNode $node): Type
174+
private function parseConstExpr(ConstTypeNode $node): Type
167175
{
168176
$exprNode = $node->constExpr;
169177

src/PHPStanParser.php

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/PHPStanTypeParser.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Typhoon\PHPStanTypeParser;
6+
7+
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8+
use PHPStan\PhpDocParser\Lexer\Lexer;
9+
use PHPStan\PhpDocParser\Parser\ConstExprParser;
10+
use PHPStan\PhpDocParser\Parser\TokenIterator;
11+
use PHPStan\PhpDocParser\Parser\TypeParser;
12+
use PHPStan\PhpDocParser\ParserConfig;
13+
use Typhoon\PHPStanTypeParser\Internal\ContextualTypeParser;
14+
use Typhoon\Type\Type;
15+
16+
/**
17+
* @api
18+
*/
19+
final class PHPStanTypeParser
20+
{
21+
private readonly CustomTypeParser $customTypeParser;
22+
23+
/**
24+
* @param iterable<CustomTypeParser> $customTypeParsers
25+
*/
26+
public function __construct(
27+
iterable $customTypeParsers = [],
28+
private readonly Lexer $lexer = new Lexer(new ParserConfig([])),
29+
private readonly TypeParser $typeParser = new TypeParser(
30+
new ParserConfig([]),
31+
new ConstExprParser(new ParserConfig([])),
32+
),
33+
) {
34+
$this->customTypeParser = new CustomTypeParsers($customTypeParsers);
35+
}
36+
37+
public function parseString(string $type, TypeContext $context = new RuntimeTypeContext()): Type
38+
{
39+
$tokens = new TokenIterator($this->lexer->tokenize($type));
40+
$typeNode = $this->typeParser->parse($tokens);
41+
42+
return $this->parseTypeNode($typeNode, $context);
43+
}
44+
45+
public function parseTypeNode(TypeNode $node, TypeContext $context = new RuntimeTypeContext()): Type
46+
{
47+
return (new ContextualTypeParser($this->customTypeParser, $context))->parseTypeNode($node);
48+
}
49+
}

0 commit comments

Comments
 (0)