Skip to content

Commit 33d18ab

Browse files
committed
fix: array expression array<SomeType> must was recognized correctly. #52
Until now, type declarations in the `Product[]` format in DocComment were reflected in dependency arrows, but type declarations in the `array<Product>` format were not output as dependency arrows.
1 parent 5263ad2 commit 33d18ab

File tree

10 files changed

+183
-6
lines changed

10 files changed

+183
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
### Bug fix
4+
5+
* fix array expression `array<SomeType>` must was recognized correctly. #52
6+
37
## v1.2.3 (2023-12-03)
48

59
### Features

dogfood.png

1.67 KB
Loading

src/DiagramElement/ArrowDependency.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,19 @@ final class ArrowDependency extends Arrow
1212

1313
public function toString(PhpClass $toClass): string
1414
{
15-
if (strpos($this->getTo()->getName(), '[]') === false) {
16-
return sprintf(' %s %s %s', $this->getFrom()->getClassNameAlias(), $this->figure, $toClass->getClassNameAlias());
15+
if (strpos($this->getTo()->getName(), '[]') !== false) {
16+
// ex. Product[]
17+
return $this->getExpression($toClass);
1718
}
19+
if (strpos($this->getTo()->getName(), 'array<') === 0) {
20+
// ex. array<Product> or array<int, Product>
21+
return $this->getExpression($toClass);
22+
}
23+
return sprintf(' %s %s %s', $this->getFrom()->getClassNameAlias(), $this->figure, $toClass->getClassNameAlias());
24+
}
25+
26+
private function getExpression(PhpClass $toClass): string
27+
{
1828
return sprintf(' %s "1" %s "*" %s', $this->getFrom()->getClassNameAlias(), $this->figure, str_replace('[]', '', $toClass->getClassNameAlias()));
1929
}
2030
}

src/DiagramElement/Relation.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function getRelations(): array
5858
}
5959
return null;
6060
}, $this->package->getArrows());
61+
6162
$relation_expressions = array_filter($relation_expressions);
6263
sort($relation_expressions);
6364
return array_values(array_unique($relation_expressions));

src/Php/PhpType.php

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,33 @@ public function getNullable(): bool
6868
return $this->nullable;
6969
}
7070

71+
/**
72+
* Determine whether they are the same class.
73+
*
74+
* Since it is used to determine dependency arrows,
75+
* array representations of the same class are also determined to be the same class.
76+
*/
7177
public function equals(PhpType $other): bool
7278
{
73-
if (str_replace('[]', '', $this->name) !== str_replace('[]', '', $other->name)) {
74-
return false;
75-
}
7679
if ($this->namespace !== $other->namespace) {
7780
return false;
7881
}
79-
return true;
82+
// ex. Product or Product[]
83+
if (str_replace('[]', '', $this->name) === str_replace('[]', '', $other->name)) {
84+
return true;
85+
}
86+
// ex. array<Product>
87+
if (preg_match('/array<([^,>]+)>/', $other->name, $matches)) {
88+
if ($this->name === $matches[1]) {
89+
return true;
90+
}
91+
}
92+
// ex. array<int, Product>
93+
if (preg_match('/array<[^>]+,\s*([^>]+)>/', $other->name, $matches)) {
94+
if ($this->name === $matches[1]) {
95+
return true;
96+
}
97+
}
98+
return false;
8099
}
81100
}

test/PhpDocCommentTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,42 @@ public function test_getReturnType(): void
153153

154154
$this->assertSame("int|null", $doc->getReturnTypeName(), 'return type name.');
155155
}
156+
public function test_getVarType_array_expression(): void
157+
{
158+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
159+
$filename = sprintf('%s/array-expression-in-doc/product/Product.php', $this->fixtureDir);
160+
try {
161+
$ast = $parser->parse(file_get_contents($filename));
162+
} catch (Error $error) {
163+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
164+
}
165+
$finder = new NodeFinder();
166+
$var = $finder->findFirst($ast, function(Node $node){
167+
return $node instanceof Property && $node->props[0]->name->toString() === 'tags';
168+
});
169+
170+
$doc = new PhpDocComment($var);
171+
172+
$this->assertSame("array<Tag>", $doc->getVarTypeName(), 'var type name.');
173+
}
174+
public function test_getReturnType_array_expression(): void
175+
{
176+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
177+
$filename = sprintf('%s/array-expression-in-doc/product/Product.php', $this->fixtureDir);
178+
try {
179+
$ast = $parser->parse(file_get_contents($filename));
180+
} catch (Error $error) {
181+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
182+
}
183+
$finder = new NodeFinder();
184+
$method = $finder->findFirst($ast, function(Node $node){
185+
return $node instanceof ClassMethod && $node->name->toString() === 'getTags';
186+
});
187+
188+
$doc = new PhpDocComment($method);
189+
190+
$this->assertSame("array<Tag>", $doc->getReturnTypeName(), 'return type name.');
191+
}
156192
public function test_getClassComment(): void
157193
{
158194
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);

