Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit b5ed066

Browse files
authored
Merge pull request #35 from tasuku43/issue/26
feat: trait modes, flatten optimization, dumper, CLI flag, docs, tests
2 parents 6c38a68 + 2a942a9 commit b5ed066

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1587
-251
lines changed

ARCHITECTURE.md

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,32 @@
1212
- Key classes:
1313
- `ClassDiagram`: Aggregates nodes and relationships; renders Mermaid text.
1414
- `ClassDiagramBuilder`: Facade to parse and assemble nodes/relationships.
15-
- `Node/*`: Type representations (`Class_`, `AbstractClass_`, `Interface_`, `Enum_`) and the common abstract `Node`, plus the `Nodes` collection.
16-
- `Node/Relationship/*`: Relationship representations (`Inheritance`, `Realization`, `Composition`, `Dependency`).
15+
- `Node/*`: Type representations (`Class_`, `AbstractClass_`, `Interface_`, `Enum_`, `Trait_`) with common abstract `Node`, and the `Nodes` collection.
16+
- `Node/Relationship/*`: Relationship representations (`Inheritance`, `Realization`, `Composition`, `Dependency`, `TraitUsage`) and the `Relationships` collection.
1717
- `Node/Connector/*`: Extract relationship information from the AST and connect `Node`s on `Nodes`.
1818
- `Node/NodeParser`: Uses Finder to collect files, PHP-Parser to build AST, and Connectors to extract relationships.
19+
- `TraitRenderMode`: Rendering strategy for traits (WithTraits or Flatten).
20+
- `ClassDiagramDumper`: Debug utility to dump YAML-like structure of nodes/relationships (via reflection).
1921
- `Console/Command/GenerateCommand.php`: Symfony Console command (`generate --path`).
2022
- `mermaid-class-diagram`: Executable binary that boots the Console app.
2123
- `tests/`: PHPUnit tests and fixtures (`tests/data/Project/*`).
2224

2325
## Domain Model
2426
### Nodes (types)
25-
- `Node` (abstract)
27+
- `Node` (abstract)
2628
- Minimal representation of a type with `$name`.
2729
- Holds source collections for relationships:
2830
- `$extends`, `$implements`, `$properties` (composition), `$depends` (dependency)
2931
- `relationships()`: Builds an array of `Relationship` objects from the above collections.
3032
- De-duplicates and filters: removes conflicts (e.g., dependency also present as composition/inheritance/realization) and self references.
3133
- `sortNodes(array &$nodes)`: Sorts by node name (ascending).
3234
- Concrete types
33-
- `Class_`, `AbstractClass_`, `Interface_`, `Enum_`
34-
- `render()`: Outputs Mermaid `class` syntax; stereotypes `<<abstract>>`, `<<interface>>`, `<<enum>>` when applicable.
35-
- `Nodes`
36-
- Dictionary-like collection keyed by node name.
37-
- Provides `add()`, `findByName()`, `getAllNodes()`.
35+
- `Class_`, `AbstractClass_`, `Interface_`, `Enum_`, `Trait_`
36+
- `render()`: Outputs Mermaid `class` syntax; stereotypes `<<abstract>>`, `<<interface>>`, `<<enum>>`, `<<trait>>` when applicable.
37+
- `Nodes`
38+
- Dictionary-like collection keyed by node name.
39+
- Provides `add()`, `findByName()`, `getAll()` and `sort()`.
40+
- `optimize(RenderOptions $options)`: view-time pruning (e.g., hide trait nodes in Flatten mode). Internally uses a private filter.
3841

