diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php
index d51fce79f45e..b392eef8d2e2 100644
--- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php
+++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php
@@ -35,10 +35,18 @@ class KeyGenerateCommand extends Command
*/
public function handle()
{
+ if ($this->shouldSaveExistingKey()) {
+ $this->call('key:rotate');
+
+ return;
+ }
+
$key = $this->generateRandomKey();
if ($this->option('show')) {
- return $this->line(''.$key.'');
+ $this->line(''.$key.'');
+
+ return;
}
// Next, we will replace the application key in the environment file so it is
@@ -53,6 +61,23 @@ public function handle()
$this->components->info('Application key set successfully.');
}
+ /**
+ * Ask the user if they want to save the existing key.
+ */
+ protected function shouldSaveExistingKey(): bool
+ {
+ $currentKey = $this->laravel['config']['app.key'];
+
+ if (! empty($currentKey) && $this->confirm(
+ 'There is already an app key. Do you want to store the old key before generating a new one?',
+ true
+ )) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Generate a random key for the application.
*
diff --git a/src/Illuminate/Foundation/Console/RotateKeyCommand.php b/src/Illuminate/Foundation/Console/RotateKeyCommand.php
new file mode 100644
index 000000000000..0e36df468aa8
--- /dev/null
+++ b/src/Illuminate/Foundation/Console/RotateKeyCommand.php
@@ -0,0 +1,93 @@
+laravel['config']['app.key'];
+
+ // 2. Get current APP_PREVIOUS_KEYS as array and prepend the current key
+ $previousKeys = Arr::prepend($this->laravel['config']['app.previous_keys'] ?? [], $currentKey);
+
+ // 3. Update .env file with the new APP_PREVIOUS_KEYS and clear APP_KEY
+ if (! $this->updateEnvFile($currentKey, $previousKeys)) {
+ $this->components->error('Failed to update the environment file.');
+
+ return;
+ }
+
+ // 4. Notify the user and generate a new key
+ $this->components->info('Current application key has been saved successfully. Running php artisan key:generate --force to generate a new key.');
+ $this->call('key:generate', ['--force' => true]);
+ $this->components->info('Application key has been rotated successfully.');
+ }
+
+ /**
+ * Update the .env file with the new APP_PREVIOUS_KEYS and clear APP_KEY.
+ */
+ protected function updateEnvFile(string $currentKey, array $previousKeys): bool
+ {
+ $envPath = $this->laravel->environmentFilePath();
+ $contents = file_get_contents($envPath);
+
+ // Convert array to comma-separated string and wrap in quotes
+ $quotedPreviousKeys = '"'.implode(',', $previousKeys).'"';
+
+ // Update APP_PREVIOUS_KEYS
+ if (str_contains($contents, 'APP_PREVIOUS_KEYS=')) {
+ // If APP_PREVIOUS_KEYS already exists, update its value
+ $contents = preg_replace($this->previousKeysPattern(), 'APP_PREVIOUS_KEYS='.$quotedPreviousKeys, $contents);
+ } else {
+ // If APP_PREVIOUS_KEYS does not exist, insert it after APP_KEY
+ $contents = preg_replace($this->keyPattern(), "APP_KEY=\nAPP_PREVIOUS_KEYS=".$quotedPreviousKeys, $contents);
+ }
+
+ // Clear APP_KEY
+ $contents = preg_replace($this->keyPattern(), 'APP_KEY=', $contents);
+ $this->laravel['config']['app.key'] = null;
+
+ return file_put_contents($envPath, $contents) !== false;
+ }
+
+ /**
+ * Generate a regex pattern to match the current APP_KEY line.
+ */
+ protected function keyPattern(): string
+ {
+ return '/^APP_KEY='.preg_quote($this->laravel['config']['app.key'], '/').'/m';
+ }
+
+ /**
+ * Generate a regex pattern to match the current APP_PREVIOUS_KEYS line.
+ */
+ protected function previousKeysPattern(): string
+ {
+ return '/^APP_PREVIOUS_KEYS="'.preg_quote(implode(',', (array) $this->laravel['config']['app.previous_keys']), '/').'"/m';
+ }
+}