Skip to content

Commit 49ca58d

Browse files
committed
add PhpTypeExpression to parse union type.
1 parent e2bcfd6 commit 49ca58d

File tree

9 files changed

+144
-12
lines changed

9 files changed

+144
-12
lines changed

src/Php/PhpClass.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
NullableType,
77
Identifier,
88
Name,
9+
UnionType,
910
};
1011
use PhpParser\Node\Name\FullyQualified;
1112
use PhpParser\Node\Stmt;
@@ -184,6 +185,8 @@ public function findTypeByTypeParts(NodeAbstract $stmt, string $property, string
184185
end($type->parts));
185186
} else if ($type instanceOf Name) {
186187
$parts = $type->parts;
188+
} else if ($type instanceOf UnionType) {
189+
187190
}
188191
$namespace = [];
189192
if (count($parts) > 0) {

src/Php/PhpType.php

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,14 @@ class PhpType {
66
private string $meta;
77
private array $namespace;
88
private string $alias;
9+
private bool $nullable;
910

10-
public function __construct(array $namespace, string $meta, string $name, $alias = null) {
11+
public function __construct(array $namespace, string $meta, string $name, $alias = null, bool $nullable = false) {
1112
$this->namespace = $namespace;
1213
$this->meta = $meta;
1314
$this->name = $name;
1415
$this->alias = is_object($alias) ? $alias->name : '';
15-
}
16-
17-
public function equals(PhpType $other): bool {
18-
if (str_replace('[]', '', $this->name) !== str_replace('[]', '', $other->name)) {
19-
return false;
20-
}
21-
if ($this->namespace !== $other->namespace) {
22-
return false;
23-
}
24-
return true;
16+
$this->nullable = $nullable;
2517
}
2618

2719
public function getName(): string {
@@ -33,7 +25,7 @@ public function getMeta(): string {
3325
}
3426

3527
/**
36-
* @return string[]
28+
* @return string[] namespace
3729
*/
3830
public function getNamespace(): array {
3931
return $this->namespace;
@@ -42,4 +34,18 @@ public function getNamespace(): array {
4234
public function getAlias(): string {
4335
return $this->alias;
4436
}
37+
38+
public function getNullable(): bool {
39+
return $this->nullable;
40+
}
41+
42+
public function equals(PhpType $other): bool {
43+
if (str_replace('[]', '', $this->name) !== str_replace('[]', '', $other->name)) {
44+
return false;
45+
}
46+
if ($this->namespace !== $other->namespace) {
47+
return false;
48+
}
49+
return true;
50+
}
4551
}

src/Php/PhpTypeExpression.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Smeghead\PhpClassDiagram\Php;
4+
5+
use PhpParser\Node\Identifier;
6+
use PhpParser\Node\NullableType;
7+
use PhpParser\NodeAbstract;
8+
9+
class PhpTypeExpression {
10+
public const TYPE = 'type';
11+
public const RETURN_TYPE = 'return_type';
12+
13+
/** @var PhpType[] */
14+
private array $types;
15+
16+
public function __construct(NodeAbstract $stmt, string $targetType) {
17+
$type = $stmt->{$targetType};
18+
$nullable = false;
19+
if ($type instanceOf NullableType) {
20+
$type = $type->type;
21+
$nullable = true;
22+
}
23+
$parts = [];
24+
if ($type instanceOf Identifier) {
25+
$parts[] = $type->name;
26+
}
27+
$namespace = [];
28+
$typeName = array_pop($parts);
29+
$this->types[] = new PhpType($namespace, $stmt->getType(), $typeName ?? '', null, $nullable);
30+
}
31+
/**
32+
* @return PhpType[] types
33+
*/
34+
public function getTypes(): array {
35+
return $this->types;
36+
}
37+
}

test/PhpReflectionTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ public function testDump_Price(): void {
5252
$this->assertSame([], $data->getProperties()[0]->getType()->getNamespace(), 'namespace.');
5353
$this->assertSame(true, $data->getProperties()[0]->getAccessModifier()->isPrivate(), 'property price Modifiers.');
5454
}
55+
// public function testDump_php8_Price(): void {
56+
// $options = new Options([]);
57+
// $directory = sprintf('%s/php8', $this->fixtureDir);
58+
// $filename = sprintf('%s/php8/product/Price.php', $this->fixtureDir);
59+
// $classes = PhpReader::parseFile($directory, $filename, $options);
60+
61+
// $data = $classes[0]->getInfo();
62+
// $this->assertSame('Price', $data->getClassType()->name, 'class type name.');
63+
// $this->assertSame(['hoge', 'fuga', 'product'], $data->getClassType()->getNamespace(), 'namespace name.');
64+
// $this->assertSame('price', $data->getProperties()[0]->name, 'property price.');
65+
// $this->assertSame('int|float', $data->getProperties()[0]->type->getName(), 'property price type. php8 union type.');
66+
// $this->assertSame(['hoge', 'fuga', 'product'], $data->getProperties()[0]->type->getNamespace(), 'namespace.');
67+
// $this->assertSame(true, $data->getProperties()[0]->accessModifier->private, 'property price Modifiers.');
68+
// }
5569

5670
public function testDump_with_namespace(): void {
5771
$options = new Options([]);

test/PhpTypeExpressionTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types=1);
2+
3+
use PhpParser\ParserFactory;
4+
use PHPUnit\Framework\TestCase;
5+
6+
use Smeghead\PhpClassDiagram\Php\PhpTypeExpression;
7+
8+
final class PhpTypeExpressionTest extends TestCase {
9+
private $fixtureDir;
10+
public function setUp(): void {
11+
$this->fixtureDir = sprintf('%s/fixtures', __DIR__);
12+
}
13+
14+
public function testNullableString(): void {
15+
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
16+
$filename = sprintf('%s/php8/product/Product.php', $this->fixtureDir);
17+
try {
18+
$ast = $parser->parse(file_get_contents($filename));
19+
} catch (Error $error) {
20+
throw new \Exception("Parse error: {$error->getMessage()} file: {$filename}\n");
21+
}
22+
23+
$expression = new PhpTypeExpression($ast[0]->stmts[1]->stmts[0], PhpTypeExpression::TYPE);
24+
$types = $expression->getTypes();
25+
26+
$this->assertSame([], $types[0]->getNamespace(), 'namespace');
27+
$this->assertSame('string', $types[0]->getName(), 'name');
28+
$this->assertSame(true, $types[0]->getNullable(), 'nullable');
29+
}
30+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
namespace hoge\fuga\product;
3+
4+
use external\sales;
5+
6+
class Exception extends \Exception {
7+
private \Exception $inner;
8+
9+
public function getInner(\Exception $e): \Exception {
10+
}
11+
public function external(): \external\Exception {
12+
}
13+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
namespace hoge\fuga\product;
3+
4+
class Name {
5+
private string $name;
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
namespace hoge\fuga\product;
3+
4+
class Price {
5+
private int|float $price8;
6+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
namespace hoge\fuga\product;
3+
4+
use hoge\fuga\product\ {
5+
Name,
6+
Price,
7+
};
8+
9+
class Product {
10+
private ?string $nullableString;
11+
private Name $name;
12+
private Price $price;
13+
14+
/** @return Product product */
15+
public function method1(string $param1) {
16+
}
17+
}

0 commit comments

Comments
 (0)