3942
### Relationships
4043
- `Relationship` (abstract): Holds `from` and `to`; `render()` outputs Mermaid edges.
@@ -53,7 +56,7 @@
5356
- Input: `--path` may be a directory (glob `*.php`) or a single file (`Symfony\Component\Finder\Finder`).
5457
- Preparation: Build AST with `PhpParser`; run `NameResolver` and `ParentConnectingVisitor`.
5558
- Class-like detection:
56-
- Gather `Stmt\ClassLike` (Class/Interface/Enum) and also `Stmt\Trait_` nodes, but only Class/Interface/Enum become `Node`s.
59+
- Gather `Stmt\ClassLike` (Class/Interface/Enum) and also `Stmt\Trait_` nodes; Class, Interface, Enum, and Trait all become `Node`s.
5760
- If no valid class-like found in a file, skip via `CannnotParseToClassLikeException`.
5861
- Two-phase processing:
5962
- Phase 1: Create `Node` (`Class_`/`AbstractClass_`/`Interface_`/`Enum_`) for each valid class-like and add to `Nodes`.
@@ -72,10 +75,13 @@
7275
- Method return types of `Name` are dependencies.
7376
- `new` expressions (`Expr\New_`) add dependencies.
7477
- Filters out `self` and de-duplicates.
78+
- `TraitUsageConnector`
79+
- Records `use TraitName;` for class-like nodes (classes and traits).
80+
- Enables render-time merging/flattening of trait-derived associations into the using node.
7581

7682
## Rendering
7783
- `ClassDiagram::render()`
78-
- Sorts nodes by name; sorts relationships by `from to`.
84+
- Applies `Nodes::optimize($options)->sort()` and `Relationships::optimize($options)->sort()`.
7985
- Emits nodes first, then a blank line, then relationships.
8086
- Header line `classDiagram`; each subsequent line is indented by 4 spaces.
8187
- Examples
@@ -88,6 +94,30 @@
8894
- `includeCompositions`: show/hide `Composition` edges (properties/constructor promotion/FQCN properties)
8995
- `includeInheritances`: show/hide `Inheritance` edges (`extends`)
9096
- `includeRealizations`: show/hide `Realization` edges (`implements`)
97+
- `traitRenderMode` (`TraitRenderMode`): trait rendering policy (default Flatten).
98+
99+
#### Trait rendering modes
100+
- `WithTraits`:
101+
- Show trait nodes and `use` edges.
102+
- Keep trait-origin composition/dependency edges attached to the trait.
103+
- Suppress duplicate class-level composition/dependency when already provided by a used trait.
104+
- `Flatten`:
105+
- Hide trait nodes and `use` edges.
106+
- Reassign trait-origin composition/dependency edges to the using classes.
107+
- Transitive flattening: handles trait→trait→class chains (reassign to final class users), with deduplication.
108+
- Apply `include*` filters after optimization.
109+
110+
### Relationships optimization
111+
- `Relationships::optimize(RenderOptions $options)` performs view-time transformations before rendering:
112+
- Delegates to private implementations per mode:
113+
- `optimizeWithTraitsInternal()`: keep traits/`use`, suppress class-level duplicates for comp/dep.
114+
- `optimizeFlattenInternal()`: hide traits/`use`, reassign trait-origin edges to class users (including transitive chains), deduplicate.
115+
- Applies include flags via private `filterByOptions()`.
116+
117+
### Debug dumper
118+
- `ClassDiagramDumper::toYaml(RenderOptions $options = null): string`
119+
- Uses reflection to access the diagram’s internals and emits a YAML-like structure for debugging.
120+
- Mirrors `render()`’s optimize/sort policy so the dump matches what would be rendered under the same options.
91121

92122
## CLI
93123
- Entry: `mermaid-class-diagram`
@@ -104,7 +134,7 @@
104134
- `symfony/finder` (^7.0): File discovery.
105135
- `symfony/console` (^7.0): CLI framework.
106136
- Dev
107-
- `phpunit/phpunit` (^9.5): Unit tests.
137+
- `phpunit/phpunit`: Unit tests.
108138

109139
## Notes and Trade-offs
110140
- Type resolution simplifications
@@ -114,7 +144,9 @@
114144
- Types referenced but not defined in the scan scope are synthesized as minimal `Node`s so relationships still render.
115145
- This may produce nodes in the output with empty bodies.
116146
- Traits
117-
- Traits are detected in AST collection but not rendered as nodes; only Class/Interface/Enum are supported.
147+
- Participates fully in AST analysis and can be rendered as nodes depending on `TraitRenderMode`.
148+
- WithTraits: show trait nodes and `use` edges, suppress duplicate class-level comp/dep provided by traits.
149+
- Flatten: hide trait nodes and `use` edges; reassign trait-origin comp/dep to using classes, including transitive trait chains; deduplicate.
118150
- Safety
119151
- Never executes target PHP; analysis is static and AST-based.
120152
- CLI does not currently validate `--path` beyond being required.
@@ -125,6 +157,7 @@
125157
- Compare `ClassDiagram`/`ClassDiagramBuilder` output against precise expected strings.
126158
- Fixtures
127159
- `tests/data/Project/*` provides a small pseudo-app to cover common patterns.
160+
- `tests/data/TraitChain/*` covers trait→trait chains and flattening behavior.
128161

