Skip to content

Commit 2e46d00

Browse files
authored
Merge pull request #80 from adhocore/79-exception-handler
2 parents 6aa9556 + cb9e2b3 commit 2e46d00

File tree

3 files changed

+54
-9
lines changed

3 files changed

+54
-9
lines changed

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ Framework agnostic Command Line Interface utilities and helpers for PHP. Build C
88
[![Codecov branch](https://img.shields.io/codecov/c/github/adhocore/php-cli/main.svg?style=flat-square)](https://codecov.io/gh/adhocore/php-cli)
99
[![StyleCI](https://styleci.io/repos/139012552/shield)](https://styleci.io/repos/139012552)
1010
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
11-
[![Donate 15](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square&label=donate+15)](https://www.paypal.me/ji10/15usd)
12-
[![Donate 25](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square&label=donate+25)](https://www.paypal.me/ji10/25usd)
13-
[![Donate 50](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square&label=donate+50)](https://www.paypal.me/ji10/50usd)
1411
[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Framework+agnostic+Command+Line+Interface+utilities+and+helpers+for+PHP&url=https://github.com/adhocore/php-cli&hashtags=php,cli,cliapp,console)
12+
[![Support](https://img.shields.io/static/v1?label=Support&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/adhocore)
13+
<!-- [![Donate 15](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square&label=donate+15)](https://www.paypal.me/ji10/15usd)
14+
[![Donate 25](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square&label=donate+25)](https://www.paypal.me/ji10/25usd)
15+
[![Donate 50](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square&label=donate+50)](https://www.paypal.me/ji10/50usd) -->
1516

1617

1718
- Command line application made easy
@@ -252,6 +253,25 @@ $app->add((new ConfigListCommand)->inGroup('Config'));
252253
...
253254
```
254255

256+
#### Exception handler
257+
258+
Set a custom exception handler as callback. The callback receives exception & exit code. The callback may rethrow exception or may exit the program or just log exception and do nothing else.
259+
260+
```php
261+
$app = new Ahc\Cli\Application('App', 'v0.0.1');
262+
$app->add(...);
263+
$app->onException(function (Throwable $e, int $exitCode) {
264+
// send to sentry
265+
// write to logs
266+
267+
// optionally, exit with exit code:
268+
exit($exitCode);
269+
270+
// or optionally rethrow, a rethrown exception is propagated to top layer caller.
271+
throw $e;
272+
})->handle($argv);
273+
```
274+
255275
#### App help
256276

257277
It can be triggered manually with `$app->showHelp()` or automatic when `-h` or `--help` option is passed to `$app->parse()`.

src/Application.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class Application
5959
/** @var callable The callable to perform exit */
6060
protected $onExit;
6161

62+
/** @var callable The callable to catch exception, receives exception & exit code, may rethrow exception or may exit program */
63+
protected $onException = null;
64+
6265
public function __construct(protected string $name, protected string $version = '0.0.1', callable $onExit = null)
6366
{
6467
$this->onExit = $onExit ?? static fn (int $exitCode = 0) => exit($exitCode);
@@ -123,7 +126,7 @@ public function logo(string $logo = null)
123126
}
124127

125128
/**
126-
* Add a command by its name desc alias etc.
129+
* Add a command by its name desc alias etc and return command.
127130
*/
128131
public function command(
129132
string $name,
@@ -140,7 +143,7 @@ public function command(
140143
}
141144

142145
/**
143-
* Add a prepred command.
146+
* Add a prepared command and return itself.
144147
*/
145148
public function add(Command $command, string $alias = '', bool $default = false): self
146149
{
@@ -256,6 +259,19 @@ public function parse(array $argv): Command
256259
return $command->parse($argv);
257260
}
258261

262+
/**
263+
* Sets exception handler callback.
264+
*
265+
* The callback receives exception & exit code. It may rethrow exception
266+
* or may exit the program or just log exception and do nothing else.
267+
*/
268+
public function onException(callable $fn): self
269+
{
270+
$this->onException = $fn;
271+
272+
return $this;
273+
}
274+
259275
/**
260276
* Handle the request, invoke action and call exit handler.
261277
*/
@@ -266,12 +282,12 @@ public function handle(array $argv): mixed
266282
}
267283

268284
$exitCode = 255;
269-
270285
try {
271286
$command = $this->parse($argv);
272287
$result = $this->doAction($command);
273288
$exitCode = is_int($result) ? $result : 0;
274289
} catch (Throwable $e) {
290+
isset($this->onException) && ($this->onException)($e, $exitCode);
275291
$this->outputHelper()->printTrace($e);
276292
}
277293

tests/ApplicationTest.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Ahc\Cli\IO\Interactor;
1717
use InvalidArgumentException;
1818
use PHPUnit\Framework\TestCase;
19+
use Throwable;
1920

2021
class ApplicationTest extends TestCase
2122
{
@@ -288,11 +289,19 @@ public function test_io_returns_new_instance_if_not_provided(): void
288289
);
289290
}
290291

292+
public function test_on_exception()
293+
{
294+
$this->expectException(InvalidArgumentException::class);
295+
$this->expectExceptionMessage($msg = 'this will be rethrown and propagated');
296+
297+
$cmd = (new Command('cmd'))->action(fn () => throw new InvalidArgumentException($msg));
298+
$app = $this->newApp('test')->add($cmd)->onException(fn (Throwable $e) => throw $e);
299+
$app->handle(['test', 'cmd']);
300+
}
301+
291302
protected function newApp(string $name, string $version = '')
292303
{
293-
$app = new Application($name, $version ?: '0.0.1', function () {
294-
return false;
295-
});
304+
$app = new Application($name, $version ?: '0.0.1', fn () => false);
296305

297306
return $app->io(new Interactor(static::$in, static::$ou));
298307
}

0 commit comments

Comments
 (0)