55use InvalidArgumentException ;
66use PhpDevCommunity \Console \Command \CommandInterface ;
77use PhpDevCommunity \Console \Command \HelpCommand ;
8+ use PhpDevCommunity \Console \Option \CommandOption ;
89use PhpDevCommunity \Console \Output \ConsoleOutput ;
910use Throwable ;
1011use const PHP_EOL ;
@@ -13,6 +14,9 @@ final class CommandRunner
1314{
1415 const CLI_ERROR = 1 ;
1516 const CLI_SUCCESS = 0 ;
17+ public const CLI_COMMAND_NOT_FOUND = 10 ; // Aucune commande trouvée
18+ public const CLI_INVALID_ARGUMENTS = 11 ; // Arguments invalides
19+ public const CLI_AMBIGUOUS_COMMAND = 12 ; // Plusieurs correspondances possibles
1620
1721 /**
1822 * @var CommandInterface[]
@@ -58,7 +62,7 @@ public function run(CommandParser $commandParser, OutputInterface $output): int
5862 }
5963
6064 if (empty ($ commands )) {
61- throw new InvalidArgumentException (sprintf ('Command "%s" is not defined. ' , $ commandParser ->getCommandName ()));
65+ throw new InvalidArgumentException (sprintf ('Command "%s" is not defined. ' , $ commandParser ->getCommandName ()), self :: CLI_COMMAND_NOT_FOUND );
6266 }
6367
6468 if (count ($ commands ) > 1 ) {
@@ -69,7 +73,7 @@ public function run(CommandParser $commandParser, OutputInterface $output): int
6973 $ consoleOutput = new ConsoleOutput ($ output );
7074 $ consoleOutput ->error (sprintf ('Command "%s" is ambiguous. ' , $ commandParser ->getCommandName ()));
7175 $ consoleOutput ->listKeyValues ($ names , true );
72- return self ::CLI_ERROR ;
76+ return self ::CLI_AMBIGUOUS_COMMAND ;
7377 }
7478
7579 $ command = $ commands [0 ];
@@ -84,16 +88,32 @@ public function run(CommandParser $commandParser, OutputInterface $output): int
8488
8589 } catch (Throwable $ e ) {
8690 (new ConsoleOutput ($ output ))->error ($ e ->getMessage ());
87- return self ::CLI_ERROR ;
91+ return in_array ( $ e -> getCode (), [ self :: CLI_COMMAND_NOT_FOUND , self :: CLI_INVALID_ARGUMENTS ]) ? $ e -> getCode () : self ::CLI_ERROR ;
8892 }
8993
9094 }
9195
9296 private function execute (CommandInterface $ command , CommandParser $ commandParser , OutputInterface $ output )
9397 {
9498 $ argvOptions = [];
95-
9699 $ options = $ command ->getOptions ();
100+ $ forbidden = ['help ' , 'h ' , 'verbose ' , 'v ' ];
101+ foreach ($ options as $ option ) {
102+ $ name = $ option ->getName ();
103+ $ shortcut = $ option ->getShortcut ();
104+ if (in_array ($ name , $ forbidden , true ) || ($ shortcut !== null && in_array ($ shortcut , $ forbidden , true ))) {
105+ $ invalid = in_array ($ name , $ forbidden , true ) ? $ name : $ shortcut ;
106+ throw new \InvalidArgumentException (
107+ sprintf (
108+ 'The option "%s" is reserved and cannot be used with the "%s" command. ' ,
109+ $ invalid ,
110+ $ command ->getName ()
111+ )
112+ );
113+ }
114+ }
115+
116+ $ options [] = new CommandOption ('verbose ' , 'v ' , 'Enable verbose output ' , true );
97117 foreach ($ options as $ option ) {
98118 if ($ option ->isFlag ()) {
99119 $ argvOptions ["-- {$ option ->getName ()}" ] = false ;
@@ -135,7 +155,20 @@ private function execute(CommandInterface $command, CommandParser $commandParser
135155 throw new InvalidArgumentException (sprintf ('Too many arguments for command "%s". Expected %d, got %d. ' , $ command ->getName (), count ($ arguments ), count ($ commandParser ->getArguments ())));
136156 }
137157
138- $ command ->execute (new Input ($ commandParser ->getCommandName (), $ argvOptions , $ argv ), $ output );
158+ $ startTime = microtime (true );
159+ $ input = new Input ($ commandParser ->getCommandName (), $ argvOptions , $ argv );
160+ $ command ->execute ($ input , $ output );
161+ $ endTime = microtime (true );
162+ $ peakMemoryBytes = memory_get_peak_usage (true );
163+ $ peakMemoryMB = round ($ peakMemoryBytes / 1024 / 1024 , 2 );
164+ $ duration = round ($ endTime - $ startTime , 2 );
165+ if ($ input ->getOptionValue ('verbose ' )) {
166+ $ output ->writeln (sprintf (
167+ 'Execution time: %.2fs; Peak memory usage: %.2f MB ' ,
168+ $ duration ,
169+ $ peakMemoryMB
170+ ));
171+ }
139172 }
140173
141174 private function showCommandHelp (CommandInterface $ selectedCommand , OutputInterface $ output ): void
@@ -171,8 +204,18 @@ private function showCommandHelp(CommandInterface $selectedCommand, OutputInterf
171204 $ consoleOutput ->listKeyValues ($ options , true );
172205 }
173206
174- private static function stringStartsWith (string $ haystack , string $ needle ): bool
207+ private static function stringStartsWith (string $ command , string $ input ): bool
175208 {
176- return substr ($ haystack , 0 , strlen ($ needle )) === $ needle ;
209+ $ commandParts = explode (': ' , $ command );
210+ $ inputParts = explode (': ' , $ input );
211+ foreach ($ inputParts as $ i => $ inPart ) {
212+ $ cmdPart = $ commandParts [$ i ] ?? null ;
213+
214+ if ($ cmdPart === null || strpos ($ cmdPart , $ inPart ) !== 0 ) {
215+ return false ;
216+ }
217+ }
218+
219+ return true ;
177220 }
178221}
0 commit comments