129162
## Extensibility
130163
- New relationships

README.md

Lines changed: 103 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,93 +10,59 @@ composer require --dev tasuku43/mermaid-class-diagram
1010
```
1111

1212
## Usage
13-
Here is an example run on a sample project
14-
```shell
15-
$ tree
16-
.
17-
├── composer.json
18-
├── composer.lock
19-
├── src
20-
│   ├── SomeAbstractClass.php
21-
│   ├── SomeClassA.php
22-
│   ├── SomeClassB.php
23-
│   ├── SomeClassC.php
24-
│   ├── SomeClassD.php
25-
│   ├── SomeClassE.php
26-
│   └── SomeInterface.php
27-
└── vendor
28-
```
29-
```php
30-
class SomeClassA extends SomeAbstractClass
31-
{
32-
private SomeClassB $someClassB;
33-
34-
public function __construct(private SomeClassC $someClassC, SomeClassD $someClassD, private int $int)
35-
{
36-
}
37-
}
38-
39-
class SomeClassB
40-
{
41-
}
42-
43-
class SomeClassC
44-
{
45-
}
46-
47-
class SomeClassD
48-
{
49-
}
50-
51-
class SomeClassE
52-
{
53-
public function __construct(private SomeClassA $a)
54-
{
55-
$b = new SomeClassB;
56-
}
57-
58-
public function dependAandC(SomeClassA $a): SomeClassC
59-
{
60-
}
61-
}
6213

