diff --git a/config/octane.php b/config/octane.php index 8cfba0114..0343672cd 100644 --- a/config/octane.php +++ b/config/octane.php @@ -71,8 +71,6 @@ ], RequestReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - ...Octane::prepareApplicationForNextRequest(), // ], @@ -85,7 +83,6 @@ ], TaskReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), // ], @@ -94,7 +91,6 @@ ], TickReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), // ], diff --git a/src/ApplicationGateway.php b/src/ApplicationGateway.php index 21dd42841..4ee957944 100644 --- a/src/ApplicationGateway.php +++ b/src/ApplicationGateway.php @@ -2,7 +2,6 @@ namespace Laravel\Octane; -use Illuminate\Contracts\Http\Kernel; use Illuminate\Foundation\Application; use Illuminate\Http\Request; use Illuminate\Routing\Route; @@ -16,7 +15,7 @@ class ApplicationGateway { use DispatchesEvents; - public function __construct(protected Application $app, protected Application $sandbox) + public function __construct(protected ApplicationResetter $resetter, protected Application $snapshot, protected Application $sandbox) { } @@ -27,13 +26,17 @@ public function handle(Request $request): Response { $request->enableHttpMethodParameterOverride(); - $this->dispatchEvent($this->sandbox, new RequestReceived($this->app, $this->sandbox, $request)); + // reset all default services + $this->resetter->prepareApplicationForNextRequest($request); + $this->resetter->prepareApplicationForNextOperation(); + + $this->dispatchEvent($this->sandbox, new RequestReceived($this->snapshot, $this->sandbox, $request)); if (Octane::hasRouteFor($request->getMethod(), '/'.$request->path())) { return Octane::invokeRoute($request, $request->getMethod(), '/'.$request->path()); } - return tap($this->sandbox->make(Kernel::class)->handle($request), function ($response) use ($request) { + return tap($this->resetter->kernel->handle($request), function ($response) use ($request) { $this->dispatchEvent($this->sandbox, new RequestHandled($this->sandbox, $request, $response)); }); } @@ -43,9 +46,9 @@ public function handle(Request $request): Response */ public function terminate(Request $request, Response $response): void { - $this->sandbox->make(Kernel::class)->terminate($request, $response); + $this->resetter->kernel->terminate($request, $response); - $this->dispatchEvent($this->sandbox, new RequestTerminated($this->app, $this->sandbox, $request, $response)); + $this->dispatchEvent($this->sandbox, new RequestTerminated($this->snapshot, $this->sandbox, $request, $response)); $route = $request->route(); diff --git a/src/ApplicationResetter.php b/src/ApplicationResetter.php new file mode 100644 index 000000000..f1d1c78f3 --- /dev/null +++ b/src/ApplicationResetter.php @@ -0,0 +1,298 @@ +sandbox = $sandbox; + $this->snapshot = $snapshot; + $this->config = $snapshot['config']; + $this->url = $snapshot['url']; + $this->octaneHttps = $this->config->get('octane.https'); + $this->initialAppLocale = $this->config->get('app.locale'); + $this->initialAppFallbackLocale = $this->config->get('app.fallback_locale'); + $this->kernel = $this->snapshot->make(Kernel::class); + + if ($this->config->get('cache.stores.array')) { + $this->arrayCache = $this->snapshot->make('cache')->store('array'); + } + + if ($snapshot->resolved('log')) { + $this->logDefaultDriver = $snapshot->make('log')->driver(); + } + + if ($snapshot->resolved('auth.driver')) { + $snapshot->forgetInstance('auth.driver'); + } + } + + public function prepareApplicationForNextRequest(Request $request): void + { + $this->flushLocaleState(); + $this->flushCookies(); + $this->flushSession(); + $this->flushAuthenticationState(); + $this->enforceRequestScheme($request); + $this->ensureRequestServerPortMatchesScheme($request); + $this->giveNewRequestInstanceToApplication($request); + } + + public function prepareApplicationForNextOperation(): void + { + $this->createConfigurationSandbox(); + $this->createUrlGeneratorSandbox(); + $this->giveApplicationInstanceToMailManager(); + $this->giveApplicationInstanceToNotificationChannelManager(); + $this->flushDatabaseState(); + $this->flushLogContext(); + $this->flushMonologState(); + $this->flushArrayCache(); + Str::flushCache(); + $this->flushTranslatorCache(); + $this->forgetViewEngines(); + + // First-Party Packages... + $this->prepareInertiaForNextOperation(); + $this->prepareLivewireForNextOperation(); + $this->prepareScoutForNextOperation(); + $this->prepareSocialiteForNextOperation(); + } + + private function flushLocaleState(): void + { + // resetting the locale is an expensive operation + // only do it if the locale has changed + if (Carbon::getLocale() !== $this->initialAppLocale) { + (new CarbonServiceProvider($this->sandbox))->updateLocale(); + } + } + + private function flushCookies() + { + $this->snapshot->resetInitialInstance('cookie', function ($cookie) { + $cookie->flushQueuedCookies(); + }); + } + + private function flushSession(): void + { + $this->snapshot->resetInitialInstance('session', function ($session) { + $driver = $session->driver(); + $driver->flush(); + $driver->regenerate(); + }); + } + + private function flushAuthenticationState(): void + { + $this->snapshot->resetInitialInstance('auth', function ($auth) { + $auth->forgetGuards(); + }); + } + + private function enforceRequestScheme(Request $request): void + { + if (! $this->octaneHttps) { + return; + } + + $this->snapshot->resetInitialInstance('url', function ($url) use ($request) { + $url->forceScheme('https'); + $request->server->set('HTTPS', 'on'); + }); + } + + private function ensureRequestServerPortMatchesScheme(Request $request): void + { + $port = $request->getPort(); + + if (is_null($port) || $port === '') { + $request->server->set( + 'SERVER_PORT', + $request->getScheme() === 'https' ? 443 : 80 + ); + } + } + + private function giveNewRequestInstanceToApplication(Request $request): void + { + $this->sandbox->instance('request', $request); + } + + private function createConfigurationSandbox(): void + { + $this->sandbox->instance('config', clone $this->config); + } + + private function createUrlGeneratorSandbox(): void + { + $this->sandbox->instance('url', clone $this->url); + } + + private function giveApplicationInstanceToMailManager(): void + { + $this->snapshot->resetInitialInstance('mail.manager', function ($mailManager) { + $mailManager->forgetMailers(); + }); + } + + private function giveApplicationInstanceToNotificationChannelManager(): void + { + $this->snapshot->resetInitialInstance(ChannelManager::class, function ($channelManager) { + $channelManager->forgetDrivers(); + }); + } + + private function flushDatabaseState(): void + { + $this->snapshot->resetInitialInstance('db', function ($db) { + foreach ($db->getConnections() as $connection) { + $connection->forgetRecordModificationState(); + $connection->flushQueryLog(); + + // refresh query duration handling + if ( + method_exists($connection, 'resetTotalQueryDuration') + && method_exists($connection, 'allowQueryDurationHandlersToRunAgain') + ) { + $connection->resetTotalQueryDuration(); + $connection->allowQueryDurationHandlersToRunAgain(); + } + } + }); + } + + private function flushLogContext(): void + { + $this->snapshot->resetInitialInstance('log', function ($log) { + if (method_exists($log, 'flushSharedContext')) { + $log->flushSharedContext(); + } + + if (method_exists($this->logDefaultDriver, 'withoutContext')) { + $this->logDefaultDriver->withoutContext(); + } + + if (method_exists($log, 'withoutContext')) { + $log->withoutContext(); + } + }); + } + + private function flushArrayCache(): void + { + if ($this->arrayCache) { + $this->arrayCache->flush(); + } + } + + private function flushMonologState(): void + { + $this->snapshot->resetInitialInstance('log', function ($log) { + foreach ($log->getChannels() as $channel) { + $logger = $channel->getLogger(); + if ($logger instanceof ResettableInterface) { + $logger->reset(); + } + } + }); + } + + private function flushTranslatorCache(): void + { + $this->snapshot->resetInitialInstance('translator', function ($translator) { + $translator->setLocale($this->initialAppLocale); + $translator->setFallback($this->initialAppFallbackLocale); + + if ($translator instanceof NamespacedItemResolver) { + $translator->flushParsedKeys(); + } + }); + } + + private function prepareInertiaForNextOperation(): void + { + $this->snapshot->resetInitialInstance(ResponseFactory::class, function ($responseFactory) { + if (method_exists($responseFactory, 'flushShared')) { + $responseFactory->flushShared(); + } + }); + } + + private function prepareLivewireForNextOperation(): void + { + $this->snapshot->resetInitialInstance(LivewireManager::class, function ($livewireManager) { + if (method_exists($livewireManager, 'flushState')) { + $livewireManager->flushState(); + } + }); + } + + private function prepareScoutForNextOperation(): void + { + $this->snapshot->resetInitialInstance(EngineManager::class, function ($engineManager) { + if (method_exists($engineManager, 'forgetEngines')) { + $engineManager->forgetEngines(); + } + }); + } + + private function prepareSocialiteForNextOperation(): void + { + $this->snapshot->resetInitialInstance(Factory::class, function ($socialiteFactory) { + if (! method_exists($socialiteFactory, 'forgetDrivers')) { + return; + } + + $socialiteFactory->forgetDrivers(); + }); + } + + private function forgetViewEngines(): void + { + $this->snapshot->resetInitialInstance('view.engine.resolver', function ($resolver) { + $resolver->forget('blade'); + $resolver->forget('php'); + }); + } +} diff --git a/src/ApplicationSnapshot.php b/src/ApplicationSnapshot.php new file mode 100644 index 000000000..ace87de41 --- /dev/null +++ b/src/ApplicationSnapshot.php @@ -0,0 +1,38 @@ + $value) { + $snapshot->$key = $value; + } + Container::setInstance($previousInstance); + + return $snapshot; + } + + public function loadSnapshotInto(Application $app): void + { + foreach (get_object_vars($this) as $key => $value) { + $app->$key = $value; + } + Facade::clearResolvedInstances(); + } + + public function resetInitialInstance(string $abstract, callable $callback): void + { + if (! array_key_exists($abstract, $this->resolved)) { + return; + } + $callback($this->instances[$abstract] ?? $this->bindings[$abstract]['concrete']()); + } +} diff --git a/src/Concerns/ProvidesDefaultConfigurationOptions.php b/src/Concerns/ProvidesDefaultConfigurationOptions.php index 36ebe4101..f11dc5819 100644 --- a/src/Concerns/ProvidesDefaultConfigurationOptions.php +++ b/src/Concerns/ProvidesDefaultConfigurationOptions.php @@ -5,61 +5,19 @@ trait ProvidesDefaultConfigurationOptions { /** - * Get the listeners that will prepare the Laravel application for a new request. + * This method is kept for BC reasons. */ public static function prepareApplicationForNextRequest(): array { - return [ - \Laravel\Octane\Listeners\FlushLocaleState::class, - \Laravel\Octane\Listeners\FlushQueuedCookies::class, - \Laravel\Octane\Listeners\FlushSessionState::class, - \Laravel\Octane\Listeners\FlushAuthenticationState::class, - \Laravel\Octane\Listeners\EnforceRequestScheme::class, - \Laravel\Octane\Listeners\EnsureRequestServerPortMatchesScheme::class, - \Laravel\Octane\Listeners\GiveNewRequestInstanceToApplication::class, - \Laravel\Octane\Listeners\GiveNewRequestInstanceToPaginator::class, - ]; + return []; } /** - * Get the listeners that will prepare the Laravel application for a new operation. + * This method is kept for BC reasons. */ public static function prepareApplicationForNextOperation(): array { - return [ - \Laravel\Octane\Listeners\CreateConfigurationSandbox::class, - \Laravel\Octane\Listeners\CreateUrlGeneratorSandbox::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToAuthorizationGate::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToBroadcastManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToDatabaseManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToDatabaseSessionHandler::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToFilesystemManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToHttpKernel::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToLogManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToMailManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToNotificationChannelManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToPipelineHub::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToCacheManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToSessionManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToQueueManager::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToRouter::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToValidationFactory::class, - \Laravel\Octane\Listeners\GiveNewApplicationInstanceToViewFactory::class, - \Laravel\Octane\Listeners\FlushDatabaseRecordModificationState::class, - \Laravel\Octane\Listeners\FlushDatabaseQueryLog::class, - \Laravel\Octane\Listeners\RefreshQueryDurationHandling::class, - \Laravel\Octane\Listeners\FlushLogContext::class, - \Laravel\Octane\Listeners\FlushArrayCache::class, - \Laravel\Octane\Listeners\FlushMonologState::class, - \Laravel\Octane\Listeners\FlushStrCache::class, - \Laravel\Octane\Listeners\FlushTranslatorCache::class, - - // First-Party Packages... - \Laravel\Octane\Listeners\PrepareInertiaForNextOperation::class, - \Laravel\Octane\Listeners\PrepareLivewireForNextOperation::class, - \Laravel\Octane\Listeners\PrepareScoutForNextOperation::class, - \Laravel\Octane\Listeners\PrepareSocialiteForNextOperation::class, - ]; + return []; } /** diff --git a/src/CurrentApplication.php b/src/CurrentApplication.php deleted file mode 100644 index 5a46635e8..000000000 --- a/src/CurrentApplication.php +++ /dev/null @@ -1,24 +0,0 @@ -instance('app', $app); - $app->instance(Container::class, $app); - - Container::setInstance($app); - - Facade::clearResolvedInstances(); - Facade::setFacadeApplication($app); - } -} diff --git a/src/Listeners/CreateConfigurationSandbox.php b/src/Listeners/CreateConfigurationSandbox.php deleted file mode 100644 index 2289bbf4c..000000000 --- a/src/Listeners/CreateConfigurationSandbox.php +++ /dev/null @@ -1,16 +0,0 @@ -sandbox->instance('config', clone $event->sandbox['config']); - } -} diff --git a/src/Listeners/CreateUrlGeneratorSandbox.php b/src/Listeners/CreateUrlGeneratorSandbox.php deleted file mode 100644 index 88cc0ac1c..000000000 --- a/src/Listeners/CreateUrlGeneratorSandbox.php +++ /dev/null @@ -1,16 +0,0 @@ -sandbox->instance('url', clone $event->sandbox['url']); - } -} diff --git a/src/Listeners/EnforceRequestScheme.php b/src/Listeners/EnforceRequestScheme.php deleted file mode 100644 index b6dc4c6c2..000000000 --- a/src/Listeners/EnforceRequestScheme.php +++ /dev/null @@ -1,22 +0,0 @@ -sandbox->make('config')->get('octane.https')) { - return; - } - - $event->sandbox->make('url')->forceScheme('https'); - - $event->request->server->set('HTTPS', 'on'); - } -} diff --git a/src/Listeners/EnsureRequestServerPortMatchesScheme.php b/src/Listeners/EnsureRequestServerPortMatchesScheme.php deleted file mode 100644 index b7150dc64..000000000 --- a/src/Listeners/EnsureRequestServerPortMatchesScheme.php +++ /dev/null @@ -1,23 +0,0 @@ -request->getPort(); - - if (is_null($port) || $port === '') { - $event->request->server->set( - 'SERVER_PORT', - $event->request->getScheme() === 'https' ? 443 : 80 - ); - } - } -} diff --git a/src/Listeners/FlushArrayCache.php b/src/Listeners/FlushArrayCache.php deleted file mode 100644 index a01d2768a..000000000 --- a/src/Listeners/FlushArrayCache.php +++ /dev/null @@ -1,18 +0,0 @@ -sandbox->make('cache')->store('array')->flush(); - } - } -} diff --git a/src/Listeners/FlushAuthenticationState.php b/src/Listeners/FlushAuthenticationState.php deleted file mode 100644 index fca8e1872..000000000 --- a/src/Listeners/FlushAuthenticationState.php +++ /dev/null @@ -1,25 +0,0 @@ -sandbox->resolved('auth.driver')) { - $event->sandbox->forgetInstance('auth.driver'); - } - - if ($event->sandbox->resolved('auth')) { - with($event->sandbox->make('auth'), function ($auth) use ($event) { - $auth->setApplication($event->sandbox); - $auth->forgetGuards(); - }); - } - } -} diff --git a/src/Listeners/FlushDatabaseQueryLog.php b/src/Listeners/FlushDatabaseQueryLog.php deleted file mode 100644 index 5a003fc6e..000000000 --- a/src/Listeners/FlushDatabaseQueryLog.php +++ /dev/null @@ -1,22 +0,0 @@ -sandbox->resolved('db')) { - return; - } - - foreach ($event->sandbox->make('db')->getConnections() as $connection) { - $connection->flushQueryLog(); - } - } -} diff --git a/src/Listeners/FlushDatabaseRecordModificationState.php b/src/Listeners/FlushDatabaseRecordModificationState.php deleted file mode 100644 index bd1bad76e..000000000 --- a/src/Listeners/FlushDatabaseRecordModificationState.php +++ /dev/null @@ -1,22 +0,0 @@ -sandbox->resolved('db')) { - return; - } - - foreach ($event->sandbox->make('db')->getConnections() as $connection) { - $connection->forgetRecordModificationState(); - } - } -} diff --git a/src/Listeners/FlushLocaleState.php b/src/Listeners/FlushLocaleState.php deleted file mode 100644 index c2864709b..000000000 --- a/src/Listeners/FlushLocaleState.php +++ /dev/null @@ -1,29 +0,0 @@ -sandbox->make('config'); - - tap($event->sandbox->make('translator'), function ($translator) use ($config) { - $translator->setLocale($config->get('app.locale')); - $translator->setFallback($config->get('app.fallback_locale')); - }); - - $provider = tap(new CarbonServiceProvider($event->app))->updateLocale(); - - collect($event->sandbox->getProviders($provider)) - ->values() - ->whenNotEmpty(fn ($providers) => $providers->first()->setAppGetter(fn () => $event->sandbox)); - } -} diff --git a/src/Listeners/FlushLogContext.php b/src/Listeners/FlushLogContext.php deleted file mode 100644 index 8161e7d16..000000000 --- a/src/Listeners/FlushLogContext.php +++ /dev/null @@ -1,30 +0,0 @@ -sandbox->resolved('log')) { - return; - } - - if (method_exists($event->sandbox['log'], 'flushSharedContext')) { - $event->sandbox['log']->flushSharedContext(); - } - - if (method_exists($event->sandbox['log']->driver(), 'withoutContext')) { - $event->sandbox['log']->withoutContext(); - } - - if (method_exists($event->sandbox['log'], 'withoutContext')) { - $event->sandbox['log']->withoutContext(); - } - } -} diff --git a/src/Listeners/FlushMonologState.php b/src/Listeners/FlushMonologState.php deleted file mode 100644 index 09eaaf397..000000000 --- a/src/Listeners/FlushMonologState.php +++ /dev/null @@ -1,26 +0,0 @@ -sandbox->resolved('log')) { - return; - } - - collect($event->sandbox->make('log')->getChannels()) - ->map->getLogger() - ->filter(function ($logger) { - return $logger instanceof ResettableInterface; - })->each->reset(); - } -} diff --git a/src/Listeners/FlushQueuedCookies.php b/src/Listeners/FlushQueuedCookies.php deleted file mode 100644 index 080327d17..000000000 --- a/src/Listeners/FlushQueuedCookies.php +++ /dev/null @@ -1,20 +0,0 @@ -sandbox->resolved('cookie')) { - return; - } - - $event->sandbox->make('cookie')->flushQueuedCookies(); - } -} diff --git a/src/Listeners/FlushSessionState.php b/src/Listeners/FlushSessionState.php deleted file mode 100644 index 650404211..000000000 --- a/src/Listeners/FlushSessionState.php +++ /dev/null @@ -1,23 +0,0 @@ -sandbox->resolved('session')) { - return; - } - - $driver = $event->sandbox->make('session')->driver(); - - $driver->flush(); - $driver->regenerate(); - } -} diff --git a/src/Listeners/FlushStrCache.php b/src/Listeners/FlushStrCache.php deleted file mode 100644 index caa232a47..000000000 --- a/src/Listeners/FlushStrCache.php +++ /dev/null @@ -1,19 +0,0 @@ -app->forgetScopedInstances(); } - foreach ($event->sandbox->make('config')->get('octane.flush', []) as $binding) { + foreach ($event->app->make('config')->get('octane.flush', []) as $binding) { $event->app->forgetInstance($binding); } } diff --git a/src/Listeners/FlushTranslatorCache.php b/src/Listeners/FlushTranslatorCache.php deleted file mode 100644 index db0c14f95..000000000 --- a/src/Listeners/FlushTranslatorCache.php +++ /dev/null @@ -1,27 +0,0 @@ -sandbox->resolved('translator')) { - return; - } - - $translator = $event->sandbox->make('translator'); - - if ($translator instanceof NamespacedItemResolver) { - $translator->flushParsedKeys(); - } - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToAuthorizationGate.php b/src/Listeners/GiveNewApplicationInstanceToAuthorizationGate.php deleted file mode 100644 index 51afadcf1..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToAuthorizationGate.php +++ /dev/null @@ -1,22 +0,0 @@ -sandbox->resolved(Gate::class)) { - return; - } - - $event->sandbox->make(Gate::class)->setContainer($event->sandbox); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToBroadcastManager.php b/src/Listeners/GiveNewApplicationInstanceToBroadcastManager.php deleted file mode 100644 index ce977953b..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToBroadcastManager.php +++ /dev/null @@ -1,27 +0,0 @@ -sandbox->resolved(BroadcastManager::class)) { - return; - } - - with($event->sandbox->make(BroadcastManager::class), function ($manager) use ($event) { - $manager->setApplication($event->sandbox); - - // Forgetting drivers will flush all channel routes which is unwanted... - // $manager->forgetDrivers(); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToCacheManager.php b/src/Listeners/GiveNewApplicationInstanceToCacheManager.php deleted file mode 100644 index d5f91faf9..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToCacheManager.php +++ /dev/null @@ -1,24 +0,0 @@ -sandbox->resolved('cache')) { - return; - } - - with($event->sandbox->make('cache'), function ($manager) use ($event) { - if (method_exists($manager, 'setApplication')) { - $manager->setApplication($event->sandbox); - } - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToDatabaseManager.php b/src/Listeners/GiveNewApplicationInstanceToDatabaseManager.php deleted file mode 100644 index dfcaa1a8d..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToDatabaseManager.php +++ /dev/null @@ -1,23 +0,0 @@ -sandbox->resolved('db') || - ! method_exists($event->sandbox->make('db'), 'setApplication')) { - return; - } - - with($event->sandbox->make('db'), function ($manager) use ($event) { - $manager->setApplication($event->sandbox); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToDatabaseSessionHandler.php b/src/Listeners/GiveNewApplicationInstanceToDatabaseSessionHandler.php deleted file mode 100644 index 085d9cf0f..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToDatabaseSessionHandler.php +++ /dev/null @@ -1,29 +0,0 @@ -sandbox->resolved('session')) { - return; - } - - $handler = $event->sandbox->make('session')->driver()->getHandler(); - - if (! $handler instanceof DatabaseSessionHandler || - ! method_exists($handler, 'setContainer')) { - return; - } - - $handler->setContainer($event->sandbox); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToFilesystemManager.php b/src/Listeners/GiveNewApplicationInstanceToFilesystemManager.php deleted file mode 100644 index 2d4ff0963..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToFilesystemManager.php +++ /dev/null @@ -1,22 +0,0 @@ -sandbox->resolved('filesystem')) { - return; - } - - with($event->sandbox->make('filesystem'), function ($manager) use ($event) { - $manager->setApplication($event->sandbox); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToHttpKernel.php b/src/Listeners/GiveNewApplicationInstanceToHttpKernel.php deleted file mode 100644 index 0efec06e4..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToHttpKernel.php +++ /dev/null @@ -1,18 +0,0 @@ -sandbox->make(Kernel::class)->setApplication($event->sandbox); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToMailManager.php b/src/Listeners/GiveNewApplicationInstanceToMailManager.php deleted file mode 100644 index 466c1ece2..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToMailManager.php +++ /dev/null @@ -1,23 +0,0 @@ -sandbox->resolved('mail.manager')) { - return; - } - - with($event->sandbox->make('mail.manager'), function ($manager) use ($event) { - $manager->setApplication($event->sandbox); - $manager->forgetMailers(); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToNotificationChannelManager.php b/src/Listeners/GiveNewApplicationInstanceToNotificationChannelManager.php deleted file mode 100644 index 175ef23db..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToNotificationChannelManager.php +++ /dev/null @@ -1,25 +0,0 @@ -sandbox->resolved(ChannelManager::class)) { - return; - } - - with($event->sandbox->make(ChannelManager::class), function ($manager) use ($event) { - $manager->setContainer($event->sandbox); - $manager->forgetDrivers(); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToPipelineHub.php b/src/Listeners/GiveNewApplicationInstanceToPipelineHub.php deleted file mode 100644 index 499b4621c..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToPipelineHub.php +++ /dev/null @@ -1,24 +0,0 @@ -sandbox->resolved(Hub::class)) { - return; - } - - with($event->sandbox->make(Hub::class), function ($hub) use ($event) { - $hub->setContainer($event->sandbox); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToQueueManager.php b/src/Listeners/GiveNewApplicationInstanceToQueueManager.php deleted file mode 100644 index a5b2fb283..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToQueueManager.php +++ /dev/null @@ -1,22 +0,0 @@ -sandbox->resolved('queue')) { - return; - } - - with($event->sandbox->make('queue'), function ($manager) use ($event) { - $manager->setApplication($event->sandbox); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToRouter.php b/src/Listeners/GiveNewApplicationInstanceToRouter.php deleted file mode 100644 index 34ad8ad97..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToRouter.php +++ /dev/null @@ -1,28 +0,0 @@ -sandbox->resolved('router')) { - return; - } - - $event->sandbox->make('router')->setContainer($event->sandbox); - - if ($event->sandbox->resolved('routes') && $event->sandbox->make('routes') instanceof RouteCollection) { - foreach ($event->sandbox->make('routes') as $route) { - $route->setContainer($event->sandbox); - } - } - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToSessionManager.php b/src/Listeners/GiveNewApplicationInstanceToSessionManager.php deleted file mode 100644 index b687afcf0..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToSessionManager.php +++ /dev/null @@ -1,16 +0,0 @@ -sandbox->resolved('validator')) { - return; - } - - with($event->sandbox->make('validator'), function ($factory) use ($event) { - $factory->setContainer($event->sandbox); - }); - } -} diff --git a/src/Listeners/GiveNewApplicationInstanceToViewFactory.php b/src/Listeners/GiveNewApplicationInstanceToViewFactory.php deleted file mode 100644 index 6fd386183..000000000 --- a/src/Listeners/GiveNewApplicationInstanceToViewFactory.php +++ /dev/null @@ -1,24 +0,0 @@ -sandbox->resolved('view')) { - return; - } - - with($event->sandbox->make('view'), function ($view) use ($event) { - $view->setContainer($event->sandbox); - - $view->share('app', $event->sandbox); - }); - } -} diff --git a/src/Listeners/GiveNewRequestInstanceToApplication.php b/src/Listeners/GiveNewRequestInstanceToApplication.php deleted file mode 100644 index 962e1542a..000000000 --- a/src/Listeners/GiveNewRequestInstanceToApplication.php +++ /dev/null @@ -1,17 +0,0 @@ -app->instance('request', $event->request); - $event->sandbox->instance('request', $event->request); - } -} diff --git a/src/Listeners/GiveNewRequestInstanceToPaginator.php b/src/Listeners/GiveNewRequestInstanceToPaginator.php deleted file mode 100644 index 6e7b03964..000000000 --- a/src/Listeners/GiveNewRequestInstanceToPaginator.php +++ /dev/null @@ -1,18 +0,0 @@ -sandbox); - } -} diff --git a/src/Listeners/PrepareInertiaForNextOperation.php b/src/Listeners/PrepareInertiaForNextOperation.php deleted file mode 100644 index c14a86e41..000000000 --- a/src/Listeners/PrepareInertiaForNextOperation.php +++ /dev/null @@ -1,26 +0,0 @@ -sandbox->resolved(ResponseFactory::class)) { - return; - } - - $factory = $event->sandbox->make(ResponseFactory::class); - - if (method_exists($factory, 'flushShared')) { - $factory->flushShared(); - } - } -} diff --git a/src/Listeners/PrepareLivewireForNextOperation.php b/src/Listeners/PrepareLivewireForNextOperation.php deleted file mode 100644 index 4cf7ec1a1..000000000 --- a/src/Listeners/PrepareLivewireForNextOperation.php +++ /dev/null @@ -1,26 +0,0 @@ -sandbox->resolved(LivewireManager::class)) { - return; - } - - $manager = $event->sandbox->make(LivewireManager::class); - - if (method_exists($manager, 'flushState')) { - $manager->flushState(); - } - } -} diff --git a/src/Listeners/PrepareScoutForNextOperation.php b/src/Listeners/PrepareScoutForNextOperation.php deleted file mode 100644 index 1a6132a47..000000000 --- a/src/Listeners/PrepareScoutForNextOperation.php +++ /dev/null @@ -1,28 +0,0 @@ -sandbox->resolved(EngineManager::class)) { - return; - } - - $factory = $event->sandbox->make(EngineManager::class); - - if (! method_exists($factory, 'forgetEngines')) { - return; - } - - $factory->forgetEngines(); - } -} diff --git a/src/Listeners/PrepareSocialiteForNextOperation.php b/src/Listeners/PrepareSocialiteForNextOperation.php deleted file mode 100644 index 1d7f48eb9..000000000 --- a/src/Listeners/PrepareSocialiteForNextOperation.php +++ /dev/null @@ -1,29 +0,0 @@ -sandbox->resolved(Factory::class)) { - return; - } - - $factory = $event->sandbox->make(Factory::class); - - if (! method_exists($factory, 'forgetDrivers')) { - return; - } - - $factory->forgetDrivers(); - $factory->setContainer($event->sandbox); - } -} diff --git a/src/Listeners/RefreshQueryDurationHandling.php b/src/Listeners/RefreshQueryDurationHandling.php deleted file mode 100644 index 8bb4e481e..000000000 --- a/src/Listeners/RefreshQueryDurationHandling.php +++ /dev/null @@ -1,28 +0,0 @@ -sandbox->resolved('db')) { - return; - } - - foreach ($event->sandbox->make('db')->getConnections() as $connection) { - if ( - method_exists($connection, 'resetTotalQueryDuration') - && method_exists($connection, 'allowQueryDurationHandlersToRunAgain') - ) { - $connection->resetTotalQueryDuration(); - $connection->allowQueryDurationHandlersToRunAgain(); - } - } - } -} diff --git a/src/OctaneServiceProvider.php b/src/OctaneServiceProvider.php index 206e32768..db042ebcd 100644 --- a/src/OctaneServiceProvider.php +++ b/src/OctaneServiceProvider.php @@ -122,34 +122,10 @@ public function boot() protected function bindListeners() { $this->app->singleton(Listeners\CollectGarbage::class); - $this->app->singleton(Listeners\CreateConfigurationSandbox::class); - $this->app->singleton(Listeners\CreateUrlGeneratorSandbox::class); $this->app->singleton(Listeners\DisconnectFromDatabases::class); - $this->app->singleton(Listeners\EnforceRequestScheme::class); - $this->app->singleton(Listeners\EnsureRequestServerPortMatchesScheme::class); $this->app->singleton(Listeners\EnsureUploadedFilesAreValid::class); $this->app->singleton(Listeners\EnsureUploadedFilesCanBeMoved::class); - $this->app->singleton(Listeners\FlushAuthenticationState::class); - $this->app->singleton(Listeners\FlushQueuedCookies::class); - $this->app->singleton(Listeners\FlushSessionState::class); $this->app->singleton(Listeners\FlushTemporaryContainerInstances::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToAuthorizationGate::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToBroadcastManager::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToHttpKernel::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToLogManager::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToMailManager::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToNotificationChannelManager::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToPipelineHub::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToQueueManager::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToRouter::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToValidationFactory::class); - $this->app->singleton(Listeners\GiveNewApplicationInstanceToViewFactory::class); - $this->app->singleton(Listeners\GiveNewRequestInstanceToApplication::class); - $this->app->singleton(Listeners\GiveNewRequestInstanceToPaginator::class); - $this->app->singleton(Listeners\PrepareInertiaForNextOperation::class); - $this->app->singleton(Listeners\PrepareLivewireForNextOperation::class); - $this->app->singleton(Listeners\PrepareScoutForNextOperation::class); - $this->app->singleton(Listeners\PrepareSocialiteForNextOperation::class); $this->app->singleton(Listeners\ReportException::class); $this->app->singleton(Listeners\StopWorkerIfNecessary::class); } diff --git a/src/Worker.php b/src/Worker.php index 0a87ca4b1..84a705b36 100644 --- a/src/Worker.php +++ b/src/Worker.php @@ -27,11 +27,20 @@ class Worker implements WorkerContract protected $requestHandledCallbacks = []; /** - * The root application instance. + * The current application instance. * - * @var \Illuminate\Foundation\Application + * @var Application */ - protected $app; + protected $sandbox; + + /** + * A clone of the warmed up initial application. + * + * @var ApplicationSnapshot + */ + protected $appSnapshot; + + protected ApplicationResetter $appResetter; public function __construct( protected ApplicationFactory $appFactory, @@ -47,7 +56,7 @@ public function boot(array $initialInstances = []): void // First we will create an instance of the Laravel application that can serve as // the base container instance we will clone from on every request. This will // also perform the initial bootstrapping that's required by the framework. - $this->app = $app = $this->appFactory->createApplication( + $this->sandbox = $app = $this->appFactory->createApplication( array_merge( $initialInstances, [Client::class => $this->client], @@ -72,9 +81,9 @@ public function handle(Request $request, RequestContext $context): void // We will clone the application instance so that we have a clean copy to switch // back to once the request has been handled. This allows us to easily delete // certain instances that got resolved / mutated during a previous request. - CurrentApplication::set($sandbox = clone $this->app); + $this->createAppSnapshot(); - $gateway = new ApplicationGateway($this->app, $sandbox); + $gateway = new ApplicationGateway($this->appResetter, $this->appSnapshot, $this->sandbox); try { $responded = false; @@ -99,23 +108,16 @@ public function handle(Request $request, RequestContext $context): void $responded = true; - $this->invokeRequestHandledCallbacks($request, $response, $sandbox); + $this->invokeRequestHandledCallbacks($request, $response, $this->sandbox); $gateway->terminate($request, $response); } catch (Throwable $e) { - $this->handleWorkerError($e, $sandbox, $request, $context, $responded); + $this->handleWorkerError($e, $this->sandbox, $request, $context, $responded); } finally { - $sandbox->flush(); - - $this->app->make('view.engine.resolver')->forget('blade'); - $this->app->make('view.engine.resolver')->forget('php'); - // After the request handling process has completed we will unset some variables // plus reset the current application state back to its original state before // it was cloned. Then we will be ready for the next worker iteration loop. - unset($gateway, $sandbox, $context, $request, $response, $octaneResponse, $output); - - CurrentApplication::set($this->app); + unset($gateway, $request, $response, $octaneResponse, $output); } } @@ -132,27 +134,21 @@ public function handleTask($data) // We will clone the application instance so that we have a clean copy to switch // back to once the request has been handled. This allows us to easily delete // certain instances that got resolved / mutated during a previous request. - CurrentApplication::set($sandbox = clone $this->app); + $this->createAppSnapshot(); try { - $this->dispatchEvent($sandbox, new TaskReceived($this->app, $sandbox, $data)); + $this->appResetter->prepareApplicationForNextOperation(); + $this->dispatchEvent($this->sandbox, new TaskReceived($this->appSnapshot, $this->sandbox, $data)); $result = $data(); - $this->dispatchEvent($sandbox, new TaskTerminated($this->app, $sandbox, $data, $result)); + $this->dispatchEvent($this->sandbox, new TaskTerminated($this->appSnapshot, $this->sandbox, $data, $result)); } catch (Throwable $e) { - $this->dispatchEvent($sandbox, new WorkerErrorOccurred($e, $sandbox)); + $this->dispatchEvent($this->sandbox, new WorkerErrorOccurred($e, $this->sandbox)); return TaskExceptionResult::from($e); } finally { - $sandbox->flush(); - - // After the request handling process has completed we will unset some variables - // plus reset the current application state back to its original state before - // it was cloned. Then we will be ready for the next worker iteration loop. - unset($sandbox); - - CurrentApplication::set($this->app); + $this->sandbox->flush(); } return new TaskResult($result); @@ -163,19 +159,16 @@ public function handleTask($data) */ public function handleTick(): void { - CurrentApplication::set($sandbox = clone $this->app); + $this->createAppSnapshot(); try { - $this->dispatchEvent($sandbox, new TickReceived($this->app, $sandbox)); - $this->dispatchEvent($sandbox, new TickTerminated($this->app, $sandbox)); + $this->appResetter->prepareApplicationForNextOperation(); + $this->dispatchEvent($this->sandbox, new TickReceived($this->appSnapshot, $this->sandbox)); + $this->dispatchEvent($this->sandbox, new TickTerminated($this->appSnapshot, $this->sandbox)); } catch (Throwable $e) { - $this->dispatchEvent($sandbox, new WorkerErrorOccurred($e, $sandbox)); + $this->dispatchEvent($this->sandbox, new WorkerErrorOccurred($e, $this->sandbox)); } finally { - $sandbox->flush(); - - unset($sandbox); - - CurrentApplication::set($this->app); + $this->sandbox->flush(); } } @@ -227,11 +220,11 @@ public function onRequestHandled(Closure $callback) */ public function application(): Application { - if (! $this->app) { + if (! $this->sandbox) { throw new RuntimeException('Worker has not booted. Unable to access application.'); } - return $this->app; + return $this->sandbox; } /** @@ -239,6 +232,15 @@ public function application(): Application */ public function terminate(): void { - $this->dispatchEvent($this->app, new WorkerStopping($this->app)); + $this->dispatchEvent($this->sandbox, new WorkerStopping($this->sandbox)); + } + + protected function createAppSnapshot(): void + { + if (! isset($this->appSnapshot)) { + $this->appSnapshot = ApplicationSnapshot::createSnapshotFrom($this->sandbox); + $this->appResetter = new ApplicationResetter($this->appSnapshot, $this->sandbox); + } + $this->appSnapshot->loadSnapshotInto($this->sandbox); } } diff --git a/tests/ApplicationSnapshotTest.php b/tests/ApplicationSnapshotTest.php new file mode 100644 index 000000000..c59bb6c23 --- /dev/null +++ b/tests/ApplicationSnapshotTest.php @@ -0,0 +1,91 @@ +assertSame($application, Container::getInstance()); + } + + public function test_forget_a_binding_after_reloading_from_snapshot() + { + $application = new Application; + $snapshot = ApplicationSnapshot::createSnapshotFrom($application); + $application->bind('generic', fn () => new GenericObject($application)); + + $snapshot->loadSnapshotInto($application); + + $this->assertFalse($application->bound('generic')); + } + + public function test_forget_a_value_set_from_array_access_after_reloading_from_snapshot() + { + $application = new Application; + $snapshot = ApplicationSnapshot::createSnapshotFrom($application); + $application['key'] = 'value'; + + $snapshot->loadSnapshotInto($application); + + $this->assertFalse($application->bound('key')); + } + + public function test_forget_a_singleton_after_reloading_from_snapshot() + { + $application = new Application; + $snapshot = ApplicationSnapshot::createSnapshotFrom($application); + $application->singleton(GenericObject::class); + + $snapshot->loadSnapshotInto($application); + + $this->assertFalse($application->bound(GenericObject::class)); + } + + public function test_forget_an_instance_after_reloading_from_snapshot() + { + $application = new Application; + $snapshot = ApplicationSnapshot::createSnapshotFrom($application); + $application->instance(GenericObject::class, new GenericObject($application)); + + $snapshot->loadSnapshotInto($application); + + $this->assertFalse($application->bound(GenericObject::class)); + } + + public function test_reset_the_base_path_after_reloading_from_snapshot() + { + $application = new Application; + $application->setBasePath('initial/path'); + $snapshot = ApplicationSnapshot::createSnapshotFrom($application); + $application->setBasePath('changed/path'); + + $snapshot->loadSnapshotInto($application); + + $this->assertSame('initial/path', $application->basePath()); + } + + /** + * This test verifies that there are no private properties in the Application and Container classes. + * This is important because the snapshotting mechanism relies on the ability to access all properties. + */ + public function test_the_application_container_has_no_private_properties() + { + $appReflection = new \ReflectionClass(Application::class); + $containerReflection = new \ReflectionClass(Container::class); + + $privatePropsInApp = $appReflection->getProperties(\ReflectionProperty::IS_PRIVATE); + $privatePropsInContainer = $containerReflection->getProperties(\ReflectionProperty::IS_PRIVATE); + + $this->assertSame(0, count($privatePropsInApp)); + $this->assertSame(0, count($privatePropsInContainer)); + } +} diff --git a/tests/BindingStateTest.php b/tests/BindingStateTest.php index 04503fd81..8e4ceb2f7 100644 --- a/tests/BindingStateTest.php +++ b/tests/BindingStateTest.php @@ -7,7 +7,7 @@ class BindingStateTest extends TestCase { - public function test_container_instances_given_to_dependencies_can_be_stale_if_an_old_instance_is_given() + public function test_container_instances_given_to_dependencies_will_always_be_fresh() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -24,59 +24,13 @@ public function test_container_instances_given_to_dependencies_can_be_stale_if_a $worker->run(); - $this->assertNotEquals( - $client->responses[0]->original['app'], - $client->responses[0]->original['state'] - ); - } - - public function test_container_instances_given_to_dependencies_will_be_fresh_if_injected_container_is_used() - { - [$app, $worker, $client] = $this->createOctaneContext([ - Request::create('/first', 'GET'), - ]); - - $app->bind(GenericObject::class, fn ($app) => new GenericObject($app)); - - $app['router']->get('/first', function (Application $app) { - return [ - 'app' => spl_object_hash($app), - 'state' => spl_object_hash($app->make(GenericObject::class)->state), - ]; - }); - - $worker->run(); - $this->assertEquals( $client->responses[0]->original['app'], $client->responses[0]->original['state'] ); } - public function test_container_instances_given_to_dependencies_will_be_fresh_if_singleton_but_not_resolved_before_boot() - { - [$app, $worker, $client] = $this->createOctaneContext([ - Request::create('/first', 'GET'), - ]); - - $app->singleton(GenericObject::class, fn ($app) => new GenericObject($app)); - - $app['router']->get('/first', function (Application $app) { - return [ - 'app' => spl_object_hash($app), - 'state' => spl_object_hash($app->make(GenericObject::class)->state), - ]; - }); - - $worker->run(); - - $this->assertEquals( - $client->responses[0]->original['app'], - $client->responses[0]->original['state'] - ); - } - - public function test_container_instances_given_to_dependencies_will_be_stale_if_singleton_and_resolved_during_boot() + public function test_container_instances_given_to_dependencies_will_be_fresh_if_singleton_and_resolved_during_boot() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -95,7 +49,7 @@ public function test_container_instances_given_to_dependencies_will_be_stale_if_ $worker->run(); - $this->assertNotEquals( + $this->assertEquals( $client->responses[0]->original['app'], $client->responses[0]->original['state'] ); diff --git a/tests/BroadcastManagerStateTest.php b/tests/BroadcastManagerStateTest.php index d25d599fc..6b9b3555c 100644 --- a/tests/BroadcastManagerStateTest.php +++ b/tests/BroadcastManagerStateTest.php @@ -8,7 +8,7 @@ class BroadcastManagerStateTest extends TestCase { - public function test_broadcast_manager_has_fresh_application_instance() + public function test_broadcast_manager_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first?name=Taylor', 'GET'), @@ -29,6 +29,6 @@ public function test_broadcast_manager_has_fresh_application_instance() $this->assertEquals('Taylor', $client->responses[0]->original['name']); $this->assertEquals('Abigail', $client->responses[1]->original['name']); - $this->assertNotEquals($client->responses[0]->original['hash'], $client->responses[1]->original['hash']); + $this->assertEquals($client->responses[0]->original['hash'], $client->responses[1]->original['hash']); } } diff --git a/tests/CommandTest.php b/tests/CommandTest.php index 84a8d4914..f07ac655c 100644 --- a/tests/CommandTest.php +++ b/tests/CommandTest.php @@ -13,7 +13,7 @@ public function test_info() $command->info('foo'); - $this->assertEquals(<<<'EOF' + $this->assertStringEqualsStringIgnoringLineEndings(<<<'EOF' INFO foo @@ -26,7 +26,7 @@ public function test_error() $command->error('bar'); - $this->assertEquals(<<<'EOF' + $this->assertStringEqualsStringIgnoringLineEndings(<<<'EOF' ERROR bar @@ -39,7 +39,7 @@ public function test_warn() $command->warn('beta period'); - $this->assertEquals(<<<'EOF' + $this->assertStringEqualsStringIgnoringLineEndings(<<<'EOF' WARN beta period @@ -74,7 +74,7 @@ public function test_request() 'duration' => 4567854, ]); - $this->assertEquals(<<<'EOF' + $this->assertStringEqualsStringIgnoringLineEndings(<<<'EOF' 200 GET /welcome ......... 16.59 mb 10.00 ms 404 POST / ............. 19.45 mb 1234.00 ms 500 POST /foofoofo... 28.99 mb 4567854.00 ms diff --git a/tests/FilesystemManagerStateTest.php b/tests/FilesystemManagerStateTest.php index a22989dc0..b2c4393fb 100644 --- a/tests/FilesystemManagerStateTest.php +++ b/tests/FilesystemManagerStateTest.php @@ -8,7 +8,7 @@ class FilesystemManagerStateTest extends TestCase { - public function test_filesystem_manager_has_fresh_application_instance() + public function test_filesystem_manager_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -24,6 +24,6 @@ public function test_filesystem_manager_has_fresh_application_instance() $worker->run(); - $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + $this->assertEquals($client->responses[0]->original, $client->responses[1]->original); } } diff --git a/tests/Listeners/FlushStrCacheTest.php b/tests/Listeners/FlushStrCacheTest.php index 6293e6dff..e4912e4e7 100644 --- a/tests/Listeners/FlushStrCacheTest.php +++ b/tests/Listeners/FlushStrCacheTest.php @@ -11,8 +11,6 @@ class FlushStrCacheTest extends TestCase { public function test_str_is_flushed() { - Str::flushCache(); - [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/test-str-cache', 'GET'), Request::create('/', 'GET'), @@ -30,6 +28,9 @@ public function test_str_is_flushed() $property = $reflection->getProperty('snakeCache'); $property->setAccessible(true); + // make sure the cache is empty before the request + Str::flushCache(); + $this->assertEmpty($property->getValue()); $worker->run(); diff --git a/tests/Listeners/GiveNewApplicationInstanceToNotificationChannelManagerTest.php b/tests/Listeners/GiveNewApplicationInstanceToNotificationChannelManagerTest.php new file mode 100644 index 000000000..8e5a966a5 --- /dev/null +++ b/tests/Listeners/GiveNewApplicationInstanceToNotificationChannelManagerTest.php @@ -0,0 +1,28 @@ +createOctaneContext([ + Request::create('/first', 'GET'), + ]); + $app->register(NotificationServiceProvider::class); + $channelManager = $app->make(ChannelManager::class); + $channelManager->driver(); + $initialDriverCount = count($channelManager->getDrivers()); + $app['router']->get('/first', fn () => 'Hello World'); + + $worker->run(); + + $this->assertSame(1, $initialDriverCount); + $this->assertSame(0, count($channelManager->getDrivers())); + } +} diff --git a/tests/MailManagerStateTest.php b/tests/MailManagerStateTest.php index d5c1a00a4..6ab708f57 100644 --- a/tests/MailManagerStateTest.php +++ b/tests/MailManagerStateTest.php @@ -7,7 +7,7 @@ class MailManagerStateTest extends TestCase { - public function test_mail_manager_has_fresh_application_instance() + public function test_mail_manager_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -22,6 +22,6 @@ public function test_mail_manager_has_fresh_application_instance() $worker->run(); - $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + $this->assertEquals($client->responses[0]->original, $client->responses[1]->original); } } diff --git a/tests/NotificationChannelManagerStateTest.php b/tests/NotificationChannelManagerStateTest.php index c9ff0bad0..5cbb0b32a 100644 --- a/tests/NotificationChannelManagerStateTest.php +++ b/tests/NotificationChannelManagerStateTest.php @@ -8,7 +8,7 @@ class NotificationChannelManagerStateTest extends TestCase { - public function test_notification_channel_manager_has_fresh_application_instance() + public function test_notification_channel_manager_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -23,6 +23,6 @@ public function test_notification_channel_manager_has_fresh_application_instance $worker->run(); - $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + $this->assertEquals($client->responses[0]->original, $client->responses[1]->original); } } diff --git a/tests/PipelineHubStateTest.php b/tests/PipelineHubStateTest.php index f0572c211..2e7583da2 100644 --- a/tests/PipelineHubStateTest.php +++ b/tests/PipelineHubStateTest.php @@ -8,7 +8,7 @@ class PipelineHubStateTest extends TestCase { - public function test_pipeline_hub_has_fresh_application_instance() + public function test_pipeline_hub_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -23,6 +23,6 @@ public function test_pipeline_hub_has_fresh_application_instance() $worker->run(); - $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + $this->assertEquals($client->responses[0]->original, $client->responses[1]->original); } } diff --git a/tests/QueueManagerStateTest.php b/tests/QueueManagerStateTest.php index 227425ddc..ff4e0a3bb 100644 --- a/tests/QueueManagerStateTest.php +++ b/tests/QueueManagerStateTest.php @@ -7,7 +7,7 @@ class QueueManagerStateTest extends TestCase { - public function test_queue_manager_has_fresh_application_instance() + public function test_queue_manager_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -22,6 +22,6 @@ public function test_queue_manager_has_fresh_application_instance() $worker->run(); - $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + $this->assertEquals($client->responses[0]->original, $client->responses[1]->original); } } diff --git a/tests/RequestStateTest.php b/tests/RequestStateTest.php index 72d373bd7..e2c244d0f 100644 --- a/tests/RequestStateTest.php +++ b/tests/RequestStateTest.php @@ -48,7 +48,7 @@ public function test_form_requests_contain_the_correct_state_across_subsequent_r $this->assertEquals('Taylor', $client->responses[0]->original['name']); $this->assertEquals('Abigail', $client->responses[1]->original['name']); - $this->assertNotEquals($client->responses[0]->original['container'], $client->responses[1]->original['container']); + $this->assertEquals($client->responses[0]->original['container'], $client->responses[1]->original['container']); } public function test_request_routes_flush_controller_state() diff --git a/tests/TemporaryContainerInstanceFlushingTest.php b/tests/TemporaryContainerInstanceFlushingTest.php index 9ebb47a8e..be02f264f 100644 --- a/tests/TemporaryContainerInstanceFlushingTest.php +++ b/tests/TemporaryContainerInstanceFlushingTest.php @@ -34,4 +34,27 @@ public function test_temporary_container_bindings_are_flushed() $client->responses[1]->original ); } + + public function test_scoped_container_instances_should_be_flushed() + { + [$app, $worker, $client] = $this->createOctaneContext([ + Request::create('/first', 'GET'), + Request::create('/first', 'GET'), + ]); + + $app->scoped('random-string', fn () => Str::random(10)); + + $app['random-string']; + + $app['router']->get('/first', function (Application $app) { + return $app['random-string']; + }); + + $worker->run(); + + $this->assertNotEquals( + $client->responses[0]->original, + $client->responses[1]->original + ); + } } diff --git a/tests/ValidationFactoryStateTest.php b/tests/ValidationFactoryStateTest.php index 1af62b956..c60110181 100644 --- a/tests/ValidationFactoryStateTest.php +++ b/tests/ValidationFactoryStateTest.php @@ -7,7 +7,7 @@ class ValidationFactoryStateTest extends TestCase { - public function test_validation_factory_has_fresh_application_instance() + public function test_validation_factory_has_same_application_instance() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -22,6 +22,6 @@ public function test_validation_factory_has_fresh_application_instance() $worker->run(); - $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + $this->assertEquals($client->responses[0]->original, $client->responses[1]->original); } } diff --git a/tests/ViewEngineTest.php b/tests/ViewEngineTest.php new file mode 100644 index 000000000..ef34fc27e --- /dev/null +++ b/tests/ViewEngineTest.php @@ -0,0 +1,41 @@ +createOctaneContext([ + Request::create('/first', 'GET'), + Request::create('/first', 'GET'), + ]); + + $app['router']->get('/first', function (Application $app) { + return spl_object_hash($app['view.engine.resolver']->resolve('blade')); + }); + + $worker->run(); + + $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + } + + public function test_forget_php_view_engine() + { + [$app, $worker, $client] = $this->createOctaneContext([ + Request::create('/first', 'GET'), + Request::create('/first', 'GET'), + ]); + + $app['router']->get('/first', function (Application $app) { + return spl_object_hash($app['view.engine.resolver']->resolve('blade')); + }); + + $worker->run(); + + $this->assertNotEquals($client->responses[0]->original, $client->responses[1]->original); + } +} diff --git a/tests/ViewFactoryStateTest.php b/tests/ViewFactoryStateTest.php index 19ae62b7d..991eec811 100644 --- a/tests/ViewFactoryStateTest.php +++ b/tests/ViewFactoryStateTest.php @@ -7,7 +7,7 @@ class ViewFactoryStateTest extends TestCase { - public function test_view_factory_application_is_updated_on_subsequent_requests() + public function test_view_factory_application_is_not_updated_on_subsequent_requests() { [$app, $worker, $client] = $this->createOctaneContext([ Request::create('/first', 'GET'), @@ -20,7 +20,7 @@ public function test_view_factory_application_is_updated_on_subsequent_requests( $worker->run(); - $this->assertNotEquals( + $this->assertEquals( $client->responses[0]->getContent(), $client->responses[1]->getContent() );