Skip to content

Commit 1c56012

Browse files
committed
Generate PrefixMap constants from Mixin attributes
- Add PrefixMapGenerator to produce COMPOSABLE/COMPOSABLE_WITH_ARGUMENT constants from #[Mixin] attributes, replacing hand-written arrays - Move Prefix transformer to reference generated PrefixMap constants - Extract NamespaceScanner from MixinGenerator for shared directory scanning - Introduce FluentBuilder subnamespace for builder-chain generators (MixinGenerator, PrefixMapGenerator, MethodBuilder, Mixin attribute) - Add CodeGenerator interface and Config class as shared CodeGen contracts
1 parent 6965c64 commit 1c56012

50 files changed

Lines changed: 393 additions & 152 deletions

Some content is hidden

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

src-dev/CodeGen/CodeGenerator.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/*
4+
* SPDX-License-Identifier: MIT
5+
* SPDX-FileCopyrightText: (c) Respect Project Contributors
6+
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Respect\Dev\CodeGen;
12+
13+
interface CodeGenerator
14+
{
15+
/** @return array<string, string> filename => content */
16+
public function generate(): array;
17+
}

src-dev/CodeGen/Config.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* SPDX-License-Identifier: MIT
5+
* SPDX-FileCopyrightText: (c) Respect Project Contributors
6+
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Respect\Dev\CodeGen;
12+
13+
final readonly class Config
14+
{
15+
public function __construct(
16+
public string $sourceDir,
17+
public string $sourceNamespace,
18+
public string $outputDir,
19+
public string $outputNamespace,
20+
public OutputFormatter $outputFormatter = new OutputFormatter(),
21+
) {
22+
}
23+
}

src-dev/CodeGen/MethodBuilder.php renamed to src-dev/CodeGen/FluentBuilder/MethodBuilder.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
declare(strict_types=1);
1010

11-
namespace Respect\Dev\CodeGen;
11+
namespace Respect\Dev\CodeGen\FluentBuilder;
1212

