4
4
5
5
namespace Kauffinger \Codemap ;
6
6
7
+ use Kauffinger \Codemap \Dto \CodemapClassDto ;
8
+ use Kauffinger \Codemap \Dto \CodemapFileDto ;
9
+ use Kauffinger \Codemap \Dto \CodemapMethodDto ;
10
+ use Kauffinger \Codemap \Dto \CodemapPropertyDto ;
7
11
use PhpParser \Error ;
8
12
use PhpParser \Node ;
9
13
use PhpParser \Node \Stmt \Class_ ;
16
20
use PhpParser \PhpVersion ;
17
21
use RecursiveDirectoryIterator ;
18
22
use RecursiveIteratorIterator ;
23
+ use SplFileInfo ;
19
24
20
25
final class CodemapGenerator
21
26
{
22
27
/**
23
28
* Scans the provided path (directory or single file).
24
- * If it's a directory, it scans recursively for PHP files.
25
- * If it's a single file, it parses just that file.
29
+ * If it's a directory, scans recursively for PHP files.
30
+ * If it's a single file, parses just that file.
26
31
*
27
- * @param string $path Directory or file to parse.
28
- * @return array Parsed structure of classes, methods, and properties.
32
+ * @return array<string, CodemapFileDto> A map of fileName => codemap DTO
29
33
*/
30
34
public function generate (string $ path ): array
31
35
{
@@ -46,6 +50,7 @@ public function generate(string $path): array
46
50
$ results = [];
47
51
$ iterator = new RecursiveIteratorIterator (new RecursiveDirectoryIterator ($ path ));
48
52
53
+ /** @var SplFileInfo $file */
49
54
foreach ($ iterator as $ file ) {
50
55
if ($ file ->isDir ()) {
51
56
continue ;
@@ -55,17 +60,19 @@ public function generate(string $path): array
55
60
}
56
61
57
62
$ parsed = $ this ->processFile ($ file ->getRealPath ());
58
- $ results = array_merge ($ results , $ parsed );
63
+ // Merge results by file name
64
+ foreach ($ parsed as $ fileName => $ dto ) {
65
+ $ results [$ fileName ] = $ dto ;
66
+ }
59
67
}
60
68
61
69
return $ results ;
62
70
}
63
71
64
72
/**
65
- * Processes a single PHP file, returning codemap data.
73
+ * Processes a single PHP file, returning codemap data in a DTO .
66
74
*
67
- * @param string $filePath The file to parse.
68
- * @return array The codemap results for this file.
75
+ * @return array<string, CodemapFileDto> The codemap results for this file
69
76
*/
70
77
private function processFile (string $ filePath ): array
71
78
{
@@ -75,6 +82,9 @@ private function processFile(string $filePath): array
75
82
76
83
$ parser = (new ParserFactory )->createForVersion (PhpVersion::getHostVersion ());
77
84
$ code = file_get_contents ($ filePath );
85
+ if ($ code === false ) {
86
+ return [];
87
+ }
78
88
79
89
try {
80
90
$ ast = $ parser ->parse ($ code );
@@ -85,20 +95,16 @@ private function processFile(string $filePath): array
85
95
}
86
96
87
97
$ fileName = basename ($ filePath );
88
- $ results = [
89
- $ fileName => ['classes ' => []],
90
- ];
91
98
92
- // Create a NodeVisitor to track classes, methods, and properties
93
99
$ visitor = new class extends NodeVisitorAbstract
94
100
{
101
+ /* @phpstan-ignore-next-line */
95
102
public array $ classes = [];
96
103
97
104
private ?string $ currentClassName = null ;
98
105
99
106
public function enterNode (Node $ node )
100
107
{
101
- // Capture class names
102
108
if ($ node instanceof Class_) {
103
109
$ this ->currentClassName = $ node ->namespacedName
104
110
? $ node ->namespacedName ->toString ()
@@ -108,9 +114,7 @@ public function enterNode(Node $node)
108
114
'methods ' => [],
109
115
'properties ' => [],
110
116
];
111
- }
112
- // Capture methods
113
- elseif ($ node instanceof ClassMethod && $ this ->currentClassName !== null ) {
117
+ } elseif ($ node instanceof ClassMethod && $ this ->currentClassName !== null ) {
114
118
$ visibility = $ node ->isPublic ()
115
119
? 'public '
116
120
: ($ node ->isProtected () ? 'protected ' : 'private ' );
@@ -129,9 +133,7 @@ public function enterNode(Node $node)
129
133
'name ' => $ node ->name ->toString (),
130
134
'returnType ' => $ returnType ,
131
135
];
132
- }
133
- // Capture properties
134
- elseif ($ node instanceof Property && $ this ->currentClassName !== null ) {
136
+ } elseif ($ node instanceof Property && $ this ->currentClassName !== null ) {
135
137
$ visibility = $ node ->isPublic ()
136
138
? 'public '
137
139
: ($ node ->isProtected () ? 'protected ' : 'private ' );
@@ -143,18 +145,41 @@ public function enterNode(Node $node)
143
145
];
144
146
}
145
147
}
148
+
149
+ return null ;
146
150
}
147
151
};
148
152
149
- // Traverse the AST
150
153
$ traverser = new NodeTraverser ;
151
154
$ traverser ->addVisitor (new NameResolver );
152
155
$ traverser ->addVisitor ($ visitor );
153
- $ traverser ->traverse ($ ast );
156
+ $ traverser ->traverse ((array ) $ ast );
157
+
158
+ // Convert arrays to typed DTOs
159
+ $ classes = [];
160
+ foreach ($ visitor ->classes as $ className => $ classData ) {
161
+ $ methods = [];
162
+ foreach ($ classData ['methods ' ] as $ methodData ) {
163
+ $ methods [] = new CodemapMethodDto (
164
+ $ methodData ['visibility ' ],
165
+ $ methodData ['name ' ],
166
+ $ methodData ['returnType ' ]
167
+ );
168
+ }
154
169
155
- // Store the gathered class info for this file
156
- $ results [$ fileName ]['classes ' ] = $ visitor ->classes ;
170
+ $ properties = [];
171
+ foreach ($ classData ['properties ' ] as $ propData ) {
172
+ $ properties [] = new CodemapPropertyDto (
173
+ $ propData ['visibility ' ],
174
+ $ propData ['name ' ]
175
+ );
176
+ }
157
177
158
- return $ results ;
178
+ $ classes [$ className ] = new CodemapClassDto ($ methods , $ properties );
179
+ }
180
+
181
+ $ dto = new CodemapFileDto ($ classes );
182
+
183
+ return [$ fileName => $ dto ];
159
184
}
160
185
}
0 commit comments