diff --git a/src/Illuminate/Foundation/Console/ConfigCacheCommand.php b/src/Illuminate/Foundation/Console/ConfigCacheCommand.php index 6cef3389ea64..cedf0309dabc 100644 --- a/src/Illuminate/Foundation/Console/ConfigCacheCommand.php +++ b/src/Illuminate/Foundation/Console/ConfigCacheCommand.php @@ -5,6 +5,7 @@ use Illuminate\Console\Command; use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract; use Illuminate\Filesystem\Filesystem; +use Illuminate\Support\Arr; use LogicException; use Symfony\Component\Console\Attribute\AsCommand; use Throwable; @@ -69,6 +70,14 @@ public function handle() } catch (Throwable $e) { $this->files->delete($configPath); + foreach (Arr::dot($config) as $key => $value) { + try { + eval(var_export($value, true).';'); + } catch (Throwable $e) { + throw new LogicException("Your configuration files could not be serialized because the value at \"{$key}\" is non-serializable.", 0, $e); + } + } + throw new LogicException('Your configuration files are not serializable.', 0, $e); } diff --git a/tests/Integration/Foundation/Console/ConfigCacheCommandTest.php b/tests/Integration/Foundation/Console/ConfigCacheCommandTest.php new file mode 100644 index 000000000000..37eaa268adc8 --- /dev/null +++ b/tests/Integration/Foundation/Console/ConfigCacheCommandTest.php @@ -0,0 +1,127 @@ +afterApplicationCreated(function () use ($files) { + $files->ensureDirectoryExists($this->app->configPath()); + }); + + $this->beforeApplicationDestroyed(function () use ($files) { + $files->delete($this->app->configPath('testconfig.php')); + }); + + parent::setUp(); + } + + public function testConfigurationCanBeCachedSuccessfully() + { + $files = new Filesystem; + $files->put($this->app->configPath('testconfig.php'), <<<'PHP' + 'value', + 'number' => 123, + 'boolean' => true, + 'array' => ['foo', 'bar'], + 'from_env' => env('SOMETHING_FROM_ENV', 10), + 'nested' => [ + 'key' => 'value', + ], + ]; + PHP + ); + + $this->artisan('config:cache') + ->assertSuccessful() + ->expectsOutputToContain('Configuration cached successfully'); + + $this->assertFileExists($this->app->getCachedConfigPath()); + } + + public function testConfigurationCacheFailsWithNonSerializableValue() + { + $files = new Filesystem; + $files->put($this->app->configPath('testconfig.php'), <<<'PHP' + function () { + return 'test'; + }, + ]; + PHP + ); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Your configuration files could not be serialized because the value at "testconfig.closure" is non-serializable.'); + + $this->artisan('config:cache'); + } + + public function testConfigurationCacheFailsWithNestedNonSerializableValue() + { + $files = new Filesystem; + $files->put($this->app->configPath('testconfig.php'), <<<'PHP' + [ + 'deep' => [ + 'closure' => function () { + return 'test'; + }, + ], + ], + ]; + PHP + ); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Your configuration files could not be serialized because the value at "testconfig.nested.deep.closure" is non-serializable.'); + + $this->artisan('config:cache'); + } + + public function testConfigurationCacheIsDeletedWhenSerializationFails() + { + $files = new Filesystem; + $files->put($this->app->configPath('testconfig.php'), <<<'PHP' + function () { + return 'test'; + }, + ]; + PHP + ); + + try { + $this->artisan('config:cache'); + $this->fail('should have thrown an exception'); + } catch (LogicException) { + // Expected exception + } + + $this->assertFileDoesNotExist($this->app->getCachedConfigPath()); + } +}