13
13
use Inhere \Console \Command ;
14
14
use Inhere \Console \Console ;
15
15
use Inhere \Console \Contract \CommandInterface ;
16
+ use Inhere \Console \Handler \AbstractHandler ;
17
+ use Inhere \Console \Handler \CommandWrapper ;
18
+ use Inhere \Console \Util \ConsoleUtil ;
16
19
use Inhere \Console \Util \Helper ;
17
20
use InvalidArgumentException ;
21
+ use Toolkit \Stdlib \Helper \Assert ;
18
22
use Toolkit \Stdlib \Obj \Traits \NameAliasTrait ;
19
23
use function array_keys ;
20
24
use function array_merge ;
24
28
use function is_object ;
25
29
use function is_string ;
26
30
use function is_subclass_of ;
27
- use function method_exists ;
28
31
use function preg_match ;
29
32
30
33
/**
@@ -36,6 +39,11 @@ trait SubCommandsWareTrait
36
39
{
37
40
use NameAliasTrait;
38
41
42
+ /**
43
+ * @var AbstractHandler|null
44
+ */
45
+ protected ?AbstractHandler $ parent = null ;
46
+
39
47
/**
40
48
* @var array
41
49
*/
@@ -44,76 +52,110 @@ trait SubCommandsWareTrait
44
52
/**
45
53
* The sub-commands of the command
46
54
*
47
- * @var array
55
+ * ```php
48
56
* [
49
57
* 'name' => [
50
- * 'handler' => MyCommand::class, // allow: string|Closure|CommandInterface
51
- * 'config' => []
58
+ * 'handler' => MyCommand::class,
59
+ * 'config' => [
60
+ * 'name' => 'string',
61
+ * 'desc' => 'string',
62
+ * 'options' => [],
63
+ * 'arguments' => [],
64
+ * ]
52
65
* ]
53
66
* ]
67
+ * ```
68
+ *
69
+ * @var array<string, array{handler:mixed, config:array}>
54
70
*/
55
71
private array $ commands = [];
56
72
57
73
/**
58
- * Can attach sub-commands
74
+ * Can attach sub-commands to current command
59
75
*
60
76
* @return array
61
77
*/
62
- protected function commands (): array
78
+ protected function subCommands (): array
63
79
{
64
80
// [
65
81
// 'cmd1' => function(){},
82
+ // // class name
66
83
// MySubCommand::class,
67
84
// 'cmd2' => MySubCommand2::class,
68
- // new FooCommand,
85
+ // // no key
86
+ // new FooCommand(),
69
87
// 'cmd3' => new FooCommand2(),
70
88
// ]
71
89
return [];
72
90
}
73
91
74
92
/**
75
93
* @param string $name
94
+ * @param array $args
95
+ *
96
+ * @return mixed
76
97
*/
77
- protected function dispatchCommand (string $ name ): void
98
+ protected function dispatchSub (string $ name, array $ args ): mixed
78
99
{
100
+ $ subInfo = $ this ->commands [$ name ];
101
+ $ this ->debugf ('dispatch the attached subcommand: %s ' , $ name );
102
+
103
+ // create and init sub-command
104
+ $ subCmd = $ this ->createSubCommand ($ subInfo );
105
+ $ subCmd ->setParent ($ this );
106
+ $ subCmd ->setInputOutput ($ this ->input , $ this ->output );
107
+
108
+ return $ subCmd ->run ($ args );
109
+ }
110
+
111
+ /**
112
+ * @param array{name: string, desc: string, options: array, arguments: array} $subInfo
113
+ *
114
+ * @return Command
115
+ */
116
+ protected function createSubCommand (array $ subInfo ): Command
117
+ {
118
+ $ handler = $ subInfo ['handler ' ];
119
+ if (is_object ($ handler )) {
120
+ if ($ handler instanceof Command) {
121
+ return $ handler ;
122
+ }
123
+
124
+ return CommandWrapper::wrap ($ handler , $ subInfo ['config ' ]);
125
+ }
126
+
127
+ // class-string of Command
128
+ return new $ handler ;
79
129
}
80
130
81
131
/**
82
132
* Register a app independent console command
83
133
*
84
- * @param string|CommandInterface $name
134
+ * @param string|class-string $name
85
135
* @param string|Closure|CommandInterface|null $handler
86
- * @param array $config
87
- * array:
88
- * - aliases The command aliases
89
- * - description The description message
90
- *
91
- * @throws InvalidArgumentException
136
+ * @param array $config
92
137
*/
93
138
public function addSub (string $ name , string |Closure |CommandInterface $ handler = null , array $ config = []): void
94
139
{
95
140
if (!$ handler && class_exists ($ name )) {
96
141
/** @var Command $name name is an command class */
97
142
$ handler = $ name ;
98
143
$ name = $ name ::getName ();
144
+ } elseif (!$ name && $ handler instanceof Command) {
145
+ $ name = $ handler ->getRealName ();
146
+ } elseif (!$ name && class_exists ($ handler )) {
147
+ $ name = $ handler ::getName ();
99
148
}
100
149
101
- if (!$ name || !$ handler ) {
102
- Helper::throwInvalidArgument ("Command 'name' and 'handler' cannot be empty! name: $ name " );
103
- }
150
+ Assert::isFalse (!$ name || !$ handler , "Command 'name' and 'handler' cannot be empty! name: $ name " );
151
+ Assert::isFalse (isset ($ this ->commands [$ name ]), "Command ' $ name' have been registered! " );
104
152
105
153
$ this ->validateName ($ name );
106
154
107
- if (isset ($ this ->commands [$ name ])) {
108
- Helper::throwInvalidArgument ("Command ' $ name' have been registered! " );
109
- }
110
-
111
155
$ config ['aliases ' ] = isset ($ config ['aliases ' ]) ? (array )$ config ['aliases ' ] : [];
112
156
113
157
if (is_string ($ handler )) {
114
- if (!class_exists ($ handler )) {
115
- Helper::throwInvalidArgument ("The command handler class [ $ handler] not exists! " );
116
- }
158
+ Assert::isTrue (class_exists ($ handler ), "The console command class ' $ handler' not exists! " );
117
159
118
160
if (!is_subclass_of ($ handler , Command::class)) {
119
161
Helper::throwInvalidArgument ('The command handler class must is subclass of the: ' . Command::class);
@@ -129,42 +171,89 @@ public function addSub(string $name, string|Closure|CommandInterface $handler =
129
171
if ($ aliases = $ handler ::aliases ()) {
130
172
$ config ['aliases ' ] = array_merge ($ config ['aliases ' ], $ aliases );
131
173
}
132
- } elseif (!is_object ($ handler ) || !method_exists ($ handler, ' __invoke ' )) {
174
+ } elseif (!is_object ($ handler ) || !ConsoleUtil:: isValidCmdObject ($ handler )) {
133
175
Helper::throwInvalidArgument (
134
- 'The console command handler must is an subclass of %s OR a Closure OR a object have method __invoke() ' ,
135
- Command::class
176
+ 'The command handler must is an subclass of %s OR a Closure OR a sub-object of %s ' ,
177
+ Command::class,
178
+ Command::class,
136
179
);
137
180
}
138
181
182
+ // has alias option
183
+ if ($ config ['aliases ' ]) {
184
+ $ this ->setAlias ($ name , $ config ['aliases ' ], true );
185
+ }
186
+
187
+ $ config ['name ' ] = $ name ;
188
+ // save
189
+ $ this ->commands [$ name ] = [
190
+ 'type ' => Console::CMD_SINGLE ,
191
+ 'handler ' => $ handler ,
192
+ 'config ' => $ config ,
193
+ ];
194
+ }
195
+
196
+ /**
197
+ * @param CommandInterface $handler
198
+ *
199
+ * @return $this
200
+ */
201
+ public function addSubHandler (CommandInterface $ handler ): static
202
+ {
203
+ $ name = $ handler ->getRealName ();
204
+
139
205
// is an class name string
140
206
$ this ->commands [$ name ] = [
141
207
'type ' => Console::CMD_SINGLE ,
142
208
'handler ' => $ handler ,
143
- 'config ' => $ config ,
209
+ 'config ' => [] ,
144
210
];
145
211
146
- // has alias option
147
- if (isset ($ config ['aliases ' ])) {
148
- $ this ->setAlias ($ name , $ config ['aliases ' ], true );
149
- }
212
+ return $ this ;
150
213
}
151
214
152
215
/**
153
216
* @param array $commands
154
- *
155
- * @throws InvalidArgumentException
156
217
*/
157
218
public function addCommands (array $ commands ): void
158
219
{
159
220
foreach ($ commands as $ name => $ handler ) {
160
221
if (is_int ($ name )) {
161
- $ this ->addSub ($ handler );
222
+ $ this ->addSub ('' , $ handler );
162
223
} else {
163
224
$ this ->addSub ($ name , $ handler );
164
225
}
165
226
}
166
227
}
167
228
229
+ /**
230
+ * @param AbstractHandler $parent
231
+ */
232
+ public function setParent (AbstractHandler $ parent ): void
233
+ {
234
+ $ this ->parent = $ parent ;
235
+ }
236
+
237
+ /**
238
+ * @return $this
239
+ */
240
+ public function getRoot (): static
241
+ {
242
+ if ($ this ->parent ) {
243
+ return $ this ->parent ->getRoot ();
244
+ }
245
+
246
+ return $ this ;
247
+ }
248
+
249
+ /**
250
+ * @return static|null
251
+ */
252
+ public function getParent (): ?static
253
+ {
254
+ return $ this ->parent ;
255
+ }
256
+
168
257
/**********************************************************
169
258
* helper methods
170
259
**********************************************************/
0 commit comments