diff --git a/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php b/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php index e9bad20719b3..8bf5ba858028 100644 --- a/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php +++ b/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php @@ -373,13 +373,17 @@ public function withExceptions(?callable $using = null) \Illuminate\Foundation\Exceptions\Handler::class ); + $configureExceptions = new Exceptions; + if ($using !== null) { - $this->app->afterResolving( - \Illuminate\Foundation\Exceptions\Handler::class, - fn ($handler) => $using(new Exceptions($handler)), - ); + $using($configureExceptions); } + $this->app->afterResolving( + \Illuminate\Foundation\Exceptions\Handler::class, + fn ($handler) => $configureExceptions->handle($handler), + ); + return $this; } diff --git a/src/Illuminate/Foundation/Configuration/Exceptions.php b/src/Illuminate/Foundation/Configuration/Exceptions.php index aa92d688a0d8..8ca7c112ca11 100644 --- a/src/Illuminate/Foundation/Configuration/Exceptions.php +++ b/src/Illuminate/Foundation/Configuration/Exceptions.php @@ -10,13 +10,11 @@ class Exceptions { /** - * Create a new exception handling configuration instance. + * List of configuration callbacks for exception handler. * - * @param \Illuminate\Foundation\Exceptions\Handler $handler + * @var array */ - public function __construct(public Handler $handler) - { - } + protected $bootstrapCallbacks = []; /** * Register a reportable callback. @@ -130,7 +128,7 @@ public function level(string $type, string $level) */ public function context(Closure $contextCallback) { - $this->handler->buildContextUsing($contextCallback); + $this->bootstrapCallbacks[] = fn ($handler) => $handler->buildContextUsing($contextCallback); return $this; } @@ -143,9 +141,11 @@ public function context(Closure $contextCallback) */ public function dontReport(array|string $class) { - foreach (Arr::wrap($class) as $exceptionClass) { - $this->handler->dontReport($exceptionClass); - } + $this->bootstrapCallbacks[] = function ($handler) use ($class) { + foreach (Arr::wrap($class) as $exceptionClass) { + $handler->dontReport($exceptionClass); + } + }; return $this; } @@ -158,7 +158,7 @@ public function dontReport(array|string $class) */ public function dontReportWhen(Closure $dontReportWhen) { - $this->handler->dontReportWhen($dontReportWhen); + $this->bootstrapCallbacks[] = fn ($handler) => $handler->dontReportWhen($dontReportWhen); return $this; } @@ -170,7 +170,7 @@ public function dontReportWhen(Closure $dontReportWhen) */ public function dontReportDuplicates() { - $this->handler->dontReportDuplicates(); + $this->bootstrapCallbacks[] = fn ($handler) => $handler->dontReportDuplicates(); return $this; } @@ -183,7 +183,7 @@ public function dontReportDuplicates() */ public function dontFlash(array|string $attributes) { - $this->handler->dontFlash($attributes); + $this->bootstrapCallbacks[] = fn ($handler) => $handler->dontFlash($attributes); return $this; } @@ -196,7 +196,7 @@ public function dontFlash(array|string $attributes) */ public function shouldRenderJsonWhen(callable $callback) { - $this->handler->shouldRenderJsonWhen($callback); + $this->bootstrapCallbacks[] = fn ($handler) => $handler->shouldRenderJsonWhen($callback); return $this; } @@ -209,7 +209,7 @@ public function shouldRenderJsonWhen(callable $callback) */ public function stopIgnoring(array|string $class) { - $this->handler->stopIgnoring($class); + $this->bootstrapCallbacks[] = fn ($handler) => $handler->stopIgnoring($class); return $this; } @@ -238,4 +238,17 @@ public function dontTruncateRequestExceptions() return $this; } + + /** + * Register the callbacks to exception handler. + * + * @param \Illuminate\Foundation\Exceptions\Handler $handler + * @return void + */ + public function handle(Handler $handler) + { + foreach ($this->bootstrapCallbacks as $callback) { + value($callback, $handler); + } + } } diff --git a/tests/Foundation/Configuration/ExceptionsTest.php b/tests/Foundation/Configuration/ExceptionsTest.php index fc868e6c5f4c..917ba970834a 100644 --- a/tests/Foundation/Configuration/ExceptionsTest.php +++ b/tests/Foundation/Configuration/ExceptionsTest.php @@ -2,51 +2,89 @@ namespace Illuminate\Tests\Foundation\Configuration; +use Closure; use Exception; use Illuminate\Container\Container; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Exceptions\Handler; use Illuminate\Http\Request; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Exception\HttpException; class ExceptionsTest extends TestCase { - public function testStopIgnoring() + #[TestWith([HttpException::class])] + #[TestWith([ModelNotFoundException::class])] + public function testStopIgnoring(string $class) { $container = new Container; - $exceptions = new Exceptions($handler = new class($container) extends Handler + $exceptions = new Exceptions; + + $handler = new class($container) extends Handler { public function getDontReport(): array { return array_merge($this->dontReport, $this->internalDontReport); } - }); + }; + + $this->assertContains($class, $handler->getDontReport()); + + $exceptions = $exceptions->stopIgnoring($class); + + $exceptions->handle($handler); - $this->assertContains(HttpException::class, $handler->getDontReport()); - $exceptions = $exceptions->stopIgnoring(HttpException::class); $this->assertInstanceOf(Exceptions::class, $exceptions); - $this->assertNotContains(HttpException::class, $handler->getDontReport()); + $this->assertNotContains($class, $handler->getDontReport()); + } - $this->assertContains(ModelNotFoundException::class, $handler->getDontReport()); - $exceptions->stopIgnoring([ModelNotFoundException::class]); - $this->assertNotContains(ModelNotFoundException::class, $handler->getDontReport()); + #[TestWith([HttpException::class])] + #[TestWith([ModelNotFoundException::class])] + public function testStopIgnoringUsingArray(string $class) + { + $container = new Container; + $exceptions = new Exceptions; + + $handler = new class($container) extends Handler + { + public function getDontReport(): array + { + return array_merge($this->dontReport, $this->internalDontReport); + } + }; + + $this->assertContains($class, $handler->getDontReport()); + + $exceptions = $exceptions->stopIgnoring([$class]); + + $exceptions->handle($handler); + + $this->assertInstanceOf(Exceptions::class, $exceptions); + $this->assertNotContains($class, $handler->getDontReport()); + } + + public static function shouldRenderJsonDataProvider() + { + yield [null, false]; + yield [fn () => true, true]; + yield [fn () => false, false]; } - public function testShouldRenderJsonWhen() + #[DataProvider('shouldRenderJsonDataProvider')] + public function testShouldRenderJsonWhen(?Closure $given, bool $expects) { - $exceptions = new Exceptions(new Handler(new Container)); + $exceptions = new Exceptions; + $handler = new Handler(new Container); - $shouldReturnJson = (fn () => $this->shouldReturnJson(new Request, new Exception()))->call($exceptions->handler); - $this->assertFalse($shouldReturnJson); + if (! is_null($given)) { + $exceptions->shouldRenderJsonWhen($given); + } - $exceptions->shouldRenderJsonWhen(fn () => true); - $shouldReturnJson = (fn () => $this->shouldReturnJson(new Request, new Exception()))->call($exceptions->handler); - $this->assertTrue($shouldReturnJson); + $exceptions->handle($handler); - $exceptions->shouldRenderJsonWhen(fn () => false); - $shouldReturnJson = (fn () => $this->shouldReturnJson(new Request, new Exception()))->call($exceptions->handler); - $this->assertFalse($shouldReturnJson); + $this->assertSame($expects, (fn () => $this->shouldReturnJson(new Request, new Exception()))->call($handler)); } }