Skip to content

Commit 6114330

Browse files
bert-wcrynobonetaylorotwell
authored
[9.x] Return non-zero exit code for uncaught exceptions (#46541)
* wip * Update MakesArtisanScript.php * artisanScript function * Update MakesArtisanScript.php * styleci * change to PhpExecutableFinder * move `exit(1)` call up to only console applications * styleci * wip Signed-off-by: Mior Muhammad Zaki <[email protected]> * imports * formatting --------- Signed-off-by: Mior Muhammad Zaki <[email protected]> Co-authored-by: Mior Muhammad Zaki <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent 4307b85 commit 6114330

File tree

6 files changed

+126
-1
lines changed

6 files changed

+126
-1
lines changed

src/Illuminate/Foundation/Bootstrap/HandleExceptions.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,15 @@ public function handleException(Throwable $e)
197197
try {
198198
$this->getExceptionHandler()->report($e);
199199
} catch (Exception $e) {
200-
//
200+
$exceptionHandlerFailed = true;
201201
}
202202

203203
if (static::$app->runningInConsole()) {
204204
$this->renderForConsole($e);
205+
206+
if ($exceptionHandlerFailed ?? false) {
207+
exit(1);
208+
}
205209
} else {
206210
$this->renderHttpResponse($e);
207211
}

tests/Integration/Foundation/ExceptionHandlerTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,21 @@
66
use Illuminate\Auth\Access\Response;
77
use Illuminate\Support\Facades\Route;
88
use Orchestra\Testbench\TestCase;
9+
use Symfony\Component\Process\PhpProcess;
910

1011
class ExceptionHandlerTest extends TestCase
1112
{
13+
/**
14+
* Resolve application HTTP exception handler.
15+
*
16+
* @param \Illuminate\Foundation\Application $app
17+
* @return void
18+
*/
19+
protected function resolveApplicationExceptionHandler($app)
20+
{
21+
$app->singleton('Illuminate\Contracts\Debug\ExceptionHandler', 'Illuminate\Foundation\Exceptions\Handler');
22+
}
23+
1224
public function testItRendersAuthorizationExceptions()
1325
{
1426
Route::get('test-route', fn () => Response::deny('expected message', 321)->authorize());
@@ -107,4 +119,36 @@ public function testItHasFallbackErrorMessageForUnknownStatusCodes()
107119
'message' => 'Whoops, looks like something went wrong.',
108120
]);
109121
}
122+
123+
/**
124+
* @dataProvider exitCodesProvider
125+
*/
126+
public function testItReturnsNonZeroExitCodesForUncaughtExceptions($providers, $successful)
127+
{
128+
$basePath = static::applicationBasePath();
129+
$providers = json_encode($providers, true);
130+
131+
$process = new PhpProcess(<<<EOF
132+
<?php
133+
134+
require 'vendor/autoload.php';
135+
136+
\$laravel = Orchestra\Testbench\Foundation\Application::create(basePath: '$basePath', options: ['extra' => ['providers' => $providers]]);
137+
\$laravel->singleton('Illuminate\Contracts\Debug\ExceptionHandler', 'Illuminate\Foundation\Exceptions\Handler');
138+
139+
\$kernel = \$laravel[Illuminate\Contracts\Console\Kernel::class];
140+
141+
return \$kernel->call('throw-exception-command');
142+
EOF, __DIR__.'/../../../', ['APP_RUNNING_IN_CONSOLE' => true]);
143+
144+
$process->run();
145+
146+
$this->assertSame($successful, $process->isSuccessful());
147+
}
148+
149+
public static function exitCodesProvider()
150+
{
151+
yield 'Throw exception' => [[Fixtures\Providers\ThrowUncaughtExceptionServiceProvider::class], false];
152+
yield 'Do not throw exception' => [[Fixtures\Providers\ThrowExceptionServiceProvider::class], true];
153+
}
110154
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Foundation\Fixtures\Console;
4+
5+
use Exception;
6+
use Illuminate\Console\Command;
7+
8+
class ThrowExceptionCommand extends Command
9+
{
10+
protected $signature = 'throw-exception-command';
11+
12+
public function handle()
13+
{
14+
throw new Exception('Thrown inside ThrowExceptionCommand');
15+
}
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Foundation\Fixtures\Logs;
4+
5+
use Exception;
6+
use Monolog\Handler\AbstractProcessingHandler;
7+
8+
class ThrowExceptionLogHandler extends AbstractProcessingHandler
9+
{
10+
protected function write(array $record): void
11+
{
12+
throw new Exception('Thrown inside ThrowExceptionLogHandler');
13+
}
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Foundation\Fixtures\Providers;
4+
5+
use Illuminate\Console\Application;
6+
use Illuminate\Support\ServiceProvider;
7+
use Illuminate\Tests\Integration\Foundation\Fixtures\Console\ThrowExceptionCommand;
8+
9+
class ThrowExceptionServiceProvider extends ServiceProvider
10+
{
11+
public function boot()
12+
{
13+
Application::starting(function ($artisan) {
14+
$artisan->add(new ThrowExceptionCommand);
15+
});
16+
}
17+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Foundation\Fixtures\Providers;
4+
5+
use Illuminate\Console\Application;
6+
use Illuminate\Support\ServiceProvider;
7+
use Illuminate\Tests\Integration\Foundation\Fixtures\Console\ThrowExceptionCommand;
8+
use Illuminate\Tests\Integration\Foundation\Fixtures\Logs\ThrowExceptionLogHandler;
9+
10+
class ThrowUncaughtExceptionServiceProvider extends ServiceProvider
11+
{
12+
public function register()
13+
{
14+
$config = $this->app['config'];
15+
16+
$config->set('logging.default', 'throw_exception');
17+
18+
$config->set('logging.channels.throw_exception', [
19+
'driver' => 'monolog',
20+
'handler' => ThrowExceptionLogHandler::class,
21+
]);
22+
}
23+
24+
public function boot()
25+
{
26+
Application::starting(function ($artisan) {
27+
$artisan->add(new ThrowExceptionCommand);
28+
});
29+
}
30+
}

0 commit comments

Comments
 (0)