Skip to content

Commit 42cccec

Browse files
committed
parse method return type.
1 parent a33fc5c commit 42cccec

File tree

4 files changed

+160
-7
lines changed

4 files changed

+160
-7
lines changed

src/Php/PhpClass.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ protected function findNamespaceByTypeParts(array $type_parts): array {
150150
/**
151151
* php-parserの解析結果の情報から、指定のクラスの型情報を取得します。
152152
* クラスのuseの状態から、namespaceを解決したPhpTypeを返却します。
153-
*
154153
* @param NodeAbstract ClassMethod または Property または Param が渡されることを想定
155154
*/
156155
public function findTypeByTypeParts(NodeAbstract $stmt, string $property, string $docAttribute = ''): PhpType {

src/Php/PhpTypeExpression.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ public static function buildByVar(NodeAbstract $stmt, array $currentNamespace):
5151
}
5252
return new self($stmt, self::VAR, $currentNamespace, $typeString);
5353
}
54+
public static function buildByMethodParam(NodeAbstract $stmt, array $currentNamespace, string $docString, string $paramName): self {
55+
$typeString = '';
56+
if (!empty($docString)) {
57+
if (preg_match(sprintf('/@%s\s+(\S+)(\b|\s)\s*\$%s.*/', 'param', $paramName), $docString, $matches)) {
58+
$typeString = $matches[1];
59+
}
60+
}
61+
return new self($stmt, self::PARAM, $currentNamespace, $typeString);
62+
}
63+
public static function buildByMethodReturn(NodeAbstract $stmt, array $currentNamespace): self {
64+
$doc = $stmt->getDocComment();
65+
$typeString = '';
66+
if ($doc instanceof Doc) {
67+
$docString = $doc->getText();
68+
if (preg_match(sprintf('/@%s\s+(\S+)(\b|\s).*/', 'return'), $docString, $matches)) {
69+
$typeString = $matches[1];
70+
}
71+
}
72+
return new self($stmt, self::RETURN_TYPE, $currentNamespace, $typeString);
73+
}
5474