63-
abstract class SomeAbstractClass implements SomeInterface
64-
{
65-
}
66-
67-
interface SomeInterface
68-
{
69-
}
70-
```
71-
### Execute command by specifying a directory
14+
### Command
15+
- `vendor/bin/mermaid-class-diagram generate --path <path> [options]`
16+
17+
### Options
18+
- `--path <string>`: Required. Directory (recursively scanned) or single PHP file.
19+
- `--exclude-relationships <csv>`: Hide edge types; case-insensitive; aliases supported.
20+
- `dependency|dependencies|dep|deps`
21+
- `composition|compositions|comp`
22+
- `inheritance|inheritances|extends`
23+
- `realization|realizations|implements`
24+
- `--trait-mode <mode>`: Trait rendering mode. Default: `flatten`.
25+
- `with-traits` (aliases: `with_traits`, `with`): show trait nodes and `use` edges; suppress class-level comp/dep duplicates provided by traits.
26+
- `flatten` (aliases: `flat`): hide trait nodes and `use` edges; reassign trait-origin comp/dep to using classes; supports trait→trait→class chains; deduplicates.
27+
28+
### Examples
29+
#### Execute command by specifying a directory (sample project)
7230
```shell
73-
$ vendor/bin/mermaid-class-diagram generate --path src
31+
$ vendor/bin/mermaid-class-diagram generate --path tests/data/Project
7432
classDiagram
75-
class SomeAbstractClass {
33+
class AbstractController {
7634
<<abstract>>
7735
}
78-
class SomeClassA {
36+
class AuditLogger {
7937
}
80-
class SomeClassB {
38+
class AuditTarget {
8139
}
82-
class SomeClassC {
40+
class User {
8341
}
84-
class SomeClassD {
42+
class UserController {
8543
}
86-
class SomeClassE {
44+
class UserRepository {
8745
}
88-
class SomeInterface {
46+
class UserRepositoryInterface {
8947
<<interface>>
9048
}
49+
class UserService {
50+
}
51+
class UserStatus {
52+
<<enum>>
53+
}
9154
92-
SomeInterface <|.. SomeAbstractClass: realization
93-
SomeAbstractClass <|-- SomeClassA: inheritance
94-
SomeClassA *-- SomeClassB: composition
95-
SomeClassA *-- SomeClassC: composition
96-
SomeClassA ..> SomeClassD: dependency
97-
SomeClassE *-- SomeClassA: composition
98-
SomeClassE ..> SomeClassB: dependency
99-
SomeClassE ..> SomeClassC: dependency
55+
User *-- UserStatus: composition
56+
AbstractController <|-- UserController: inheritance
57+
UserController *-- UserService: composition
58+
UserRepository ..> User: dependency
59+
UserRepositoryInterface <|.. UserRepository: realization
60+
UserRepositoryInterface ..> User: dependency
61+
UserService *-- AuditLogger: composition
62+
UserService ..> AuditTarget: dependency
63+
UserService ..> InvalidArgumentException: dependency
64+
UserService ..> User: dependency
65+
UserService *-- UserRepositoryInterface: composition
10066
```
10167
```mermaid
10268
classDiagram
@@ -126,17 +92,15 @@ classDiagram
12692
SomeClassE ..> SomeClassB: dependency
12793
SomeClassE ..> SomeClassC: dependency
12894
```
129-
### Execute command by specifying a file
95+
#### Execute command by specifying a file (sample file)
13096
```shell
131-
$ vendor/bin/mermaid-class-diagram generate --path src/SomeClassA.php
97+
$ vendor/bin/mermaid-class-diagram generate --path tests/data/Project/Controller/UserController.php
13298
classDiagram
133-
class SomeClassA {
99+
class UserController {
134100
}
135101
136-
SomeAbstractClass <|-- SomeClassA: inheritance
137-
SomeClassA *-- SomeClassB: composition
138-
SomeClassA *-- SomeClassC: composition
139-
SomeClassD <.. SomeClassA: dependency
102+
AbstractController <|-- UserController: inheritance
103+
UserController *-- UserService: composition
140104
```
141105
```mermaid
142106
classDiagram
@@ -149,7 +113,7 @@ classDiagram
149113
SomeClassD <.. SomeClassA: dependency
150114
```
151115
152-
### Filter relationships
116+
#### Filter relationships
153117
You can hide specific relationship types via CSV with `--exclude-relationships`.
154118
155119
- Allowed values (case-insensitive, aliases supported):
@@ -167,5 +131,62 @@ $ vendor/bin/mermaid-class-diagram generate --path src --exclude-relationships d
167131
$ vendor/bin/mermaid-class-diagram generate --path src --exclude-relationships dependency
168132
```
169133
134+
#### Traits
135+
There are two render modes for traits (the CLI uses Flatten by default):
136+
137+
- Flatten (default)
138+
- Hides trait nodes and `use` edges.
139+
- Reassigns trait-origin composition/dependency edges to the using classes.
140+
- Supports transitive trait chains (TraitA uses TraitB); edges are reassigned to the final class users.
141+
- WithTraits
142+
- Shows trait nodes and `use` edges.
143+
- Keeps trait-origin composition/dependency edges on the trait.
144+
- Suppresses duplicate class-level composition/dependency when already provided by a used trait.
145+
146+
Example
147+
```php
148+
trait HasUserDeps {
149+
private UserRepository $repo;
150+
public function findById(UserId $id): ?User {}
151+
}
152+
153+
class UserController {
154+
use HasUserDeps;
155+
}
156+
```
157+
Output examples (order simplified):
158+
159+
Flatten (default)
160+
```mermaid
161+
classDiagram
162+
class UserController {
163+
}
164+
class UserRepository {
165+
}
166+
class UserId {
167+
}
168+
169+
UserController *-- UserRepository: composition
170+
UserController ..> UserId: dependency
171+
```
172+
173+
WithTraits
174+
```mermaid
175+
classDiagram
176+
class UserController {
177+
}
178+
class HasUserDeps {
179+
<<trait>>
180+
}
181+
class UserRepository {
182+
}
183+
class UserId {
184+
}
185+
186+
UserController --> HasUserDeps: use
187+
HasUserDeps *-- UserRepository: composition
188+
HasUserDeps ..> UserId: dependency
189+
```
190+
170191
## License
171192
The MIT License (MIT). Please see [LICENSE](https://github.com/tasuku43/php-mermaid-class-diagram/blob/main/LICENSE) for more information.

0 commit comments

Comments
 (0)