Skip to content

Commit f01eba1

Browse files
committed
feat: configurability
1 parent 5c76254 commit f01eba1

File tree

8 files changed

+180
-28
lines changed

8 files changed

+180
-28
lines changed

codemap.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@
99
->withScanPaths([
1010
__DIR__.'/src',
1111
])
12+
->withExcludePaths([
13+
'vendor', // Example: exclude vendor directory
14+
// 'tests/Fixtures', // Example: exclude test fixtures
15+
])
1216
->withPhpVersion(PhpVersion::PHP_8_3);

codemap.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ File: Config/CodemapConfig.php
3434
public function withPhpVersion(Kauffinger\Codemap\Enum\PhpVersion $phpVersion): self
3535
public function getScanPaths(): array
3636
public function getConfiguredPhpVersion(): ?Kauffinger\Codemap\Enum\PhpVersion
37+
public function withPropertyVisibility(array $levels): self
38+
public function withMethodVisibility(array $levels): self
39+
public function withExcludePaths(array $paths): self
40+
public function getPropertyVisibilityLevels(): array
41+
public function getMethodVisibilityLevels(): array
42+
public function getExcludePaths(): array
3743

3844
File: Enum/PhpVersion.php
3945
Enum: Kauffinger\Codemap\Enum\PhpVersion: string
@@ -70,6 +76,7 @@ File: Generator/CodemapGenerator.php
7076

7177
File: Formatter/TextCodemapFormatter.php
7278
Class: Kauffinger\Codemap\Formatter\TextCodemapFormatter
79+
public function __construct(array $propertyVisibilityLevels, array $methodVisibilityLevels): mixed
7380
public function format(array $codemapData): string
7481
private function formatMethod(Kauffinger\Codemap\Dto\CodemapMethodDto $methodInformation): string
7582
private function formatParameters(array $parameters): string
@@ -85,7 +92,7 @@ File: Console/CodemapCommand.php
8592
private function ensureConfigurationExists(): void
8693
private function loadConfiguration(): Kauffinger\Codemap\Config\CodemapConfig
8794
private function getPhpVersion(Kauffinger\Codemap\Config\CodemapConfig $config): ?Kauffinger\Codemap\Enum\PhpVersion
88-
private function generateCodemap(array $scanPaths, ?Kauffinger\Codemap\Enum\PhpVersion $phpVersion): array
95+
private function generateCodemap(array $scanPaths, ?Kauffinger\Codemap\Enum\PhpVersion $phpVersion, Kauffinger\Codemap\Config\CodemapConfig $config): array
8996
private function writeOutput(string $output, string $outputFile): void
9097
private function generateDefaultConfig(Kauffinger\Codemap\Enum\PhpVersion $mappedPhpVersion): string
9198
protected function info(string $message): void

src/Config/CodemapConfig.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ final class CodemapConfig
1515

1616
private ?PhpVersion $configuredPhpVersion = null;
1717

