Skip to content

Commit 073e0b7

Browse files
authored
Merge pull request #35 from smeghead/external-package-hierarchy
External package hierarchy
2 parents 06ded8a + fb99783 commit 073e0b7

File tree

14 files changed

+215
-37
lines changed

14 files changed

+215
-37
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/vendor/
22
*.phar
33
*.un~
4-
composer.lock
4+
composer.lock
5+
.vscode

CHANGELOG.md

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

3+
### Features
4+
5+
* Fixed so that the external package of the package relationship diagram is displayed hierarchically.
6+
37
### Bug fix
48

59
* Method's Parameter Dependencies added to arrows.

dogfood-model.png

-6.43 KB
Loading

dogfood-package.png

12.9 KB
Loading

dogfood.png

4.03 KB
Loading
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php declare(strict_types=1);
2+
namespace Smeghead\PhpClassDiagram\DiagramElement\ExternalPackage;
3+
4+
class PackageHierarchy {
5+
/**
6+
* @var string[] external packages.
7+
*/
8+
private array $externalPackages;
9+
10+
private PackageNode $root;
11+
12+
/**
13+
* @param string[] $externalPackages external package list
14+
*/
15+
public function __construct(array $externalPackages) {
16+
$this->externalPackages = $externalPackages;
17+
$this->root = new PackageNode('root');
18+
foreach ($externalPackages as $p) {
19+
$this->root->register(explode('.', $p));
20+
}
21+
}
22+
23+
public function dump(): string {
24+
$elements = [];
25+
foreach ($this->root->getChildren() as $c) {
26+
$elements[] = $c->dump(1);
27+
}
28+
return implode("\n", $elements);
29+
}
30+
}
31+
32+
class PackageNode {
33+
private string $name;
34+
/** @var PackageNode[] children */
35+
private array $children = [];
36+
37+
public function __construct(string $name) {
38+
$this->name = $name;
39+
}
40+
41+
/**
42+
* @return PackageNode[]
43+
*/
44+
public function getChildren(): array {
45+
return $this->children;
46+
}
47+
48+
/**
49+
* @param string[] $packages
50+
*/
51+
public function register(array $packages): void {
52+
if (count($packages) === 0) {
53+
return;
54+
}
55+
$next = array_shift($packages);
56+
foreach ($this->children as $c) {
57+
if ($c->name === $next) {
58+
$c->register($packages);
59+
return;
60+
}
61+
}
62+
$new = new PackageNode($next);
63+
$new->register($packages);
64+
$this->children[] = $new;
65+
}
66+
67+
public function dump(int $indent): string {
68+
$lines = [];
69+
$lines[] = sprintf('%spackage %s as %s #DDDDDD {', str_repeat(' ', $indent), $this->name, $this->name);
70+
foreach ($this->children as $c) {
71+
$lines[] = $c->dump($indent + 1);
72+
}
73+
$lines[] = sprintf('%s}', str_repeat(' ', $indent));
74+
return implode("\n", $lines);
75+
}
76+
}

