Skip to content

Commit a76d51d

Browse files
committed
refactor: migrate make:test to AbstractGeneratorCommand
1 parent 4f9d165 commit a76d51d

5 files changed

Lines changed: 125 additions & 220 deletions

File tree

system/CLI/AbstractGeneratorCommand.php

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ protected function provideGeneratorOptions(): void
9393
$this->addNamespaceOption()->addSuffixOption()->addForceOption();
9494
}
9595

96-
final protected function addNamespaceOption(): static
96+
final protected function addNamespaceOption(string $default = APP_NAMESPACE): static
9797
{
9898
return $this->addOption(new Option(
9999
name: 'namespace',
100100
shortcut: 'n',
101101
description: 'Set the root namespace.',
102102
requiresValue: true,
103-
default: APP_NAMESPACE,
103+
default: $default,
104104
));
105105
}
106106

@@ -259,28 +259,34 @@ protected function buildContent(string $class): string
259259
protected function buildPath(string $class): string
260260
{
261261
$namespace = $this->getNamespace();
262+
$basePath = $this->getBasePath($namespace);
262263

263-
$bases = service('autoloader')->getNamespace($namespace);
264-
$base = reset($bases);
265-
266-
if ($base === false || $base === '') {
264+
if ($basePath === null) {
267265
CLI::error(lang('CLI.namespaceNotDefined', [$namespace]));
268266

269267
return '';
270268
}
271269

272-
$realpath = realpath($base);
273-
$base = ($realpath !== false) ? $realpath : $base;
270+
$realpath = realpath($basePath);
271+
$basePath = ($realpath !== false) ? $realpath : $basePath;
274272

275273
$prefix = $namespace . '\\';
276274
$relative = str_starts_with($class, $prefix) ? substr($class, strlen($prefix)) : $class;
277275

278-
$file = $base . DIRECTORY_SEPARATOR
276+
$file = $basePath . DIRECTORY_SEPARATOR
279277
. str_replace('\\', DIRECTORY_SEPARATOR, trim($relative, '\\')) . '.php';
280278

281279
return dirname($file) . DIRECTORY_SEPARATOR . $this->basename($file);
282280
}
283281

282+
/**
283+
* Returns the directory registered for the namespace in the autoloader, or `null` when it is not defined.
284+
*/
285+
protected function getBasePath(string $namespace): ?string
286+
{
287+
return service('autoloader')->getNamespace($namespace)[0] ?? null;
288+
}
289+
284290
/**
285291
* Gets the root namespace from the attribute override or the `--namespace` option.
286292
*/
@@ -321,7 +327,7 @@ private function generate(string $class): int
321327
*/
322328
private function generateFile(string $target, string $content): int
323329
{
324-
if ($this->getNamespace() === 'CodeIgniter') {
330+
if (str_starts_with($target, SYSTEMPATH)) {
325331
CLI::write(lang('CLI.generator.usingCINamespace'), 'yellow');
326332

327333
if (

system/Commands/Generators/TestGenerator.php

Lines changed: 39 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -13,177 +13,65 @@
1313

1414
namespace CodeIgniter\Commands\Generators;
1515

16-
use CodeIgniter\CLI\BaseCommand;
17-
use CodeIgniter\CLI\CLI;
18-
use CodeIgniter\CLI\GeneratorTrait;
19-
20-
/**
21-
* Generates a skeleton command file.
22-
*/
23-
class TestGenerator extends BaseCommand
16+
use CodeIgniter\CLI\AbstractGeneratorCommand;
17+
use CodeIgniter\CLI\Attributes\Command;
18+
use CodeIgniter\CLI\Attributes\GeneratorCommand;
19+
20+
#[Command(name: 'make:test', description: 'Generates a new test file.', group: 'Generators')]
21+
#[GeneratorCommand(
22+
component: 'Test',
23+
template: 'test.tpl.php',
24+
classNameLang: 'CLI.generator.className.test',
25+
)]
26+
class TestGenerator extends AbstractGeneratorCommand
2427
{
25-
use GeneratorTrait;
26-
27-
/**
28-
* The Command's Group
29-
*
30-
* @var string
31-
*/
32-
protected $group = 'Generators';
33-
34-
/**
35-
* The Command's Name
36-
*
37-
* @var string
38-
*/
39-
protected $name = 'make:test';
40-
41-
/**
42-
* The Command's Description
43-
*
44-
* @var string
45-
*/
46-
protected $description = 'Generates a new test file.';
47-
48-
/**
49-
* The Command's Usage
50-
*
51-
* @var string
52-
*/
53-
protected $usage = 'make:test <name> [options]';
54-
55-
/**
56-
* The Command's Arguments
57-
*
58-
* @var array<string, string>
59-
*/
60-
protected $arguments = [
61-
'name' => 'The test class name.',
62-
];
63-
64-
/**
65-
* The Command's Options
66-
*
67-
* @var array<string, string>
68-
*/
69-
protected $options = [
70-
'--namespace' => 'Set root namespace. Default: "Tests".',
71-
'--force' => 'Force overwrite existing file.',
72-
];
73-
74-
/**
75-
* Actually execute a command.
76-
*/
77-
public function run(array $params)
78-
{
79-
// Ensure tests are always suffixed with 'Test'
80-
$params['suffix'] = null;
81-
82-
$this->component = 'Test';
83-
$this->template = 'test.tpl.php';
84-
85-
$this->classNameLang = 'CLI.generator.className.test';
28+
private const DEFAULT_NAMESPACE = 'Tests';
8629

87-
$autoload = service('autoloader');
88-
$autoload->addNamespace('CodeIgniter', TESTPATH . 'system');
89-
$autoload->addNamespace('Tests', ROOTPATH . 'tests');
30+
protected function provideGeneratorOptions(): void
31+
{
32+
$this->addNamespaceOption(self::DEFAULT_NAMESPACE)->addForceOption();
33+
}
9034

91-
$this->generateClass($params);
35+
protected function initialize(array &$arguments, array &$options): void
36+
{
37+
$autoloader = service('autoloader');
38+
$autoloader->addNamespace('CodeIgniter', TESTPATH . 'system');
39+
$autoloader->addNamespace(self::DEFAULT_NAMESPACE, ROOTPATH . 'tests');
40+
}
9241

93-
return EXIT_SUCCESS;
42+
protected function shouldAppendSuffix(): bool
43+
{
44+
return true;
9445
}
9546

96-
/**
97-
* Gets the namespace from input or the default namespace.
98-
*/
9947
protected function getNamespace(): string
10048
{
101-
if ($this->namespace !== null) {
102-
return $this->namespace;
49+
if ($this->hasUnboundOption('namespace')) {
50+
return parent::getNamespace();
10351
}
10452

105-
if ($this->getOption('namespace') !== null) {
106-
return trim(
107-
str_replace(
108-
'/',
109-
'\\',
110-
$this->getOption('namespace'),
111-
),
112-
'\\',
113-
);
114-
}
53+
helper('inflector');
11554

116-
$class = $this->normalizeInputClassName();
117-
$classPaths = explode('\\', $class);
55+
$name = $this->getValidatedArgument('name');
56+
$segments = array_map(pascalize(...), explode('\\', str_replace('/', '\\', $name)));
57+
$autoloader = service('autoloader');
11858

119-
$namespaces = service('autoloader')->getNamespace();
59+
while ($segments !== []) {
60+
array_pop($segments);
12061

121-
while ($classPaths !== []) {
122-
array_pop($classPaths);
123-
$namespace = implode('\\', $classPaths);
62+
$namespace = implode('\\', $segments);
12463

125-
foreach (array_keys($namespaces) as $prefix) {
126-
if ($prefix === $namespace) {
127-
// The input classname is FQCN, and use the namespace.
128-
return $namespace;
129-
}
64+
if ($namespace !== '' && $autoloader->getNamespace($namespace) !== []) {
65+
return $namespace;
13066
}
13167
}
13268

133-
return 'Tests';
134-
}
135-
136-
/**
137-
* Builds the test file path from the class name.
138-
*
139-
* @param string $class namespaced classname.
140-
*/
141-
protected function buildPath(string $class): string
142-
{
143-
$namespace = $this->getNamespace();
144-
145-
$base = $this->searchTestFilePath($namespace);
146-
147-
if ($base === null) {
148-
CLI::error(
149-
lang('CLI.namespaceNotDefined', [$namespace]),
150-
'light_gray',
151-
'red',
152-
);
153-
CLI::newLine();
154-
155-
return '';
156-
}
157-
158-
$realpath = realpath($base);
159-
$base = ($realpath !== false) ? $realpath : $base;
160-
161-
$file = $base . DIRECTORY_SEPARATOR
162-
. str_replace(
163-
'\\',
164-
DIRECTORY_SEPARATOR,
165-
trim(str_replace($namespace . '\\', '', $class), '\\'),
166-
) . '.php';
167-
168-
return implode(
169-
DIRECTORY_SEPARATOR,
170-
array_slice(
171-
explode(DIRECTORY_SEPARATOR, $file),
172-
0,
173-
-1,
174-
),
175-
) . DIRECTORY_SEPARATOR . $this->basename($file);
69+
return self::DEFAULT_NAMESPACE;
17670
}
17771

178-
/**
179-
* Returns test file path for the namespace.
180-
*/
181-
private function searchTestFilePath(string $testNamespace): ?string
72+
protected function getBasePath(string $namespace): ?string
18273
{
183-
/** @var list<non-empty-string> $testPaths */
184-
$testPaths = service('autoloader')->getNamespace($testNamespace);
185-
186-
foreach ($testPaths as $candidate) {
74+
foreach (service('autoloader')->getNamespace($namespace) as $candidate) {
18775
if (str_contains($candidate, DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR)) {
18876
return $candidate;
18977
}

0 commit comments

Comments
 (0)