18+
/**
19+
* @var string[] Default visibility levels for properties
20+
*/
21+
private array $propertyVisibilityLevels = ['public'];
22+
23+
/**
24+
* @var string[] Default visibility levels for methods (all)
25+
*/
26+
private array $methodVisibilityLevels = ['public', 'protected', 'private'];
27+
28+
/**
29+
* @var string[] Paths or patterns to exclude from scanning
30+
*/
31+
private array $excludePaths = [];
32+
1833
private function __construct()
1934
{
2035
// Builder pattern
@@ -67,4 +82,74 @@ public function getConfiguredPhpVersion(): ?PhpVersion
6782
{
6883
return $this->configuredPhpVersion;
6984
}
85+
86+
/**
87+
* Sets the visibility levels for properties to include in the output.
88+
* Default: ['public']
89+
*
90+
* @param string[] $levels e.g., ['public', 'protected']
91+
*/
92+
public function withPropertyVisibility(array $levels): self
93+
{
94+
// Basic validation could be added (e.g., ensure only 'public', 'protected', 'private' are used)
95+
$this->propertyVisibilityLevels = $levels;
96+
97+
return $this;
98+
}
99+
100+
/**
101+
* Sets the visibility levels for methods to include in the output.
102+
* Default: ['public', 'protected', 'private']
103+
*
104+
* @param string[] $levels e.g., ['public']
105+
*/
106+
public function withMethodVisibility(array $levels): self
107+
{
108+
// Basic validation could be added
109+
$this->methodVisibilityLevels = $levels;
110+
111+
return $this;
112+
}
113+
114+
/**
115+
* Sets the paths or patterns to exclude from the scan.
116+
*
117+
* @param string[] $paths Array of paths or glob patterns to exclude
118+
*/
119+
public function withExcludePaths(array $paths): self
120+
{
121+
$this->excludePaths = $paths;
122+
123+
return $this;
124+
}
125+
126+
/**
127+
* Returns the configured property visibility levels.
128+
*
129+
* @return string[]
130+
*/
131+
public function getPropertyVisibilityLevels(): array
132+
{
133+
return $this->propertyVisibilityLevels;
134+
}
135+
136+
/**
137+
* Returns the configured method visibility levels.
138+
*
139+
* @return string[]
140+
*/
141+
public function getMethodVisibilityLevels(): array
142+
{
143+
return $this->methodVisibilityLevels;
144+
}
145+
146+
/**
147+
* Returns the configured exclusion paths/patterns.
148+
*
149+
* @return string[]
150+
*/
151+
public function getExcludePaths(): array
152+
{
153+
return $this->excludePaths;
154+
}
70155
}

src/Console/CodemapCommand.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ protected function configure(): void
3333
->setDescription('Generate a codemap of PHP code')
3434
->addArgument('paths', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Paths to scan', [])
3535
->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'Output file path (use "-" for stdout)', 'codemap.txt')
36-
->addOption('php-version', null, InputOption::VALUE_REQUIRED, 'PHP version to use for parsing (e.g., "8.3")');
36+
->addOption('php-version', null, InputOption::VALUE_REQUIRED, 'PHP version to use for parsing (e.g., "8.3")')
37+
->addOption('visibility', null, InputOption::VALUE_REQUIRED, 'Comma-separated property visibility levels (e.g., "public,protected")');
3738
}
3839

3940
#[Override]
@@ -61,9 +62,19 @@ protected function handle(): int
6162
$phpVersion = $this->getPhpVersion($config);
6263

6364
/* @phpstan-ignore-next-line */
64-
$codemapResults = $this->generateCodemap($scanPaths, $phpVersion);
65+
$codemapResults = $this->generateCodemap($scanPaths, $phpVersion, $config);
6566

66-
$formatter = new TextCodemapFormatter;
67+
// Determine visibility levels
68+
$propertyVisibilityLevels = $config->getPropertyVisibilityLevels();
69+
$methodVisibilityLevels = $config->getMethodVisibilityLevels(); // Keep default for now
70+
71+
$visibilityOption = $this->option('visibility');
72+
if (is_string($visibilityOption) && $visibilityOption !== '') {
73+
$propertyVisibilityLevels = array_map('trim', explode(',', strtolower($visibilityOption)));
74+
// TODO: Add validation for allowed values ('public', 'protected', 'private')
75+
}
76+
77+
$formatter = new TextCodemapFormatter($propertyVisibilityLevels, $methodVisibilityLevels);
6778
$formattedOutput = $formatter->format($codemapResults);
6879

