Skip to content

Commit d9bb14d

Browse files
Copilotswissspidy
andcommitted
Implement process restart using pcntl_exec to fully reload code
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
1 parent 84c524f commit d9bb14d

1 file changed

Lines changed: 78 additions & 1 deletion

File tree

src/Shell_Command.php

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ class Shell_Command extends WP_CLI_Command {
1313
* is loaded, you have access to all the functions, classes and globals
1414
* that you can use within a WordPress plugin, for example.
1515
*
16+
* The `restart` command reloads the shell by spawning a new PHP process,
17+
* allowing modified code to be fully reloaded. Note that this requires
18+
* the `pcntl_exec()` function. If not available, the shell restarts
19+
* in-process, which resets variables but doesn't reload PHP files.
20+
*
1621
* ## OPTIONS
1722
*
1823
* [--basic]
@@ -33,7 +38,7 @@ class Shell_Command extends WP_CLI_Command {
3338
* # Restart the shell to reload code changes.
3439
* $ wp shell
3540
* wp> restart
36-
* Restarting shell...
41+
* Restarting shell in new process...
3742
* wp>
3843
*
3944
* # Watch a directory for changes and auto-restart.
@@ -88,6 +93,12 @@ public function __invoke( $_, $assoc_args ) {
8893
$repl->set_watch_path( $watch_path );
8994
}
9095
$exit_code = $repl->start();
96+
97+
// If restart requested, exec a new PHP process to reload all code
98+
if ( WP_CLI\Shell\REPL::EXIT_CODE_RESTART === $exit_code ) {
99+
$this->restart_process( $assoc_args );
100+
// If restart_process() returns, pcntl_exec is not available, continue in-process
101+
}
91102
} while ( WP_CLI\Shell\REPL::EXIT_CODE_RESTART === $exit_code );
92103
}
93104
}
@@ -110,4 +121,70 @@ private function resolve_watch_path( $path ) {
110121

111122
return $realpath;
112123
}
124+
125+
/**
126+
* Restart the shell by spawning a new PHP process.
127+
*
128+
* This replaces the current process with a new one to fully reload all code.
129+
* Falls back to in-process restart if pcntl_exec is not available.
130+
*
131+
* @param array $assoc_args Command arguments to preserve.
132+
*/
133+
private function restart_process( $assoc_args ) {
134+
// Check if pcntl_exec is available
135+
if ( ! function_exists( 'pcntl_exec' ) ) {
136+
WP_CLI::debug( 'pcntl_exec not available, falling back to in-process restart', 'shell' );
137+
return;
138+
}
139+
140+
// Build the command to restart wp shell with the same arguments
141+
$php_binary = Utils\get_php_binary();
142+
143+
// Get the WP-CLI script path
144+
$wp_cli_script = null;
145+
foreach ( array( 'argv', '_SERVER' ) as $source ) {
146+
if ( 'argv' === $source && isset( $GLOBALS['argv'][0] ) ) {
147+
$wp_cli_script = $GLOBALS['argv'][0];
148+
break;
149+
} elseif ( '_SERVER' === $source && isset( $_SERVER['argv'][0] ) ) {
150+
$wp_cli_script = $_SERVER['argv'][0];
151+
break;
152+
}
153+
}
154+
155+
if ( ! $wp_cli_script ) {
156+
WP_CLI::debug( 'Could not determine WP-CLI script path, falling back to in-process restart', 'shell' );
157+
return;
158+
}
159+
160+
// Build arguments array
161+
$args = array( $php_binary, $wp_cli_script, 'shell' );
162+
163+
if ( Utils\get_flag_value( $assoc_args, 'basic' ) ) {
164+
$args[] = '--basic';
165+
}
166+
167+
$watch_path = Utils\get_flag_value( $assoc_args, 'watch', false );
168+
if ( $watch_path ) {
169+
$args[] = '--watch=' . $watch_path;
170+
}
171+
172+
// Add the path argument if present in $_SERVER
173+
if ( isset( $_SERVER['argv'] ) ) {
174+
foreach ( $_SERVER['argv'] as $arg ) {
175+
if ( '--path=' === substr( $arg, 0, 7 ) ) {
176+
$args[] = $arg;
177+
}
178+
}
179+
}
180+
181+
WP_CLI::log( 'Restarting shell in new process...' );
182+
183+
// Replace the current process with a new one
184+
// Note: pcntl_exec does not return on success
185+
pcntl_exec( $php_binary, array_slice( $args, 1 ) );
186+
187+
// If we reach here, exec failed
188+
WP_CLI::warning( 'Failed to restart process, falling back to in-process restart' );
189+
}
113190
}

0 commit comments

Comments
 (0)