1313
use Nette\PhpGenerator\Method;
1414
use Nette\PhpGenerator\PhpNamespace;
@@ -41,13 +41,13 @@ public function __construct(
4141

4242
public function build(
4343
PhpNamespace $namespace,
44-
ReflectionClass $validatorReflection,
44+
ReflectionClass $nodeReflection,
4545
string $returnType,
4646
string|null $prefix = null,
4747
bool $static = false,
4848
ReflectionParameter|null $prefixParameter = null,
4949
): Method {
50-
$originalName = $validatorReflection->getShortName();
50+
$originalName = $nodeReflection->getShortName();
5151
$name = $prefix ? $prefix . ucfirst($originalName) : lcfirst($originalName);
5252

5353
$method = new Method($name);
@@ -61,7 +61,7 @@ public function build(
6161
$this->addPrefixParameter($method, $prefixParameter);
6262
}
6363

64-
$constructor = $validatorReflection->getConstructor();
64+
$constructor = $nodeReflection->getConstructor();
6565
if ($constructor === null) {
6666
return $method;
6767
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
declare(strict_types=1);
1010

11-
namespace Respect\Dev\CodeGen\Attributes;
11+
namespace Respect\Dev\CodeGen\FluentBuilder;
1212

1313
use Attribute;
1414

src-dev/CodeGen/MixinGenerator.php renamed to src-dev/CodeGen/FluentBuilder/MixinGenerator.php

Lines changed: 41 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,39 @@
88

99
declare(strict_types=1);
1010

11-
namespace Respect\Dev\CodeGen;
11+
namespace Respect\Dev\CodeGen\FluentBuilder;
1212

13-
use DirectoryIterator;
1413
use Nette\PhpGenerator\PhpNamespace;
1514
use Nette\PhpGenerator\Printer;
1615
use ReflectionClass;
1716
use ReflectionParameter;
18-
use Respect\Dev\CodeGen\Attributes\Mixin;
17+
use Respect\Dev\CodeGen\CodeGenerator;
18+
use Respect\Dev\CodeGen\Config;
19+
use Respect\Dev\CodeGen\InterfaceConfig;
20+
use Respect\Dev\CodeGen\NamespaceScanner;
1921

2022
use function file_get_contents;
2123
use function in_array;
2224
use function is_file;
2325
use function is_readable;
2426
use function ksort;
25-
use function sprintf;
2627

27-
final class MixinGenerator
28+
final class MixinGenerator implements CodeGenerator
2829
{
2930
/** @param array<InterfaceConfig> $interfaces */
3031
public function __construct(
31-
private readonly string $sourceDir,
32-
private readonly string $sourceNamespace,
33-
private readonly string $outputDir,
34-
private readonly string $outputNamespace,
35-
private readonly array $interfaces,
32+
private readonly Config $config,
3633
private readonly MethodBuilder $methodBuilder = new MethodBuilder(),
37-
private readonly OutputFormatter $outputFormatter = new OutputFormatter(),
34+
private readonly array $interfaces = [],
3835
) {
3936
}
4037

4138
/** @return array<string, string> filename => content */
4239
public function generate(): array
4340
{
44-
$validators = $this->scanValidators();
45-
$prefixes = $this->discoverPrefixes($validators);
46-
$filters = $this->discoverFilters($validators);
41+
$nodes = NamespaceScanner::scan($this->config->sourceDir, $this->config->sourceNamespace);
42+
$prefixes = $this->discoverPrefixes($nodes);
43+
$filters = $this->discoverFilters($nodes);
4744

4845
$files = [];
4946

@@ -52,12 +49,12 @@ public function generate(): array
5249

5350
foreach ($prefixes as $prefix) {
5451
$interfaceName = $prefix['name'] . $interfaceConfig->suffix;
55-
$prefixInterfaceNames[] = $this->outputNamespace . '\\' . $interfaceName;
52+
$prefixInterfaceNames[] = $this->config->outputNamespace . '\\' . $interfaceName;
5653

5754
$this->generateInterface(
5855
$interfaceName,
5956
$interfaceConfig,
60-
$validators,
57+
$nodes,
6158
$filters,
6259
$prefix,
6360
$files,
@@ -67,7 +64,7 @@ public function generate(): array
6764
$this->generateRootInterface(
6865
$interfaceConfig,
6966
$prefixInterfaceNames,
70-
$validators,
67+
$nodes,
7168
$filters,
7269
$files,
7370
);
@@ -76,41 +73,16 @@ public function generate(): array
7673
return $files;
7774
}
7875

79-
/** @return array<string, ReflectionClass> */
80-
private function scanValidators(): array
81-
{
82-
$validators = [];
83-
84-
foreach (new DirectoryIterator($this->sourceDir) as $file) {
85-
if (!$file->isFile()) {
86-
continue;
87-
}
88-
89-
$className = $this->sourceNamespace . '\\' . $file->getBasename('.php');
90-
$reflection = new ReflectionClass($className);
91-
92-
if ($reflection->isAbstract()) {
93-
continue;
94-
}
95-
96-
$validators[$reflection->getShortName()] = $reflection;
97-
}
98-
99-
ksort($validators);
100-
101-
return $validators;
102-
}
103-
10476
/**
105-
* @param array<string, ReflectionClass> $validators
77+
* @param array<string, ReflectionClass> $nodes
10678
*
10779
* @return array<array{name: string, prefix: string, requireInclusion: bool, prefixParameter: ?ReflectionParameter}>
10880
*/
109-
private function discoverPrefixes(array $validators): array
81+
private function discoverPrefixes(array $nodes): array
11082
{
11183
$prefixes = [];
11284

113-
foreach ($validators as $reflection) {
85+
foreach ($nodes as $reflection) {
11486
$attributes = $reflection->getAttributes(Mixin::class);
11587
if ($attributes === []) {
11688
continue;
@@ -121,14 +93,13 @@ private function discoverPrefixes(array $validators): array
12193
continue;
12294
}
12395

96+
$constructor = $reflection->getConstructor();
12497
$prefixParameter = null;
125-
if ($mixin->prefixParameter) {
126-
$constructor = $reflection->getConstructor();
127-
if ($constructor !== null) {
128-
$params = $constructor->getParameters();
129-
if ($params !== []) {
130-
$prefixParameter = $params[0];
131-
}
98+
99+
if ($mixin->prefixParameter && $constructor !== null) {
100+
$parameters = $constructor->getParameters();
101+
if ($parameters !== []) {
102+
$prefixParameter = $parameters[0];
132103
}
133104
}
134105

@@ -146,15 +117,15 @@ private function discoverPrefixes(array $validators): array
146117
}
147118

148119
/**
149-
* @param array<string, ReflectionClass> $validators
120+
* @param array<string, ReflectionClass> $nodes
150121
*
151122
* @return array<string, Mixin>
152123
*/
153-
private function discoverFilters(array $validators): array
124+
private function discoverFilters(array $nodes): array
154125
{
155126
$filters = [];
156127

157-
foreach ($validators as $name => $reflection) {
128+
foreach ($nodes as $name => $reflection) {
158129
$attributes = $reflection->getAttributes(Mixin::class);
159130
if ($attributes === []) {
160131
continue;
@@ -167,23 +138,23 @@ private function discoverFilters(array $validators): array
167138
}
168139

169140
/**
170-
* @param array<string, ReflectionClass> $validators
141+
* @param array<string, ReflectionClass> $nodes
171142
* @param array<string, Mixin> $filters
172143
* @param array{name: string, prefix: string, requireInclusion: bool, prefixParameter: ?ReflectionParameter} $prefix
173144
* @param array<string, string> $files
174145
*/
175146
private function generateInterface(
176147
string $interfaceName,
177148
InterfaceConfig $config,
178-
array $validators,
149+
array $nodes,
179150
array $filters,
180151
array $prefix,
181152
array &$files,
182153
): void {
183-
$namespace = new PhpNamespace($this->outputNamespace);
154+
$namespace = new PhpNamespace($this->config->outputNamespace);
184155
$interface = $namespace->addInterface($interfaceName);
185156

186-
foreach ($validators as $name => $reflection) {
157+
foreach ($nodes as $name => $reflection) {
187158
$mixin = $filters[$name] ?? null;
188159

189160
if ($prefix['requireInclusion']) {
@@ -211,27 +182,29 @@ private function generateInterface(
211182

212183
/**
213184
* @param array<string> $prefixInterfaceNames
214-
* @param array<string, ReflectionClass> $validators
185+
* @param array<string, ReflectionClass> $nodes
215186
* @param array<string, Mixin> $filters
216187
* @param array<string, string> $files
217188
*/
218189
private function generateRootInterface(
219190
InterfaceConfig $config,
220191
array $prefixInterfaceNames,
221-
array $validators,
192+
array $nodes,
222193
array $filters,
223194
array &$files,
224195
): void {
225196
$interfaceName = $config->suffix;
226-
$namespace = new PhpNamespace($this->outputNamespace);
197+
$namespace = new PhpNamespace($this->config->outputNamespace);
227198
$interface = $namespace->addInterface($interfaceName);
228199

229200
foreach ($config->rootExtends as $extend) {
201+
$namespace->addUse($extend);
230202
$interface->addExtend($extend);
231203
}
232204

233-
foreach ($prefixInterfaceNames as $prefixInterface) {
234-
$interface->addExtend($prefixInterface);
205+
foreach ($prefixInterfaceNames as $prefixInterfaceName) {
206+
$namespace->addUse($prefixInterfaceName);
207+
$interface->addExtend($prefixInterfaceName);
235208
}
236209

237210
if ($config->rootComment !== null) {
@@ -242,7 +215,7 @@ private function generateRootInterface(
242215
$namespace->addUse($use);
243216
}
244217

245-
foreach ($validators as $reflection) {
218+
foreach ($nodes as $reflection) {
246219
$method = $this->methodBuilder->build(
247220
$namespace,
248221
$reflection,
@@ -260,16 +233,17 @@ private function generateRootInterface(
260233
/** @param array<string, string> $files */
261234
private function addFile(string $interfaceName, PhpNamespace $namespace, array &$files): void
262235
{
236+
$filename = $this->config->outputDir . '/' . $interfaceName . '.php';
237+
263238
$printer = new Printer();
264239
$printer->wrapLength = 300;
265240

266-
$filename = sprintf('%s/%s.php', $this->outputDir, $interfaceName);
267241
$existingContent = '';
268242
if (is_file($filename) && is_readable($filename)) {
269243
$existingContent = file_get_contents($filename) ?: '';
270244
}
271245

272-
$formattedContent = $this->outputFormatter->format(
246+
$formattedContent = $this->config->outputFormatter->format(
273247
$printer->printNamespace($namespace),
274248
$existingContent,
275249
);

0 commit comments

Comments
 (0)