src/DiagramElement/Package.php

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
class Package {
77
private Options $options;
88

9+
/** @var string[] */
910
public array $parents;
1011
public string $name;
1112
public string $package = '';
@@ -28,7 +29,7 @@ public function getLogicalName(): string {
2829
public function addEntry(array $paths, Entry $entry): string {
2930
if (count($paths) === 0) {
3031
if (empty($this->package)) {
31-
$this->package = implode('.', $entry->getClass()->getClassType()->getNamespace());
32+
$this->package = implode('.', $entry->getClass()->getNamespace());
3233
}
3334
$this->entries[] = $entry;
3435
return $this->package;
@@ -85,12 +86,22 @@ public function dump($level = 0): array {
8586
public function dumpPackages($level = 1): array {
8687
$indent = str_repeat(' ', $level);
8788
$lines = [];
88-
$lines[] = sprintf(
89-
'%spackage %s as %s {',
90-
$indent,
91-
$this->name === 'ROOT' ? (empty($this->package) ? 'ROOT': $this->package) : $this->name,
92-
$this->getLogicalName()
93-
);
89+
$target = empty($this->package) ? $this->getLogicalName() : $this->package;
90+
$targetElements = explode('.', $target);
91+
if ($level > 1) {
92+
$lines[] = sprintf(
93+
'%spackage %s {',
94+
$indent,
95+
end($targetElements)
96+
);
97+
} else {
98+
$lines[] = sprintf(
99+
'%spackage %s as %s {',
100+
$indent,
101+
$target,
102+
end($targetElements)
103+
);
104+
}
94105
foreach ($this->children as $n) {
95106
$lines = array_merge($lines, $n->dumpPackages($level + 1));
96107
}
@@ -131,7 +142,8 @@ public function getUses($acc): array {
131142
foreach ($this->entries as $e) {
132143
$uses = array_merge($uses, $e->getClass()->getUses());
133144
}
134-
$acc[$this->package] = $uses;
145+
$package = empty($this->package) ? sprintf('%s.%s', implode('.', $this->parents), $this->name) : $this->package;
146+
$acc[$package] = $uses;
135147
foreach ($this->children as $n) {
136148
$acc = array_merge($acc, $n->getUses($acc));
137149
}
@@ -148,4 +160,31 @@ public function getTargetPackages($acc = []) {
148160
}
149161
return $acc;
150162
}
163+
164+
public function findPackage(string $package): ?Package {
165+
return $this->recFindPackage($package);
166+
}
167+
168+
public function is(string $package): bool {
169+
$segments = $this->parents;
170+
$segments[] = $this->name;
171+
return implode('.', $segments) === $package;
172+
}
173+
174+
/**
175+
* @param string $package パッケージの表記(例: hoge.fuga)
176+
* @return ?Package
177+
*/
178+
private function recFindPackage(string $package): ?Package {
179+
if ($this->is($package)) {
180+
return $this;
181+
}
182+
foreach ($this->children as $c) {
183+
$p = $c->recFindPackage($package);
184+
if ( ! empty($p)) {
185+
return $p;
186+
}
187+
}
188+
return null;
189+
}
151190
}

src/DiagramElement/PackageArrow.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public function toString(): string {
2929
$format = $this->bothSideArrow
3030
? ' %s <-[#red,plain,thickness=4]-> %s'
3131
: ' %s --> %s';
32-
return sprintf($format, $this->from, $this->to);
32+
$fromElements = explode('.', $this->from);
33+
$toElements = explode('.', $this->to);
34+
return sprintf($format, end($fromElements), end($toElements));
3335
}
3436
}

src/DiagramElement/PackageRelations.php

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,58 @@
11
<?php declare(strict_types=1);
22
namespace Smeghead\PhpClassDiagram\DiagramElement;
33

4+
use Smeghead\PhpClassDiagram\DiagramElement\ExternalPackage\PackageHierarchy;
5+
use Smeghead\PhpClassDiagram\Php\PhpType;
6+
47
class PackageRelations {
5-
/** @var \Smeghead\PhpClassDiagram\Php\PhpType[] */
8+
/** @var array<string, \Smeghead\PhpClassDiagram\Php\PhpType[]> */
69
private array $uses;
7-
/** @var \Smeghead\PhpClassDiagram\Php\PhpType[] */
8-
private array $targetPackages;
10+
private Package $rootPackage;
911

10-
public function __construct(array $uses, array $targetPackages) {
12+
/**
13+
* @param array<string, \Smeghead\PhpClassDiagram\Php\PhpType[]> $uses
14+
* @param Package $rootPackage
15+
*/
16+
public function __construct(array $uses, Package $rootPackage) {
1117
$this->uses = $uses;
12-
$this->targetPackages = $targetPackages;
18+
$this->rootPackage = $rootPackage;
1319
}
1420

15-
private function displayPackage($package) {
16-
if (in_array($package, array_keys($this->targetPackages))) {
17-
return $this->targetPackages[$package]; // 解析対象のpackageはディレクトリ名で表示
18-
} else {
19-
return $package; //外部のpackageはpackage表示
21+
private function displayPackage(string $package): string {
22+
$p = $this->rootPackage->findPackage($package);
23+
if ( ! empty($p)) {
24+
if (empty($p->package)) {
25+
return $p->getLogicalName();
26+
}
2027
}
28+
return $package; //外部のpackageはpackage表示
2129
}
2230

2331
public function getArrows(): array {
2432
$lines = [];
2533
$all = [];
2634
$packageRelations = [];
2735
foreach ($this->uses as $namespace => $us) {
28-
$packages = array_unique(array_map(function($x){
36+
$packageNames = array_unique(array_map(function(PhpType $x){
2937
return implode('.', $x->getNamespace());
3038
}, $us));
3139
// 対象となっているpackage以外のpackageは、即席で定義する必要がある。
32-
$all = array_unique(array_merge($all, $packages));
33-
$packageRelations[$namespace] = array_map(function($x) {
34-
return $this->displayPackage($x, $this->targetPackages);
35-
}, $packages);
40+
$all = array_unique(array_merge($all, $packageNames));
41+
$packageRelations[$namespace] = array_map(function(string $x) {
42+
return $this->displayPackage($x);
43+
}, $packageNames);
3644
}
37-
foreach (array_diff($all, array_keys($this->targetPackages)) as $external) {
38-
$lines[] = sprintf(' package %s', $external);
45+
$externals = array_diff($all, array_keys($this->rootPackage->getTargetPackages()));
46+
if (count($externals) > 0) {
47+
$externalPackage = new PackageHierarchy($externals);
48+
$lines[] = $externalPackage->dump();
3949
}
4050
$arrows = [];
4151
foreach ($packageRelations as $package => $dependencies) {
42-
$package = $this->displayPackage($package, $this->targetPackages);
52+
$package = $this->displayPackage($package);
53+
if (empty($package)) {
54+
continue;
55+
}
4356
foreach ($dependencies as $d) {
4457
if (empty($d)) {
4558
continue;

src/DiagramElement/Relation.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ public function dumpPackages(): array {
5252
$lines = array_merge($lines, $this->options->headers());
5353
$lines = array_merge($lines, $this->package->dumpPackages());
5454
$uses = $this->getUses();
55-
$targetPackages = $this->package->getTargetPackages();
56-
$packageRelations = new PackageRelations($uses, $targetPackages);
55+
$packageRelations = new PackageRelations($uses, $this->package);
5756
$lines = array_merge($lines, $packageRelations->getArrows());
5857
$lines[] = '@enduml';
5958

0 commit comments

Comments
 (0)