5575
/**
5676
* @param Property|Identifier|NullableType|Name $type 型を表すAST
@@ -77,6 +97,8 @@ private function parseType(Property|Identifier|NullableType|Name|null $type, arr
7797
if (mb_substr($typeString, 0, 1) === '\\') {
7898
$docString = mb_substr($typeString, 1);
7999
} else {
100+
// TODO usesを検索して適切なnamespaceを探す必要がある。
101+
80102
$docString = sprintf('%s\\%s', implode('\\', $currentNamespace), $typeString);
81103
}
82104
$parts = explode('\\', $docString);
@@ -93,6 +115,8 @@ private function parseType(Property|Identifier|NullableType|Name|null $type, arr
93115
} else if ($type instanceOf FullyQualified) {
94116
$parts = $type->parts;
95117
} else if ($type instanceOf Name) {
118+
// TODO usesを検索して適切なnamespaceを探す必要がある。
119+
96120
$parts = array_merge($currentNamespace, $type->parts);
97121
}
98122
}

test/PhpTypeExpressionTest.php

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ public function testDocString(): void {
129129
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
130130
}
131131
public function testDocStringUnion(): void {
132-
// /** @var bur\Bon $docString */
133-
// private bar\Boo $docString;
132+
// /** @var string|int $docStringUnion */
133+
// private $docStringUnion;
134134
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
135135
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
136136
try {
@@ -148,6 +148,66 @@ public function testDocStringUnion(): void {
148148
$this->assertSame('int', $types[1]->getName(), 'name');
149149
$this->assertSame(false, $types[1]->getNullable(), 'nullable');
150150
}
151+
public function testDocStringUnion2(): void {
152+
// /** @var string|bar\Bon $docStringUnion2 */
153+
// private $docStringUnion2;
154+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
155+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
156+
try {
157+
$ast = $parser->parse(file_get_contents($filename));
158+
} catch (Error $error) {
159+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
160+
}
161+
$expression = PhpTypeExpression::buildByVar($ast[0]->stmts[1]->stmts[9], ['hoge', 'fuga', 'product']);
162+
$types = $expression->getTypes();
163+
164+
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
165+
$this->assertSame('string', $types[0]->getName(), 'name');
166+
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
167+
$this->assertSame(['hoge', 'fuga', 'product', 'bar'], $types[1]->getNamespace(), 'namespace');
168+
$this->assertSame('Bon', $types[1]->getName(), 'name');
169+
$this->assertSame(false, $types[1]->getNullable(), 'nullable');
170+
}
171+
public function testMethodParameterInt(): void {
172+
// /** @params string|int $param1 */
173+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
174+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
175+
try {
176+
$ast = $parser->parse(file_get_contents($filename));
177+
} catch (Error $error) {
178+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
179+
}
180+
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
181+
$doc = $ast[0]->stmts[1]->stmts[10]->getDocComment();
182+
$docString = $doc->getText();
183+
$param = $ast[0]->stmts[1]->stmts[10]->getParams()[0];
184+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint');
185+
$types = $expression->getTypes();
186+
187+
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
188+
$this->assertSame('int', $types[0]->getName(), 'name');
189+
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
190+
}
191+
public function testMethodParameterPrice(): void {
192+
// /** @params string|int $param1 */
193+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
194+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
195+
try {
196+
$ast = $parser->parse(file_get_contents($filename));
197+
} catch (Error $error) {
198+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
199+
}
200+
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
201+
$doc = $ast[0]->stmts[1]->stmts[10]->getDocComment();
202+
$docString = $doc->getText();
203+
$param = $ast[0]->stmts[1]->stmts[10]->getParams()[1];
204+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'paramint');
205+
$types = $expression->getTypes();
206+
207+
$this->assertSame(['hoge', 'fuga', 'product'], $types[0]->getNamespace(), 'namespace');
208+
$this->assertSame('Price', $types[0]->getName(), 'name');
209+
$this->assertSame(true, $types[0]->getNullable(), 'nullable');
210+
}
151211
public function testMethodParameterDocString(): void {
152212
// /** @params string|int $param1 */
153213
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
@@ -158,7 +218,10 @@ public function testMethodParameterDocString(): void {
158218
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
159219
}
160220
// var_dump($ast[0]->stmts[1]->stmts[8]);die();
161-
$expression = PhpTypeExpression::buildByMethodParam($ast[0]->stmts[1]->stmts[9], ['hoge', 'fuga', 'product']);
221+
$doc = $ast[0]->stmts[1]->stmts[10]->getDocComment();
222+
$docString = $doc->getText();
223+
$param = $ast[0]->stmts[1]->stmts[10]->getParams()[2];
224+
$expression = PhpTypeExpression::buildByMethodParam($param, ['hoge', 'fuga', 'product'], $docString, 'param1');
162225
$types = $expression->getTypes();
163226

164227
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
@@ -168,5 +231,59 @@ public function testMethodParameterDocString(): void {
168231
$this->assertSame('int', $types[1]->getName(), 'name');
169232
$this->assertSame(false, $types[1]->getNullable(), 'nullable');
170233
}
234+
public function testMethodReturnInt(): void {
235+
// /** @params string|int $param1 */
236+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
237+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
238+
try {
239+
$ast = $parser->parse(file_get_contents($filename));
240+
} catch (Error $error) {
241+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
242+
}
243+
$method = $ast[0]->stmts[1]->stmts[10];
244+
// var_dump($method);die();
245+
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product']);
246+
$types = $expression->getTypes();
247+
248+
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
249+
$this->assertSame('int', $types[0]->getName(), 'name');
250+
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
251+
}
252+
public function testMethodReturnProduct(): void {
253+
// /** @params string|int $param1 */
254+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
255+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
256+
try {
257+
$ast = $parser->parse(file_get_contents($filename));
258+
} catch (Error $error) {
259+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
260+
}
261+
$method = $ast[0]->stmts[1]->stmts[11];
262+
// var_dump($method);die();
263+
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product']);
264+
$types = $expression->getTypes();
265+
266+
$this->assertSame(['hoge', 'fuga', 'product'], $types[0]->getNamespace(), 'namespace');
267+
$this->assertSame('Product', $types[0]->getName(), 'name');
268+
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
269+
}
270+
public function testMethodReturnArray(): void {
271+
// /** @params string|int $param1 */
272+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
273+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
274+
try {
275+
$ast = $parser->parse(file_get_contents($filename));
276+
} catch (Error $error) {
277+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
278+
}
279+
$method = $ast[0]->stmts[1]->stmts[12];
280+
// var_dump($method);die();
281+
$expression = PhpTypeExpression::buildByMethodReturn($method, ['hoge', 'fuga', 'product']);
282+
$types = $expression->getTypes();
283+
284+
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
285+
$this->assertSame('array', $types[0]->getName(), 'name');
286+
$this->assertSame(false, $types[0]->getNullable(), 'nullable');
287+
}
171288

172289
}

test/fixtures/php8/product/Product.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,26 @@ class Product {
1616
private \hoge\fuga\product\bar\Boo $boo2;
1717
/** @var bur\Bon $docString */
1818
private bar\Boo $docString;
19-
/** @var string|int $docString */
19+
/** @var string|int $docStringUnion */
2020
private $docStringUnion;
21+
/** @var string|bar\Bon $docStringUnion2 */
22+
private $docStringUnion2;
2123

2224
/**
2325
* @param string|int $param1
24-
* @return Product product
2526
*/
26-
public function method1(string $param1) {
27+
public function method1(int $paramInt, ?Price $price, string $param1): int {
28+
return 0;
29+
}
30+
31+
/**
32+
* @return Product product (優先される情報)
33+
*/
34+
public function method2(): int {
35+
return 0;
36+
}
37+
38+
public function method3(): array {
39+
return [];
2740
}
2841
}

0 commit comments

Comments
 (0)