Skip to content

Commit 238e267

Browse files
committed
add the Command attribute
1 parent 80f8cd8 commit 238e267

2 files changed

Lines changed: 136 additions & 0 deletions

File tree

system/CLI/Attributes/Command.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\CLI\Attributes;
15+
16+
use Attribute;
17+
use CodeIgniter\Exceptions\LogicException;
18+
19+
/**
20+
* Attribute to mark a class as a CLI command.
21+
*/
22+
#[Attribute(Attribute::TARGET_CLASS)]
23+
final readonly class Command
24+
{
25+
/**
26+
* @var non-empty-string
27+
*/
28+
public string $name;
29+
30+
/**
31+
* @throws LogicException
32+
*/
33+
public function __construct(
34+
string $name,
35+
public string $description = '',
36+
public string $group = '',
37+
) {
38+
if ($name === '') {
39+
throw new LogicException(lang('Commands.emptyCommandName'));
40+
}
41+
42+
if (preg_match('/^[^\s\:]++(\:[^\s\:]++)*$/', $name) !== 1) {
43+
throw new LogicException(lang('Commands.invalidCommandName', [$name]));
44+
}
45+
46+
$this->name = $name;
47+
}
48+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <[email protected]>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\CLI\Attributes;
15+
16+
use CodeIgniter\Exceptions\LogicException;
17+
use CodeIgniter\Test\CIUnitTestCase;
18+
use PHPUnit\Framework\Attributes\CoversClass;
19+
use PHPUnit\Framework\Attributes\DataProvider;
20+
use PHPUnit\Framework\Attributes\Group;
21+
22+
/**
23+
* @internal
24+
*/
25+
#[CoversClass(Command::class)]
26+
#[Group('Others')]
27+
final class CommandTest extends CIUnitTestCase
28+
{
29+
public function testAttributeExposesProperties(): void
30+
{
31+
$command = new Command(name: 'app:about', description: 'Displays basic info.', group: 'App');
32+
33+
$this->assertSame('app:about', $command->name);
34+
$this->assertSame('Displays basic info.', $command->description);
35+
$this->assertSame('App', $command->group);
36+
}
37+
38+
public function testAttributeAllowsOmittedDescriptionAndGroup(): void
39+
{
40+
$command = new Command(name: 'app:about');
41+
42+
$this->assertSame('', $command->description);
43+
$this->assertSame('', $command->group);
44+
}
45+
46+
/**
47+
* @param array<string, mixed> $parameters
48+
*/
49+
#[DataProvider('provideInvalidDefinitionsAreRejected')]
50+
public function testInvalidDefinitionsAreRejected(string $message, array $parameters): void
51+
{
52+
$this->expectException(LogicException::class);
53+
$this->expectExceptionMessage($message);
54+
55+
new Command(...$parameters);
56+
}
57+
58+
/**
59+
* @return iterable<string, array{string, array<string, mixed>}>
60+
*/
61+
public static function provideInvalidDefinitionsAreRejected(): iterable
62+
{
63+
yield 'empty name' => [
64+
'Command name cannot be empty.',
65+
['name' => ''],
66+
];
67+
68+
yield 'name with whitespace' => [
69+
'Command name "invalid name" is not valid.',
70+
['name' => 'invalid name'],
71+
];
72+
73+
yield 'name starting with colon' => [
74+
'Command name ":invalid" is not valid.',
75+
['name' => ':invalid'],
76+
];
77+
78+
yield 'name ending with colon' => [
79+
'Command name "invalid:" is not valid.',
80+
['name' => 'invalid:'],
81+
];
82+
83+
yield 'name with consecutive colons' => [
84+
'Command name "app::about" is not valid.',
85+
['name' => 'app::about'],
86+
];
87+
}
88+
}

0 commit comments

Comments
 (0)