@@ -12,6 +12,8 @@ class App
1212 'commands ' => [],
1313 ];
1414
15+ protected $ eventListeners = [];
16+
1517 public function __construct (array $ config = [])
1618 {
1719 $ this ->config = $ config ;
@@ -96,6 +98,66 @@ public function autoloadCommands(string $path): void
9698 }
9799 }
98100
101+ /**
102+ * Register an event listener
103+ * @param string $event The event name
104+ * @param callable $listener The event listener callback
105+ * @return void
106+ */
107+ public function on (string $ event , callable $ listener ): void
108+ {
109+ if (!isset ($ this ->eventListeners [$ event ])) {
110+ $ this ->eventListeners [$ event ] = [];
111+ }
112+
113+ $ this ->eventListeners [$ event ][] = $ listener ;
114+ }
115+
116+ /**
117+ * Emit an event to all registered listeners
118+ * @param string $event The event name
119+ * @param array $data Event data
120+ * @return Event
121+ */
122+ public function emit (string $ event , array $ data = []): Event
123+ {
124+ $ eventObject = new Event ($ event , $ data );
125+
126+ if (!isset ($ this ->eventListeners [$ event ])) {
127+ return $ eventObject ;
128+ }
129+
130+ foreach ($ this ->eventListeners [$ event ] as $ listener ) {
131+ if ($ eventObject ->isPropagationStopped ()) {
132+ break ;
133+ }
134+
135+ call_user_func ($ listener , $ eventObject );
136+ }
137+
138+ return $ eventObject ;
139+ }
140+
141+ /**
142+ * Remove event listeners for a specific event
143+ * @param string $event The event name
144+ * @return void
145+ */
146+ public function off (string $ event ): void
147+ {
148+ unset($ this ->eventListeners [$ event ]);
149+ }
150+
151+ /**
152+ * Check if an event has any listeners
153+ * @param string $event The event name
154+ * @return bool
155+ */
156+ public function hasListeners (string $ event ): bool
157+ {
158+ return isset ($ this ->eventListeners [$ event ]) && !empty ($ this ->eventListeners [$ event ]);
159+ }
160+
99161 protected function normalizeCommandInput (string $ input ): array
100162 {
101163 $ tokens = preg_split ('/\s+/ ' , trim ($ input ));
@@ -182,10 +244,54 @@ public function run()
182244 }
183245
184246 if (!isset ($ this ->config ['commands ' ][$ commandName ])) {
247+ if ($ this ->hasListeners ('command.notFound ' )) {
248+ $ event = $ this ->emit ('command.notFound ' , [
249+ 'commandName ' => $ commandName ,
250+ 'commandData ' => $ commandData ,
251+ 'argv ' => $ argv
252+ ]);
253+
254+ if ($ event ->getExitCode () !== 0 ) {
255+ exit ($ event ->getExitCode ());
256+ }
257+
258+ return ;
259+ }
260+
185261 echo "Command not found \n" ;
186262 return ;
187263 }
188264
265+ $ beforeEvent = $ this ->emit ('command.before ' , [
266+ 'commandName ' => $ commandName ,
267+ 'commandData ' => $ commandData ,
268+ 'argv ' => $ argv
269+ ]);
270+
271+ if ($ beforeEvent ->isPropagationStopped ()) {
272+ if ($ beforeEvent ->getExitCode () !== 0 ) {
273+ exit ($ beforeEvent ->getExitCode ());
274+ }
275+
276+ return ;
277+ }
278+
279+ if ($ this ->hasListeners ($ commandName )) {
280+ $ customEvent = $ this ->emit ($ commandName , [
281+ 'commandName ' => $ commandName ,
282+ 'commandData ' => $ commandData ,
283+ 'argv ' => $ argv
284+ ]);
285+
286+ if ($ customEvent ->isPropagationStopped ()) {
287+ if ($ customEvent ->getExitCode () !== 0 ) {
288+ exit ($ customEvent ->getExitCode ());
289+ }
290+
291+ return ;
292+ }
293+ }
294+
189295 $ command = $ this ->config ['commands ' ][$ commandName ]['handler ' ];
190296
191297 foreach (array_values ($ command ->getHelp ()['params ' ]) as $ paramData ) {
@@ -219,7 +325,18 @@ public function run()
219325 return ;
220326 }
221327
222- return $ command ->call ($ command );
328+ $ result = $ command ->call ($ command );
329+
330+ $ this ->emit ('command.after ' , [
331+ 'commandName ' => $ commandName ,
332+ 'commandData ' => $ commandData ,
333+ 'params ' => $ params ,
334+ 'arguments ' => $ arguments ,
335+ 'result ' => $ result ,
336+ 'argv ' => $ argv
337+ ]);
338+
339+ return $ result ;
223340 }
224341
225342 protected function parseCommandSignature (string $ signature )
0 commit comments