Skip to content

Commit ad027d0

Browse files
committed
feat: add support for command events
1 parent b183a55 commit ad027d0

File tree

2 files changed

+233
-1
lines changed

2 files changed

+233
-1
lines changed

src/Sprout/App.php

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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)

src/Sprout/Event.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Leaf\Sprout;
6+
7+
class Event
8+
{
9+
protected $name;
10+
protected $data;
11+
protected $propagationStopped = false;
12+
protected $exitCode = 0;
13+
14+
public function __construct(string $name, array $data = [])
15+
{
16+
$this->name = $name;
17+
$this->data = $data;
18+
}
19+
20+
/**
21+
* Get the event name
22+
*/
23+
public function getName(): string
24+
{
25+
return $this->name;
26+
}
27+
28+
/**
29+
* Get the command name from event data
30+
*/
31+
public function getCommandName(): ?string
32+
{
33+
return $this->data['commandName'] ?? null;
34+
}
35+
36+
/**
37+
* Get all event data
38+
*/
39+
public function getData(): array
40+
{
41+
return $this->data;
42+
}
43+
44+
/**
45+
* Get a specific piece of event data
46+
*/
47+
public function get(string $key, $default = null)
48+
{
49+
return $this->data[$key] ?? $default;
50+
}
51+
52+
/**
53+
* Set event data
54+
*/
55+
public function set(string $key, $value): void
56+
{
57+
$this->data[$key] = $value;
58+
}
59+
60+
/**
61+
* Set the exit code
62+
*/
63+
public function setExitCode(int $exitCode): void
64+
{
65+
$this->exitCode = $exitCode;
66+
}
67+
68+
/**
69+
* Get the exit code
70+
*/
71+
public function getExitCode(): int
72+
{
73+
return $this->exitCode;
74+
}
75+
76+
/**
77+
* Stop event propagation
78+
*/
79+
public function stopPropagation(): void
80+
{
81+
$this->propagationStopped = true;
82+
}
83+
84+
/**
85+
* Check if propagation is stopped
86+
*/
87+
public function isPropagationStopped(): bool
88+
{
89+
return $this->propagationStopped;
90+
}
91+
92+
/**
93+
* Get command arguments
94+
*/
95+
public function getArguments(): array
96+
{
97+
return $this->data['arguments'] ?? [];
98+
}
99+
100+
/**
101+
* Get command parameters/options
102+
*/
103+
public function getParams(): array
104+
{
105+
return $this->data['params'] ?? [];
106+
}
107+
108+
/**
109+
* Get the raw command data
110+
*/
111+
public function getCommandData(): array
112+
{
113+
return $this->data['commandData'] ?? [];
114+
}
115+
}

0 commit comments

Comments
 (0)