44
55use Ahc \Cli \Helper \OutputHelper ;
66use Ahc \Cli \Input \Command ;
7- use Ahc \Cli \Output \ Writer ;
7+ use Ahc \Cli \IO \ Interactor ;
88
99/**
1010 * A cli application.
@@ -31,45 +31,128 @@ class Application
3131 /** @var string App version */
3232 protected $ version = '0.0.1 ' ;
3333
34+ /** @var string Ascii art logo */
35+ protected $ logo = '' ;
36+
37+ protected $ default = '__default__ ' ;
38+
39+ /** @var Interactor */
40+ protected $ io ;
41+
3442 public function __construct (string $ name , string $ version = '' , callable $ onExit = null )
3543 {
3644 $ this ->name = $ name ;
3745 $ this ->version = $ version ;
3846
3947 // @codeCoverageIgnoreStart
40- $ this ->onExit = $ onExit ?? function () {
41- exit (0 );
48+ $ this ->onExit = $ onExit ?? function ($ exitCode = 0 ) {
49+ exit ($ exitCode );
4250 };
4351 // @codeCoverageIgnoreEnd
4452
45- $ this ->command = $ this ->command ('__default__ ' , 'Default command ' , '' , true )
46- ->on ([$ this , 'showHelp ' ], 'help ' );
47-
48- unset($ this ->commands ['__default__ ' ]);
53+ $ this ->command ('__default__ ' , 'Default command ' , '' , true )->on ([$ this , 'showHelp ' ], 'help ' );
4954 }
5055
56+ /**
57+ * Get the name.
58+ *
59+ * @return string
60+ */
5161 public function name (): string
5262 {
5363 return $ this ->name ;
5464 }
5565
66+ /**
67+ * Get the version.
68+ *
69+ * @return string
70+ */
5671 public function version (): string
5772 {
5873 return $ this ->version ;
5974 }
6075
76+ /**
77+ * Get the commands.
78+ *
79+ * @return Command[]
80+ */
6181 public function commands (): array
6282 {
63- return $ this ->commands ;
83+ $ commands = $ this ->commands ;
84+
85+ unset($ commands ['__default__ ' ]);
86+
87+ return $ commands ;
6488 }
6589
90+ /**
91+ * Get the raw argv.
92+ *
93+ * @return array
94+ */
6695 public function argv (): array
6796 {
6897 return $ this ->argv ;
6998 }
7099
71- public function command (string $ name , string $ desc = '' , string $ alias = '' , bool $ allowUnknown = false ): Command
100+ /**
101+ * Sets or gets the ASCII art logo.
102+ *
103+ * @param string|null $logo
104+ *
105+ * @return string|self
106+ */
107+ public function logo (string $ logo = null )
72108 {
109+ if (\func_num_args () === 0 ) {
110+ return $ this ->logo ;
111+ }
112+
113+ $ this ->logo = $ logo ;
114+
115+ return $ this ;
116+ }
117+
118+ /**
119+ * Add a command by its name desc alias etc.
120+ *
121+ * @param string $name
122+ * @param string $desc
123+ * @param string $alias
124+ * @param bool $allowUnknown
125+ * @param bool $default
126+ *
127+ * @return Command
128+ */
129+ public function command (
130+ string $ name ,
131+ string $ desc = '' ,
132+ string $ alias = '' ,
133+ bool $ allowUnknown = false ,
134+ bool $ default = false
135+ ): Command {
136+ $ command = new Command ($ name , $ desc , $ allowUnknown , $ this );
137+
138+ $ this ->add ($ command , $ alias , $ default );
139+
140+ return $ command ;
141+ }
142+
143+ /**
144+ * Add a prepred command.
145+ *
146+ * @param Command $command
147+ * @param string $alias
148+ * @param bool $default
149+ *
150+ * @return self
151+ */
152+ public function add (Command $ command , string $ alias = '' , bool $ default = false ): self
153+ {
154+ $ name = $ command ->name ();
155+
73156 if ($ this ->commands [$ name ] ?? $ this ->aliases [$ name ] ?? $ this ->commands [$ alias ] ?? $ this ->aliases [$ alias ] ?? null ) {
74157 throw new \InvalidArgumentException (\sprintf ('Command "%s" already added ' , $ name ));
75158 }
@@ -78,11 +161,22 @@ public function command(string $name, string $desc = '', string $alias = '', boo
78161 $ this ->aliases [$ alias ] = $ name ;
79162 }
80163
81- $ command = (new Command ($ name , $ desc , $ allowUnknown , $ this ))->version ($ this ->version )->onExit ($ this ->onExit );
164+ if ($ default ) {
165+ $ this ->default = $ name ;
166+ }
167+
168+ $ this ->commands [$ name ] = $ command ->version ($ this ->version )->onExit ($ this ->onExit )->bind ($ this );
82169
83- return $ this -> commands [ $ name ] = $ command ;
170+ return $ this ;
84171 }
85172
173+ /**
174+ * Gets matching command for given argv.
175+ *
176+ * @param array $argv
177+ *
178+ * @return Command
179+ */
86180 public function commandFor (array $ argv ): Command
87181 {
88182 $ argv += [null , null , null ];
@@ -93,9 +187,36 @@ public function commandFor(array $argv): Command
93187 // cmd alias
94188 ?? $ this ->commands [$ this ->aliases [$ argv [1 ]] ?? null ]
95189 // default.
96- ?? $ this ->command ;
190+ ?? $ this ->commands [ $ this -> default ] ;
97191 }
98192
193+ /**
194+ * Gets or sets io.
195+ *
196+ * @param Interactor|null $io
197+ *
198+ * @return Interactor|self
199+ */
200+ public function io (Interactor $ io = null )
201+ {
202+ if ($ io || !$ this ->io ) {
203+ $ this ->io = $ io ?? new Interactor ;
204+ }
205+
206+ if (\func_num_args () === 0 ) {
207+ return $ this ->io ;
208+ }
209+
210+ return $ this ;
211+ }
212+
213+ /**
214+ * Parse the arguments via the matching command but dont execute action..
215+ *
216+ * @param array $argv Cli arguments/options.
217+ *
218+ * @return Command The matched and parsed command (or default)
219+ */
99220 public function parse (array $ argv ): Command
100221 {
101222 $ this ->argv = $ argv ;
@@ -114,13 +235,41 @@ public function parse(array $argv): Command
114235 }
115236 }
116237
117- $ command ->parse ($ argv );
238+ return $ command ->parse ($ argv );
239+ }
118240
119- $ this ->doAction ($ command );
241+ /**
242+ * Handle the request, invoke action and call exit handler.
243+ *
244+ * @param array $argv
245+ *
246+ * @return mixed
247+ */
248+ public function handle (array $ argv )
249+ {
250+ $ io = $ this ->io ();
120251
121- return $ command ;
252+ try {
253+ $ exitCode = 0 ;
254+ $ command = $ this ->parse ($ argv );
255+
256+ $ this ->doAction ($ command );
257+ } catch (\Throwable $ e ) {
258+ $ exitCode = 255 ;
259+ $ location = 'At file ' . $ e ->getFile () . '# ' . $ e ->getLine ();
260+ $ io ->error ($ e ->getMessage (), true )->bgRed ($ location , true );
261+ }
262+
263+ return ($ this ->onExit )($ exitCode );
122264 }
123265
266+ /**
267+ * Get aliases for given command.
268+ *
269+ * @param Command $command
270+ *
271+ * @return array
272+ */
124273 protected function aliasesFor (Command $ command ): array
125274 {
126275 $ aliases = [$ name = $ command ->name ()];
@@ -135,28 +284,60 @@ protected function aliasesFor(Command $command): array
135284 return $ aliases ;
136285 }
137286
138- public function showHelp (Writer $ writer = null )
287+ /**
288+ * Show help of all commands.
289+ *
290+ * @return mixed
291+ */
292+ public function showHelp ()
139293 {
294+ $ writer = $ this ->io ()->writer ();
295+ $ helper = new OutputHelper ($ writer );
296+
140297 $ header = "{$ this ->name }, version {$ this ->version }" ;
141298 $ footer = 'Run `<command> --help` for specific help ' ;
142299
143- (new OutputHelper ($ writer ))->showCommandsHelp ($ this ->commands , $ header , $ footer );
300+ if ($ this ->logo ) {
301+ $ writer ->write ($ this ->logo , true );
302+ }
303+
304+ $ helper ->showCommandsHelp ($ this ->commands (), $ header , $ footer );
144305
145306 return ($ this ->onExit )();
146307 }
147308
309+ /**
310+ * Invoke command action.
311+ *
312+ * @param Command $command
313+ *
314+ * @return mixed
315+ */
148316 protected function doAction (Command $ command )
149317 {
150318 if (null === $ action = $ command ->action ()) {
151319 return ;
152320 }
153321
322+ // Let the command collect more data (if mising or needs confirmation)
323+ $ command ->interact ($ this ->io ());
324+
154325 $ params = [];
155326 $ values = $ command ->values ();
156- foreach ((new \ReflectionFunction ($ action ))->getParameters () as $ param ) {
327+
328+ foreach ($ this ->getActionParameters ($ action ) as $ param ) {
157329 $ params [] = $ values [$ param ->getName ()] ?? null ;
158330 }
159331
160332 return $ action (...$ params );
161333 }
334+
335+ protected function getActionParameters (callable $ action ): array
336+ {
337+ $ reflex = \is_array ($ action )
338+ ? (new \ReflectionClass ($ action [0 ]))->getMethod ($ action [1 ])
339+ : new \ReflectionFunction ($ action );
340+
341+ return $ reflex ->getParameters ();
342+ }
162343}
0 commit comments