6980
$outputFile = $this->option('output');
@@ -158,9 +169,10 @@ private function getPhpVersion(CodemapConfig $config): ?PhpVersion
158169
* @param string[] $scanPaths
159170
* @return array<string, CodemapFileDto>
160171
*/
161-
private function generateCodemap(array $scanPaths, ?PhpVersion $phpVersion): array
172+
private function generateCodemap(array $scanPaths, ?PhpVersion $phpVersion, CodemapConfig $config): array
162173
{
163-
$codemapGenerator = new CodemapGenerator;
174+
// Pass config to generator for exclusion paths
175+
$codemapGenerator = new CodemapGenerator($config);
164176
if ($phpVersion instanceof PhpVersion) {
165177
$codemapGenerator->setPhpParserVersion($phpVersion->toParserPhpVersion());
166178
}

src/Formatter/TextCodemapFormatter.php

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@
99
use Kauffinger\Codemap\Dto\CodemapParameterDto;
1010
use Kauffinger\Codemap\Dto\CodemapPropertyDto;
1111

12-
// Added
12+
// Added
13+
// Added
1314

14-
final class TextCodemapFormatter
15+
final readonly class TextCodemapFormatter
1516
{
17+
/**
18+
* @param string[] $allowedPropertyVisibilities
19+
* @param string[] $allowedMethodVisibilities
20+
*/
21+
public function __construct(private array $allowedPropertyVisibilities = ['public'], private array $allowedMethodVisibilities = ['public', 'protected', 'private']) {}
22+
1623
/**
1724
* Formats the codemap data into a human-readable text representation.
1825
*
@@ -37,16 +44,17 @@ public function format(array $codemapData): string
3744
$lines[] = ' Uses: '.implode(', ', $classInformation->usesTraits);
3845
}
3946

40-
// Format Public Properties
47+
// Format Properties based on allowed visibility
4148
foreach ($classInformation->classProperties as $propertyInformation) {
42-
if ($propertyInformation->propertyVisibility === 'public') {
49+
if (in_array($propertyInformation->propertyVisibility, $this->allowedPropertyVisibilities, true)) {
4350
$lines[] = $this->formatProperty($propertyInformation);
4451
}
4552
}
46-
// Format Public Methods
53+
// Format Methods based on allowed visibility
4754
foreach ($classInformation->classMethods as $methodInformation) {
48-
// Show all visibilities for methods, unlike properties
49-
$lines[] = $this->formatMethod($methodInformation);
55+
if (in_array($methodInformation->methodVisibility, $this->allowedMethodVisibilities, true)) {
56+
$lines[] = $this->formatMethod($methodInformation);
57+
}
5058
}
5159
}
5260

@@ -62,16 +70,17 @@ public function format(array $codemapData): string
6270
// Format Traits
6371
foreach ($fileData->traitsInFile as $traitName => $traitDto) {
6472
$lines[] = " Trait: {$traitName}";
65-
// Format Public Properties
73+
// Format Trait Properties based on allowed visibility
6674
foreach ($traitDto->traitProperties as $propertyInformation) {
67-
if ($propertyInformation->propertyVisibility === 'public') {
75+
if (in_array($propertyInformation->propertyVisibility, $this->allowedPropertyVisibilities, true)) {
6876
$lines[] = $this->formatProperty($propertyInformation);
6977
}
7078
}
71-
// Format Public Methods
79+
// Format Trait Methods based on allowed visibility
7280
foreach ($traitDto->traitMethods as $methodInformation) {
73-
// Show all visibilities for methods
74-
$lines[] = $this->formatMethod($methodInformation);
81+
if (in_array($methodInformation->methodVisibility, $this->allowedMethodVisibilities, true)) {
82+
$lines[] = $this->formatMethod($methodInformation);
83+
}
7584
}
7685
}
7786

src/Generator/CodemapGenerator.php

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,18 @@ final class CodemapGenerator
2525
*/
2626
private array $scanPaths = [];
2727

28-
private ?Closure $errorHandler = null;
28+
private ?Closure $errorHandler = null; // Store the config
2929

3030
/**
3131
* Initialize the generator with optional configuration.
3232
*/
33-
public function __construct(?CodemapConfig $config = null)
33+
public function __construct(private readonly ?CodemapConfig $config = null)
3434
{
35-
if ($config instanceof CodemapConfig) {
36-
$this->phpParserVersion = $config->getConfiguredPhpVersion()?->toParserPhpVersion();
37-
$this->scanPaths = $config->getScanPaths();
35+
// Store the config instance
36+
if ($this->config instanceof CodemapConfig) {
37+
$this->phpParserVersion = $this->config->getConfiguredPhpVersion()?->toParserPhpVersion();
38+
$this->scanPaths = $this->config->getScanPaths();
39+
// Exclude paths are now accessible via $this->config->getExcludePaths()
3840
}
3941
}
4042

@@ -147,6 +149,36 @@ private function scanPath(string $pathToScan): array
147149
continue;
148150
}
149151
$filePath = $file->getRealPath();
152+
if ($filePath === false) {
153+
// Handle case where realpath fails, e.g., broken symlink
154+
if ($this->errorHandler instanceof Closure) {
155+
($this->errorHandler)('Warning: Could not resolve real path for "'.$file->getPathname().'". Skipping.');
156+
}
157+
158+
continue;
159+
}
160+
161+
// Check against exclusion paths/patterns from config
162+
$excludePaths = $this->config?->getExcludePaths() ?? [];
163+
$isExcluded = false;
164+
foreach ($excludePaths as $excludePattern) {
165+
// Simple directory prefix check (relative to project root assuming scans start there)
166+
// Or handle absolute paths based on config context. Let's assume relative for now.
167+
$normalizedExclude = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $excludePattern), DIRECTORY_SEPARATOR);
168+
$absoluteExcludePath = realpath($basePath.DIRECTORY_SEPARATOR.$normalizedExclude); // Check if exclude is relative to base
169+
170+
if ($absoluteExcludePath !== false && str_starts_with($filePath, $absoluteExcludePath.DIRECTORY_SEPARATOR)) {
171+
$isExcluded = true;
172+
break;
173+
}
174+
// TODO: Add fnmatch() support for glob patterns if needed
175+
// if (fnmatch($excludePattern, $filePath)) { $isExcluded = true; break; }
176+
}
177+
178+
if ($isExcluded) {
179+
continue; // Skip excluded file
180+
}
181+
150182
$relativePath = str_replace($basePath.DIRECTORY_SEPARATOR, '', $filePath);
151183
try {
152184
$codemapFileDto = $this->processSingleFile($filePath);

src/Generator/SymbolCollectionVisitor.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,12 @@ private function handleClassMethod(ClassMethod $node): void
182182
$methodParameters = [];
183183
foreach ($node->getParams() as $param) {
184184
$paramType = $this->renderTypeNode($param->type);
185-
/* @phpstan-ignore-next-line */
186-
$paramName = is_string($param->var->name) ? $param->var->name : 'unknown';
185+
$paramName = 'unknown'; // Default value
186+
// Ensure $param->var is a Variable node and its name property is a string
187+
if ($param->var instanceof Node\Expr\Variable && is_string($param->var->name)) {
188+
$paramName = $param->var->name;
189+
}
190+
// Optional: Add logging here if $paramName remains 'unknown' for debugging unexpected cases
187191
$methodParameters[] = new CodemapParameterDto($paramName, $paramType);
188192
}
189193

tests/Unit/CodemapGeneratorTest.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ trait TraitX {}
191191
trait TraitY {}
192192
193193
class ComplexClass extends BaseClass implements IfaceA, IfaceB {
194-
use TraitX, TraitY;
194+
use Test\Structure\TraitX, Test\Structure\TraitY;
195195
196196
public function __construct() {}
197197
}
@@ -210,7 +210,6 @@ public function __construct() {}
210210

211211
expect($classDto->extendsClass)->toBe('Test\\Structure\\BaseClass')
212212
->and($classDto->implementsInterfaces)->toEqual(['Test\\Structure\\IfaceA', 'Test\\Structure\\IfaceB'])
213-
// ->and($classDto->usesTraits)->toEqual(['Test\\Structure\\TraitX', 'Test\\Structure\\TraitY']) TODO: should be this, but lets go with the name
214-
->and($classDto->usesTraits)->toEqual(['TraitX', 'TraitY'])
213+
->and($classDto->usesTraits)->toEqual(['Test\\Structure\\TraitX', 'Test\\Structure\\TraitY']) // Expect FQCNs after NameResolver
215214
->and($classDto->classMethods)->toHaveCount(1);
216215
});

0 commit comments

Comments
 (0)