-
+

@yield('code') -

+ -
+
@yield('message')
diff --git a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php index 3eccb5d7e85d..0ac742bb9935 100755 --- a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php @@ -70,6 +70,7 @@ use Illuminate\Foundation\Console\PackageDiscoverCommand; use Illuminate\Foundation\Console\PolicyMakeCommand; use Illuminate\Foundation\Console\ProviderMakeCommand; +use Illuminate\Foundation\Console\ReloadCommand; use Illuminate\Foundation\Console\RequestMakeCommand; use Illuminate\Foundation\Console\ResourceMakeCommand; use Illuminate\Foundation\Console\RouteCacheCommand; @@ -97,9 +98,11 @@ use Illuminate\Queue\Console\ListenCommand as QueueListenCommand; use Illuminate\Queue\Console\ListFailedCommand as ListFailedQueueCommand; use Illuminate\Queue\Console\MonitorCommand as QueueMonitorCommand; +use Illuminate\Queue\Console\PauseCommand as QueuePauseCommand; use Illuminate\Queue\Console\PruneBatchesCommand as QueuePruneBatchesCommand; use Illuminate\Queue\Console\PruneFailedJobsCommand as QueuePruneFailedJobsCommand; use Illuminate\Queue\Console\RestartCommand as QueueRestartCommand; +use Illuminate\Queue\Console\ResumeCommand as QueueResumeCommand; use Illuminate\Queue\Console\RetryBatchCommand as QueueRetryBatchCommand; use Illuminate\Queue\Console\RetryCommand as QueueRetryCommand; use Illuminate\Queue\Console\TableCommand; @@ -150,12 +153,15 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid 'QueueForget' => ForgetFailedQueueCommand::class, 'QueueListen' => QueueListenCommand::class, 'QueueMonitor' => QueueMonitorCommand::class, + 'QueuePause' => QueuePauseCommand::class, 'QueuePruneBatches' => QueuePruneBatchesCommand::class, 'QueuePruneFailedJobs' => QueuePruneFailedJobsCommand::class, 'QueueRestart' => QueueRestartCommand::class, + 'QueueResume' => QueueResumeCommand::class, 'QueueRetry' => QueueRetryCommand::class, 'QueueRetryBatch' => QueueRetryBatchCommand::class, 'QueueWork' => QueueWorkCommand::class, + 'Reload' => ReloadCommand::class, 'RouteCache' => RouteCacheCommand::class, 'RouteClear' => RouteClearCommand::class, 'RouteList' => RouteListCommand::class, @@ -252,7 +258,6 @@ public function register() /** * Register the given commands. * - * @param array $commands * @return void */ protected function registerCommands(array $commands) @@ -409,7 +414,7 @@ protected function registerConfigMakeCommand() */ protected function registerConfigPublishCommand() { - $this->app->singleton(ConfigPublishCommand::class, function ($app) { + $this->app->singleton(ConfigPublishCommand::class, function () { return new ConfigPublishCommand; }); } diff --git a/src/Illuminate/Foundation/Testing/CachedState.php b/src/Illuminate/Foundation/Testing/CachedState.php new file mode 100644 index 000000000000..79d333c0d3ef --- /dev/null +++ b/src/Illuminate/Foundation/Testing/CachedState.php @@ -0,0 +1,9 @@ +getConnection($connection), function ($connectionInstance) use ($expected, $connection) { - $actual = 0; - - $connectionInstance->listen(function (QueryExecuted $event) use (&$actual, $connectionInstance, $connection) { - if (is_null($connection) || $connectionInstance === $event->connection) { - $actual++; - } - }); - - $this->beforeApplicationDestroyed(function () use (&$actual, $expected, $connectionInstance) { - $this->assertSame( - $expected, - $actual, - "Expected {$expected} database queries on the [{$connectionInstance->getName()}] connection. {$actual} occurred." - ); - }); + $connectionInstance = $this->getConnection($connection); + + $actual = 0; + + $connectionInstance->listen(function (QueryExecuted $event) use (&$actual, $connectionInstance, $connection) { + if (is_null($connection) || $connectionInstance === $event->connection) { + $actual++; + } + }); + + $this->beforeApplicationDestroyed(function () use (&$actual, $expected, $connectionInstance) { + $this->assertSame( + $expected, + $actual, + "Expected {$expected} database queries on the [{$connectionInstance->getName()}] connection. {$actual} occurred." + ); }); return $this; @@ -285,7 +285,7 @@ public function castAsJson($value, $connection = null) * Get the database connection. * * @param string|null $connection - * @param \Illuminate\Database\Eloquent\Model|class-string<\Illuminate\Database\Eloquent\Model>|string $table + * @param \Illuminate\Database\Eloquent\Model|class-string<\Illuminate\Database\Eloquent\Model>|string|null $table * @return \Illuminate\Database\Connection */ protected function getConnection($connection = null, $table = null) diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php index 0daabf1ce139..860c7a4b47f5 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithTestCaseLifecycle.php @@ -203,7 +203,7 @@ protected function tearDownTheTestEnvironment(): void */ protected function setUpTraits() { - $uses = array_flip(class_uses_recursive(static::class)); + $uses = $this->traitsUsedByTest ?? array_flip(class_uses_recursive(static::class)); if (isset($uses[RefreshDatabase::class])) { $this->refreshDatabase(); diff --git a/src/Illuminate/Foundation/Testing/RefreshDatabase.php b/src/Illuminate/Foundation/Testing/RefreshDatabase.php index f039c510f8c2..28626ec5e706 100644 --- a/src/Illuminate/Foundation/Testing/RefreshDatabase.php +++ b/src/Illuminate/Foundation/Testing/RefreshDatabase.php @@ -85,12 +85,30 @@ protected function refreshTestDatabase() $this->app[Kernel::class]->setArtisan(null); + $this->updateLocalCacheOfInMemoryDatabases(); + RefreshDatabaseState::$migrated = true; } $this->beginDatabaseTransaction(); } + /** + * Update locally cached in-memory PDO connections after migration. + * + * @return void + */ + protected function updateLocalCacheOfInMemoryDatabases() + { + $database = $this->app->make('db'); + + foreach ($this->connectionsToTransact() as $name) { + if ($this->usingInMemoryDatabase($name)) { + RefreshDatabaseState::$inMemoryConnections[$name] = $database->connection($name)->getPdo(); + } + } + } + /** * Migrate the database. * diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 1d7a17df84ce..0b5acfd37662 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -20,6 +20,13 @@ abstract class TestCase extends BaseTestCase Concerns\InteractsWithTestCaseLifecycle, Concerns\InteractsWithViews; + /** + * The list of trait that this test uses, fetched recursively. + * + * @var array + */ + protected array $traitsUsedByTest; + /** * Creates the application. * @@ -29,6 +36,18 @@ public function createApplication() { $app = require Application::inferBasePath().'/bootstrap/app.php'; + $this->traitsUsedByTest = array_flip(class_uses_recursive(static::class)); + + if (isset(CachedState::$cachedConfig) && + isset($this->traitsUsedByTest[WithCachedConfig::class])) { + $this->markConfigCached($app); + } + + if (isset(CachedState::$cachedRoutes) && + isset($this->traitsUsedByTest[WithCachedRoutes::class])) { + $app->booting(fn () => $this->markRoutesCached($app)); + } + $app->make(Kernel::class)->bootstrap(); return $app; diff --git a/src/Illuminate/Foundation/Testing/WithCachedConfig.php b/src/Illuminate/Foundation/Testing/WithCachedConfig.php new file mode 100644 index 000000000000..e1ed65a8da7d --- /dev/null +++ b/src/Illuminate/Foundation/Testing/WithCachedConfig.php @@ -0,0 +1,41 @@ +app->make('config')->all(); + } + + $this->markConfigCached($this->app); + } + + /** + * Reset the cached configuration. + * + * This is helpful if some of the tests in the suite apply this trait while others do not. + */ + protected function tearDownWithCachedConfig(): void + { + LoadConfiguration::alwaysUse(null); + } + + /** + * Inform the container that the configuration is cached. + */ + protected function markConfigCached(Application $app): void + { + $app->instance('config_loaded_from_cache', true); + + LoadConfiguration::alwaysUse(static fn () => CachedState::$cachedConfig); + } +} diff --git a/src/Illuminate/Foundation/Testing/WithCachedRoutes.php b/src/Illuminate/Foundation/Testing/WithCachedRoutes.php new file mode 100644 index 000000000000..1a02a370b7a2 --- /dev/null +++ b/src/Illuminate/Foundation/Testing/WithCachedRoutes.php @@ -0,0 +1,52 @@ +app['router']->getRoutes(); + + $routes->refreshNameLookups(); + $routes->refreshActionLookups(); + + CachedState::$cachedRoutes = $routes->compile(); + } + + $this->markRoutesCached($this->app); + } + + /** + * Reset the route service provider so it's not defaulting to loading cached routes. + * + * This is helpful if some of the tests in the suite apply this trait while others do not. + * + * @return void + */ + protected function tearDownWithCachedRoutes(): void + { + RouteServiceProvider::loadCachedRoutesUsing(null); + } + + /** + * Inform the container to treat routes as cached. + */ + protected function markRoutesCached(Application $app): void + { + $app->instance('routes.cached', true); + + RouteServiceProvider::loadCachedRoutesUsing( + static fn () => app('router')->setCompiledRoutes(CachedState::$cachedRoutes) + ); + } +} diff --git a/src/Illuminate/Foundation/Vite.php b/src/Illuminate/Foundation/Vite.php index 81278816abb8..b348992c20f2 100644 --- a/src/Illuminate/Foundation/Vite.php +++ b/src/Illuminate/Foundation/Vite.php @@ -897,7 +897,7 @@ public function content($asset, $buildDirectory = null) $chunk = $this->chunk($this->manifest($buildDirectory), $asset); - $path = public_path($buildDirectory.'/'.$chunk['file']); + $path = $this->publicPath($buildDirectory.'/'.$chunk['file']); if (! is_file($path) || ! file_exists($path)) { throw new ViteException("Unable to locate file from Vite manifest: {$path}."); @@ -918,6 +918,17 @@ protected function assetPath($path, $secure = null) return ($this->assetPathResolver ?? asset(...))($path, $secure); } + /** + * Generate a public path for an asset. + * + * @param string $path + * @return string + */ + protected function publicPath($path) + { + return public_path($path); + } + /** * Get the manifest file for the given build directory. * diff --git a/src/Illuminate/Foundation/helpers.php b/src/Illuminate/Foundation/helpers.php index 9757182626f2..94ca5f84924b 100644 --- a/src/Illuminate/Foundation/helpers.php +++ b/src/Illuminate/Foundation/helpers.php @@ -560,9 +560,9 @@ function lang_path($path = ''): string * Log a debug message to the logs. * * @param string|null $message - * @return ($message is null ? \Illuminate\Log\LogManager : null) + * @return ($message is null ? \Psr\Log\LoggerInterface : null) */ - function logger($message = null, array $context = []): ?LogManager + function logger($message = null, array $context = []): ?LoggerInterface { if (is_null($message)) { return app('log'); @@ -1090,7 +1090,7 @@ function validator(?array $data = null, array $rules = [], array $messages = [], * @param array $mergeData * @return ($view is null ? \Illuminate\Contracts\View\Factory : \Illuminate\Contracts\View\View) */ - function view($view = null, $data = [], $mergeData = []): ViewContract|ViewFactory + function view($view = null, $data = [], $mergeData = []): ViewFactory|ViewContract { $factory = app(ViewFactory::class); diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/badge.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/badge.blade.php new file mode 100644 index 000000000000..5fedfcf12535 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/badge.blade.php @@ -0,0 +1,47 @@ +@props(['type' => 'default', 'variant' => 'soft']) + +@php +$baseClasses = 'inline-flex w-fit shrink-0 items-center justify-center gap-1 font-mono leading-3 uppercase transition-colors dark:border [&_svg]:size-2.5 h-6 min-w-5 rounded-md px-1.5 text-xs/none'; + +$types = [ + 'default' => [ + 'soft' => 'bg-black/8 text-neutral-900 dark:border-neutral-700 dark:bg-white/10 dark:text-neutral-100', + 'solid' => 'bg-neutral-600 text-neutral-100 dark:border-neutral-500 dark:bg-neutral-600', + ], + 'success' => [ + 'soft' => 'bg-emerald-200 text-emerald-900 dark:border-emerald-600 dark:bg-emerald-900/70 dark:text-emerald-400', + 'solid' => 'bg-emerald-600 dark:border-emerald-500 dark:bg-emerald-600', + ], + 'primary' => [ + 'soft' => 'bg-blue-100 text-blue-900 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-300', + 'solid' => 'bg-blue-700 dark:border-blue-600 dark:bg-blue-700', + ], + 'error' => [ + 'soft' => 'bg-rose-200 text-rose-900 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-100 dark:[&_svg]:!text-white', + 'solid' => 'bg-rose-600 dark:border-rose-500 dark:bg-rose-600', + ], + 'alert' => [ + 'soft' => 'bg-amber-200 text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-300', + 'solid' => 'bg-amber-600 dark:border-amber-500 dark:bg-amber-600', + ], + 'white' => [ + 'soft' => 'bg-white text-neutral-900 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-100', + 'solid' => 'bg-black/10 text-neutral-900 dark:text-neutral-900 dark:bg-white', + ], +]; + +$variants = [ + 'soft' => '', + 'solid' => 'text-white dark:text-white [&_svg]:!text-white', +]; + +$typeClasses = $types[$type][$variant] ?? $types['default']['soft']; +$variantClasses = $variants[$variant] ?? $variants['soft']; + +$classes = implode(' ', [$baseClasses, $typeClasses, $variantClasses]); + +@endphp + +
merge(['class' => $classes]) }}> + {{ $slot }} +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php deleted file mode 100644 index 14dcd4f51426..000000000000 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/components/card.blade.php +++ /dev/null @@ -1,5 +0,0 @@ -
merge(['class' => "@container flex flex-col p-6 sm:p-12 bg-white dark:bg-gray-900/80 text-gray-900 dark:text-gray-100 rounded-lg default:col-span-full default:lg:col-span-6 default:row-span-1 dark:ring-1 dark:ring-gray-800 shadow-xl"]) }} -> - {{ $slot }} -
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/context.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/context.blade.php deleted file mode 100644 index 13b922a865e6..000000000000 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/components/context.blade.php +++ /dev/null @@ -1,148 +0,0 @@ -@use('Illuminate\Support\Str') - -
- Request -
- -
- {{ $exception->request()->method() }} - {{ Str::start($exception->request()->path(), '/') }} -
- -
- Headers -
- -
- @forelse ($exception->requestHeaders() as $key => $value) -
- - {{ $key }} - - -
{{ $value }}
-
-
- @empty - -
No headers data
-
- @endforelse -
- -
- Body -
- -
-
- -
{{ $exception->requestBody() ?: 'No body data' }}
-
-
-
- -
- - -
- Application -
- -
- Routing -
- -
- @forelse ($exception->applicationRouteContext() as $name => $value) -
- {{ $name }} - -
{{ $value }}
-
-
- @empty - -
No routing data
-
- @endforelse -
- - @if ($routeParametersContext = $exception->applicationRouteParametersContext()) -
- Routing Parameters -
- -
-
- -
{{ $routeParametersContext }}
-
-
-
- @endif - -
- Database Queries - - @if (count($exception->applicationQueries()) === 100) - only the first 100 queries are displayed - @endif - -
- -
- @forelse ($exception->applicationQueries() as ['connectionName' => $connectionName, 'sql' => $sql, 'time' => $time]) -
-
- {{ $connectionName }} - -
- -
{{ $sql }}
-
-
- @empty - -
No query data
-
- @endforelse -
-
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/copy-button.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/copy-button.blade.php deleted file mode 100644 index 115872f4278e..000000000000 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/components/copy-button.blade.php +++ /dev/null @@ -1,25 +0,0 @@ - -
- -
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/editor.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/editor.blade.php deleted file mode 100644 index fbb8b38cb006..000000000000 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/components/editor.blade.php +++ /dev/null @@ -1,29 +0,0 @@ -@foreach ($exception->frames() as $frame) -
-
-
-
- @if (config('app.editor')) - - {{ $frame->file() }}:{{ $frame->line() }} - - @else - {{ $frame->file() }}:{{ $frame->line() }} - @endif -
-
-
-
-
-
-
-@endforeach diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/empty-state.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/empty-state.blade.php new file mode 100644 index 000000000000..9a41f78a8f1b --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/empty-state.blade.php @@ -0,0 +1,5 @@ +@props(['message']) + +
+ // {{ $message }} +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/file-with-line.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/file-with-line.blade.php new file mode 100644 index 000000000000..99dd9f4a493b --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/file-with-line.blade.php @@ -0,0 +1,21 @@ +@props(['frame', 'direction' => 'ltr']) + +@php + $file = $frame->file(); + $line = $frame->line(); +@endphp + +
merge(['class' => 'truncate font-mono text-xs text-neutral-500 dark:text-neutral-400']) }} + dir="{{ $direction }}" +> + + @if (config('app.editor')) + + {{ $file }}:{{ $line }} + + @else + {{ $file }}:{{ $line }} + @endif + +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/formatted-source.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/formatted-source.blade.php new file mode 100644 index 000000000000..e6364fc9eb05 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/formatted-source.blade.php @@ -0,0 +1,23 @@ +@props(['frame']) + +@php + if ($class = $frame->class()) { + $source = $class; + + if ($previous = $frame->previous()) { + $source .= $previous->operator(); + $source .= $previous->callable(); + $source .= '('.implode(', ', $previous->args()).')'; + } + } else { + $source = $frame->source(); + } +@endphp + + diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/frame-code.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/frame-code.blade.php new file mode 100644 index 000000000000..23b022062825 --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/frame-code.blade.php @@ -0,0 +1,15 @@ +@props(['code', 'highlightedLine']) + +
+ +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/frame.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/frame.blade.php new file mode 100644 index 000000000000..406c246aebdb --- /dev/null +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/frame.blade.php @@ -0,0 +1,55 @@ +@props(['frame']) + +
+
+ {{-- Dot --}} +
+
+
+ +
+ + +
+ +
+ +
+
+ + @if($snippet = $frame->snippet()) + + @endif +
diff --git a/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php b/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php index 4da473e71d90..ffb0c9a9a212 100644 --- a/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php +++ b/src/Illuminate/Foundation/resources/exceptions/renderer/components/header.blade.php @@ -1,28 +1,33 @@ - -
-
-
- - - {{ implode(' ', array_slice(explode('\\', $exception->class()), -1)) }} - -
-
- {{ $exception->message() }} -
-
+@props(['exception']) -