Skip to content

Commit df2c56f

Browse files
committed
add test that consider uses.
1 parent b473243 commit df2c56f

File tree

3 files changed

+126
-39
lines changed

3 files changed

+126
-39
lines changed

src/Php/PhpTypeExpression.php

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,21 @@ class PhpTypeExpression {
2020
private string $docString = '';
2121
/** @var PhpType[] */
2222
private array $types;
23+
/** @var PhpType[] */
24+
private array $uses;
2325

24-
private function __construct(NodeAbstract $stmt, string $targetType, array $currentNamespace, string $docString) {
26+
/**
27+
* @param NodeAbstract $stmt 対象のツリー
28+
* @param string $targetType
29+
* @param string[] $currentNamespace
30+
* @param string $docString
31+
* @param PhpType[] $uses
32+
*/
33+
private function __construct(NodeAbstract $stmt, string $targetType, array $currentNamespace, string $docString, array $uses) {
2534
if ( ! in_array($targetType, [self::VAR, self::PARAM, self::RETURN_TYPE])) {
2635
throw new \Exception('invalid tag.');
2736
}
37+
$this->uses = $uses;
2838

2939
$type = $stmt->{$targetType === self::RETURN_TYPE ? 'returnType' : 'type'};
3040
if ( ! empty($docString)) {
@@ -40,7 +50,17 @@ private function __construct(NodeAbstract $stmt, string $targetType, array $curr
4050
}
4151
}
4252

43-
public static function buildByVar(NodeAbstract $stmt, array $currentNamespace): self {
53+
/**
54+
* @param NodeAbstract $stmt
55+
* @param string[] $currentNamespace
56+
* @param PhpType[] $uses
57+
* @return self
58+
*/
59+
public static function buildByVar(
60+
NodeAbstract $stmt,
61+
array $currentNamespace,
62+
array $uses
63+
): self {
4464
$doc = $stmt->getDocComment();
4565
$typeString = '';
4666
if ($doc instanceof Doc) {
@@ -49,18 +69,43 @@ public static function buildByVar(NodeAbstract $stmt, array $currentNamespace):
4969
$typeString = $matches[1];
5070
}
5171
}
52-
return new self($stmt, self::VAR, $currentNamespace, $typeString);
72+
return new self($stmt, self::VAR, $currentNamespace, $typeString, $uses);
5373
}
54-
public static function buildByMethodParam(NodeAbstract $stmt, array $currentNamespace, string $docString, string $paramName): self {
74+
75+
/**
76+
* @param NodeAbstract $stmt
77+
* @param string[] $currentNamespace
78+
* @param string $docString
79+
* @param PhpType[] $uses
80+
* @return self
81+
*/
82+
public static function buildByMethodParam(
83+
NodeAbstract $stmt,
84+
array $currentNamespace,
85+
string $docString,
86+
string $paramName,
87+
array $uses
88+
): self {
5589
$typeString = '';
5690
if (!empty($docString)) {
5791
if (preg_match(sprintf('/@%s\s+(\S+)(\b|\s)\s*\$%s.*/', 'param', $paramName), $docString, $matches)) {
5892
$typeString = $matches[1];
5993
}
6094
}
61-
return new self($stmt, self::PARAM, $currentNamespace, $typeString);
95+
return new self($stmt, self::PARAM, $currentNamespace, $typeString, $uses);
6296
}
63-
public static function buildByMethodReturn(NodeAbstract $stmt, array $currentNamespace): self {
97+
98+
/**
99+
* @param NodeAbstract $stmt
100+
* @param string[] $currentNamespace
101+
* @param PhpType[] $uses
102+
* @return self
103+
*/
104+
public static function buildByMethodReturn(
105+
NodeAbstract $stmt,
106+
array $currentNamespace,
107+
array $uses
108+
): self {
64109
$doc = $stmt->getDocComment();
65110
$typeString = '';
66111
if ($doc instanceof Doc) {
@@ -69,7 +114,7 @@ public static function buildByMethodReturn(NodeAbstract $stmt, array $currentNam
69114
$typeString = $matches[1];
70115
}
71116
}
72-
return new self($stmt, self::RETURN_TYPE, $currentNamespace, $typeString);
117+
return new self($stmt, self::RETURN_TYPE, $currentNamespace, $typeString, $uses);
73118
}
74119

75120
/**
@@ -96,12 +141,21 @@ private function parseType(Property|Identifier|NullableType|Name|null $type, arr
96141
} else {
97142
if (mb_substr($typeString, 0, 1) === '\\') {
98143
$docString = mb_substr($typeString, 1);
144+
$parts = explode('\\', $docString);
99145
} else {
100-
// TODO usesを検索して適切なnamespaceを探す必要がある。
101-
102-
$docString = sprintf('%s\\%s', implode('\\', $currentNamespace), $typeString);
146+
// usesを検索して適切なnamespaceを探す必要がある。
147+
$targets = array_filter($this->uses, function(PhpType $t) use($typeString) {
148+
$xParts = explode('\\', $typeString);
149+
$name = array_pop($xParts);
150+
return $name === $t->getName();
151+
});
152+
if (count($targets) > 0) {
153+
$parts = array_merge($targets[0]->getNamespace());
154+
} else {
155+
$docString = sprintf('%s\\%s', implode('\\', $currentNamespace), $typeString);
156+
$parts = explode('\\', $docString);
157+
}
103158
}
104-
$parts = explode('\\', $docString);
105159
}
106160
}
107161
$nullable = false;
@@ -115,9 +169,17 @@ private function parseType(Property|Identifier|NullableType|Name|null $type, arr
115169
} else if ($type instanceOf FullyQualified) {
116170
$parts = $type->parts;
117171
} else if ($type instanceOf Name) {
118-
// TODO usesを検索して適切なnamespaceを探す必要がある。
119-
120-
$parts = array_merge($currentNamespace, $type->parts);
172+
$typeParts = $type->parts;
173+
// usesを検索して適切なnamespaceを探す必要がある。
174+
$targets = array_filter($this->uses, function(PhpType $t) use($typeParts) {
175+
$name = array_pop($typeParts);
176+
return $name === $t->getName();
177+
});
178+
if (count($targets) > 0) {
179+
$parts = array_merge($targets[0]->getNamespace(), [array_pop($typeParts)]);
180+
} else {
181+
$parts = array_merge($currentNamespace, $type->parts);
182+
}
121183
}
122184
}
123185
$typeName = array_pop($parts);

test/PhpTypeExpressionTest.php

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use PhpParser\ParserFactory;
44
use PHPUnit\Framework\TestCase;
5-
5+
use Smeghead\PhpClassDiagram\Php\PhpType;
66
use Smeghead\PhpClassDiagram\Php\PhpTypeExpression;
77

88
final class PhpTypeExpressionTest extends TestCase {
@@ -21,7 +21,7 @@ public function testNullableString(): void {
2121
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
2222
}
2323

24-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[0], ['hoge', 'fuga', 'product']);
24+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[0], ['hoge', 'fuga', 'product'], []);
2525
$types = $expression->getTypes();
2626

2727
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -37,7 +37,7 @@ public function testIntOrString(): void {
3737
} catch (Error $error) {
3838
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
3939
}
40-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[1], ['hoge', 'fuga', 'product']);
40+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[1], ['hoge', 'fuga', 'product'], []);
4141
$types = $expression->getTypes();
4242

4343
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -56,7 +56,7 @@ public function testPrice(): void {
5656
} catch (Error $error) {
5757
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
5858
}
59-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[2], ['hoge', 'fuga', 'product']);
59+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[2], ['hoge', 'fuga', 'product'], []);
6060
$types = $expression->getTypes();
6161

6262
$this->assertSame(['hoge', 'fuga', 'product'], $types[0]->getNamespace(), 'namespace');
@@ -72,7 +72,7 @@ public function testException(): void {
7272
} catch (Error $error) {
7373
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
7474
}
75-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[4], ['hoge', 'fuga', 'product']);
75+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[4], ['hoge', 'fuga', 'product'], []);
7676
$types = $expression->getTypes();
7777

7878
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -88,7 +88,7 @@ public function testRelated(): void {
8888
} catch (Error $error) {
8989
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
9090
}
91-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[5], ['hoge', 'fuga', 'product']);
91+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[5], ['hoge', 'fuga', 'product'], []);
9292
$types = $expression->getTypes();
9393

9494
$this->assertSame(['hoge', 'fuga', 'product', 'bar'], $types[0]->getNamespace(), 'namespace');
@@ -104,7 +104,7 @@ public function testAbsolute(): void {
104104
} catch (Error $error) {
105105
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
106106
}
107-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[6], ['hoge', 'fuga', 'product']);
107+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[6], ['hoge', 'fuga', 'product'], []);
108108
$types = $expression->getTypes();
109109

110110
$this->assertSame(['hoge', 'fuga', 'product', 'bar'], $types[0]->getNamespace(), 'namespace');
@@ -121,7 +121,7 @@ public function testDocString(): void {
121121
} catch (Error $error) {
122122
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
123123
}
124-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[7], ['hoge', 'fuga', 'product']);
124+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[7], ['hoge', 'fuga', 'product'], []);
125125
$types = $expression->getTypes();
126126

127127
$this->assertSame(['hoge', 'fuga', 'product', 'bur'], $types[0]->getNamespace(), 'namespace');
@@ -138,7 +138,7 @@ public function testDocStringUnion(): void {
138138
} catch (Error $error) {
139139
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
140140
}
141-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[8], ['hoge', 'fuga', 'product']);
141+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[8], ['hoge', 'fuga', 'product'], []);
142142
$types = $expression->getTypes();
143143

144144
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -158,7 +158,7 @@ public function testDocStringUnion2(): void {
158158
} catch (Error $error) {
159159
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
160160
}
161-
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[9], ['hoge', 'fuga', 'product']);
161+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[2]->stmts[9], ['hoge', 'fuga', 'product'], []);
162162
$types = $expression->getTypes();
163163

164164
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -178,10 +178,10 @@ public function testMethodParameterInt(): void {
178178
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
179179
}
180180
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
181-
$doc = $ast[0]->stmts[1]->stmts[10]->getDocComment();
181+
$doc = $ast[0]->stmts[2]->stmts[10]->getDocComment();
182182
$docString = $doc->getText();
183-
$param = $ast[0]->stmts[1]->stmts[10]->getParams()[0];
184-
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint');
183+
$param = $ast[0]->stmts[2]->stmts[10]->getParams()[0];
184+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint', []);
185185
$types = $expression->getTypes();
186186

187187
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -198,10 +198,10 @@ public function testMethodParameterPrice(): void {
198198
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
199199
}
200200
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
201-
$doc = $ast[0]->stmts[1]->stmts[10]->getDocComment();
201+
$doc = $ast[0]->stmts[2]->stmts[10]->getDocComment();
202202
$docString = $doc->getText();
203-
$param = $ast[0]->stmts[1]->stmts[10]->getParams()[1];
204-
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint');
203+
$param = $ast[0]->stmts[2]->stmts[10]->getParams()[1];
204+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint', []);
205205
$types = $expression->getTypes();
206206

207207
$this->assertSame(['hoge', 'fuga', 'product'], $types[0]->getNamespace(), 'namespace');
@@ -218,10 +218,10 @@ public function testMethodParameterDocString(): void {
218218
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
219219
}
220220
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
221-
$doc = $ast[0]->stmts[1]->stmts[10]->getDocComment();
221+
$doc = $ast[0]->stmts[2]->stmts[10]->getDocComment();
222222
$docString = $doc->getText();
223-
$param = $ast[0]->stmts[1]->stmts[10]->getParams()[2];
224-
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'param1');
223+
$param = $ast[0]->stmts[2]->stmts[10]->getParams()[2];
224+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'param1', []);
225225
$types = $expression->getTypes();
226226

227227
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -240,9 +240,9 @@ public function testMethodReturnInt(): void {
240240
} catch (Error $error) {
241241
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
242242
}
243-
$method = $ast[0]->stmts[1]->stmts[10];
243+
$method = $ast[0]->stmts[2]->stmts[10];
244244
// var_dump($method);die();
245-
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product']);
245+
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product'], []);
246246
$types = $expression->getTypes();
247247

248248
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -258,9 +258,9 @@ public function testMethodReturnProduct(): void {
258258
} catch (Error $error) {
259259
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
260260
}
261-
$method = $ast[0]->stmts[1]->stmts[11];
261+
$method = $ast[0]->stmts[2]->stmts[11];
262262
// var_dump($method);die();
263-
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product']);
263+
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product'], []);
264264
$types = $expression->getTypes();
265265

266266
$this->assertSame(['hoge', 'fuga', 'product'], $types[0]->getNamespace(), 'namespace');
@@ -276,14 +276,34 @@ public function testMethodReturnArray(): void {
276276
} catch (Error $error) {
277277
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
278278
}
279-
$method = $ast[0]->stmts[1]->stmts[12];
279+
$method = $ast[0]->stmts[2]->stmts[12];
280280
// var_dump($method);die();
281-
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product']);
281+
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product'], []);
282282
$types = $expression->getTypes();
283283

284284
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
285285
$this->assertSame('array', $types[0]->getName(), 'name');
286286
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
287287
}
288+
public function testMethodParameterTag(): void {
289+
// /** @params string|int $param1 */
290+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
291+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
292+
try {
293+
$ast = $parser->parse(file_get_contents($filename));
294+
} catch (Error $error) {
295+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
296+
}
297+
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
298+
$docString = '';
299+
$param = $ast[0]->stmts[2]->stmts[13]->getParams()[0];
300+
$uses = [new PhpType(['hoge', 'fuga', 'product', 'tag'], '', 'Tag')];
301+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint', $uses);
302+
$types = $expression->getTypes();
303+
304+
$this->assertSame(['hoge', 'fuga', 'product', 'tag'], $types[0]->getNamespace(), 'namespace');
305+
$this->assertSame('Tag', $types[0]->getName(), 'name');
306+
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
307+
}
288308

289309
}

test/fixtures/php8/product/Product.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Name,
66
Price,
77
};
8+
use hoge\fuga\product\tag\Tag;
89

910
class Product {
1011
private ?string $nullableString;
@@ -38,4 +39,8 @@ public function method2(): int {
3839
public function method3(): array {
3940
return [];
4041
}
42+
43+
public function method4(Tag $tag): array {
44+
return [];
45+
}
4146
}

0 commit comments

Comments
 (0)