test/PhpTypeTest.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Smeghead\PhpClassDiagram\Php\PhpType;
7+
8+
final class PhpTypeTest extends TestCase
9+
{
10+
public function testEquals_different_name(): void
11+
{
12+
$sut = new PhpType(['hoge'], '', 'Product');
13+
$other = new PhpType(['hoge'], '', 'ProductXXX');
14+
15+
$this->assertFalse($sut->equals($other));
16+
}
17+
public function testEquals_different_namespace(): void
18+
{
19+
$sut = new PhpType(['hoge'], '', 'Product');
20+
$other = new PhpType(['hoge', 'fuga'], '', 'Product');
21+
22+
$this->assertFalse($sut->equals($other));
23+
}
24+
public function testEquals_same_name(): void
25+
{
26+
$sut = new PhpType(['hoge'], '', 'Product');
27+
$other = new PhpType(['hoge'], '', 'Product');
28+
29+
$this->assertTrue($sut->equals($other));
30+
}
31+
public function testEquals_braces(): void
32+
{
33+
$sut = new PhpType(['hoge'], '', 'Product');
34+
$other = new PhpType(['hoge'], '', 'Product[]');
35+
36+
$this->assertTrue($sut->equals($other));
37+
}
38+
public function testEquals_array_expression(): void
39+
{
40+
$sut = new PhpType(['hoge'], '', 'Product');
41+
$other = new PhpType(['hoge'], '', 'array<Product>');
42+
43+
$this->assertTrue($sut->equals($other));
44+
}
45+
public function testEquals_array_expression_int_and_product(): void
46+
{
47+
$sut = new PhpType(['hoge'], '', 'Product');
48+
$other = new PhpType(['hoge'], '', 'array<int, Product>');
49+
50+
$this->assertTrue($sut->equals($other));
51+
}
52+
public function testEquals_array_expression_string_and_product(): void
53+
{
54+
$sut = new PhpType(['hoge'], '', 'Product');
55+
$other = new PhpType(['hoge'], '', 'array<string, Product>');
56+
57+
$this->assertTrue($sut->equals($other));
58+
}
59+
}

test/RelationTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,29 @@ public function testGetRelations2(): void
116116
$this->assertSame(' product_Product ..> product_Price', $relations[2], 'relation 3');
117117
}
118118

119+
public function testGetRelations_array_expression_in_doc(): void
120+
{
121+
$directory = sprintf('%s/array-expression-in-doc', $this->fixtureDir);
122+
$options = new Options([]);
123+
$files = [
124+
'product/Product.php',
125+
'product/Tag.php',
126+
];
127+
$entries = [];
128+
foreach ($files as $f) {
129+
$filename = sprintf('%s/%s', $directory, $f);
130+
$classes = PhpReader::parseFile($directory, $filename, $options);
131+
foreach ($classes as $c) {
132+
$entries = array_merge($entries, [new Entry(dirname($f), $c->getInfo(), $options)]);
133+
}
134+
}
135+
$rel = new Relation($entries, $options);
136+
$relations = $rel->getRelations();
137+
138+
$this->assertSame(1, count($relations), 'count');
139+
$this->assertSame(' product_Product "1" ..> "*" product_Tag', $relations[0], 'relation 1');
140+
}
141+
119142
public function testGetRelations_extends1(): void
120143
{
121144
$directory = sprintf('%s/namespace-tag', $this->fixtureDir);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
namespace hoge\fuga\product;
3+
4+
class Product {
5+
/** @var array<Tag> */
6+
private array $tags = [];
7+
8+
/**
9+
* @return array<Tag>
10+
*/
11+
public function getTags(): array
12+
{
13+
return $this->tags;
14+
}
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
namespace hoge\fuga\product;
3+
4+
class Tag {
5+
private string $name = '';
6+
7+
public function getName(): string {
8+
return $this->name;
9+
}
10+
}

0 commit comments

Comments
 (0)