aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-10 00:57:19 +0900
committernsfisis <nsfisis@gmail.com>2026-08-10 02:35:55 +0900
commitbbf33b836026cbe9fb9e7c76291dcb133b7144e2 (patch)
tree8b0437c321467bb8151cacb17c2414874f342443 /crates/shirabe/src
parent177edc22cac6475db85cba9e0d19e6440aef64bb (diff)
downloadphp-shirabe-bbf33b836026cbe9fb9e7c76291dcb133b7144e2.tar.gz
php-shirabe-bbf33b836026cbe9fb9e7c76291dcb133b7144e2.tar.zst
php-shirabe-bbf33b836026cbe9fb9e7c76291dcb133b7144e2.zip
feat(xdebug): switch Xdebug off in the PHP worker
Composer restarts itself with Xdebug unloaded because Xdebug makes PHP several times slower. Xdebug is never loaded into this process, so what needs dealing with is the PHP worker: it is spawned with `-d xdebug.mode=off` and `XDEBUG_MODE=off` (Xdebug reads the environment variable first and lets it override every ini setting), which makes its module init return before it installs any executor, compile, error or opcode hook. Rewriting the ini files and re-executing, the way xdebug-handler does, would additionally cover Xdebug 2, which hooks unconditionally and has no equivalent setting. That is not worth its machinery here: Xdebug 2 caps out at PHP 7.4, while every PHP version Composer supports can run Xdebug 3. What remains of XdebugHandler is small enough to live beside the worker it governs, so its crate is gone and its callers inline it. isXdebugActive answers false without asking PHP whenever the worker is switched off, so a command that needs no PHP does not spawn one just for the Xdebug warning; diagnose reports what the worker measures instead, which still surfaces an Xdebug that ignores the setting. PlatformRepository has no unloaded extension to restore, since switching the mode off leaves it loaded. COMPOSER_ORIGINAL_INIS is neither written nor read: it exists so a restarted process can name the ini files it replaced, and IniHelper can report the worker's own. IniHelperTest injects through that variable, so none of its cases are ported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs3
-rw-r--r--crates/shirabe/src/console/application.rs3
-rw-r--r--crates/shirabe/src/main.rs10
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs20
-rw-r--r--crates/shirabe/src/util/ini_helper.rs8
5 files changed, 15 insertions, 29 deletions
diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs
index 2af2acfc..5094f06a 100644
--- a/crates/shirabe/src/command/diagnose_command.rs
+++ b/crates/shirabe/src/command/diagnose_command.rs
@@ -887,9 +887,6 @@ impl DiagnoseCommand {
if filter_var_boolean(diagnostics.ini_get("xdebug.profiler_enabled").unwrap_or("")) {
warnings.insert("xdebug_profile".to_string(), PhpMixed::Bool(true));
} else if diagnostics.xdebug_active {
- // PHP: XdebugHandler::isXdebugActive(). As with IniHelper::get_all, the port of that
- // method in shirabe_xdebug_handler is a stub, so the real runtime is queried through
- // the diagnose payload instead.
warnings.insert("xdebug_loaded".to_string(), PhpMixed::Bool(true));
}
diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs
index cc456c3c..8fed08e9 100644
--- a/crates/shirabe/src/console/application.rs
+++ b/crates/shirabe/src/console/application.rs
@@ -98,7 +98,6 @@ use shirabe_symfony_console::style::style_interface::StyleInterface;
use shirabe_symfony_console::style::symfony_style::SymfonyStyle;
use shirabe_symfony_console::terminal::Terminal;
use shirabe_symfony_process::exception::ProcessTimedOutException;
-use shirabe_xdebug_handler::XdebugHandler;
/// The PHP `Composer\Console\Application` and `Symfony\Component\Console\Application` are
/// flattened into a single struct. Methods that are overridden by subclass and called via
@@ -2275,7 +2274,7 @@ impl ApplicationHandle {
io.write_error(&format!("<warning>Composer supports PHP 7.2.5 and above, you will most likely encounter problems with your PHP {}. Upgrading is strongly recommended but you can use Composer 2.2.x LTS as a fallback.</warning>", PHP_VERSION));
}
- if XdebugHandler::is_xdebug_active()
+ if shirabe_php_rpc::xdebug::is_xdebug_active()
&& Platform::get_env("COMPOSER_DISABLE_XDEBUG_WARN").is_none()
{
io.write_error("<warning>Composer is operating slower than normal because you have Xdebug enabled. See https://getcomposer.org/xdebug</warning>");
diff --git a/crates/shirabe/src/main.rs b/crates/shirabe/src/main.rs
index dcf33803..f8b64c46 100644
--- a/crates/shirabe/src/main.rs
+++ b/crates/shirabe/src/main.rs
@@ -29,18 +29,14 @@ fn init_tracing() {
}
fn main() {
- // TODO(phase-c): PHP: `$xdebug = new XdebugHandler('Composer'); $xdebug->check(); unset($xdebug);`
- // (restart the process without Xdebug loaded, for performance) is not ported. Since no
- // XdebugHandler is ever constructed, `self::$name` never gets set, so the
- // COMPOSER_ORIGINAL_INIS-env-var branch of XdebugHandler::getAllIniFiles() (see
- // shirabe-xdebug-handler's xdebug_handler.rs and shirabe/src/util/ini_helper.rs) is
- // unreachable in this port, not merely unexercised by current tests.
-
// Take the $_ENV / $_SERVER snapshots before any putenv() mutates the real environment.
// See `docs/dev/env-vars-porting.md` for details.
std::sync::LazyLock::force(&PHP_ENV);
std::sync::LazyLock::force(&PHP_SERVER);
+ // Composer calls XdebugHandler::check() here to restart without Xdebug, which is intentionally
+ // unported. See `docs/dev/xdebug.md`.
+
// The single process-wide tokio Runtime. `shirabe::run` and everything under it
// (Command::execute and friends) is still synchronous top to bottom; entering the runtime
// here (rather than driving `run` via `.block_on`) just makes it ambiently available via
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 7e123151..3e1f35ec 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -20,11 +20,10 @@ use shirabe_pcre::{CaptureKey, Preg};
use shirabe_php_rpc::PlatformInfo;
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_map_str_fn,
- array_slice_strs, explode, get_class, implode, in_array_strict, is_string, php_regex,
- str_replace, str_starts_with, strpos, strtolower, var_export,
+ array_slice_strs, explode, get_class, implode, is_string, php_regex, str_replace,
+ str_starts_with, strpos, strtolower, var_export,
};
use shirabe_semver::constraint::SimpleConstraint;
-use shirabe_xdebug_handler::XdebugHandler;
use std::sync::{LazyLock, Mutex};
static LAST_SEEN_PLATFORM_PHP: LazyLock<Mutex<Option<String>>> = LazyLock::new(|| Mutex::new(None));
@@ -303,17 +302,10 @@ impl PlatformRepository {
}
// Check for Xdebug in a restarted process
- if !in_array_strict(
- "xdebug".to_string(),
- &loaded_extensions
- .iter()
- .map(|s| PhpMixed::String(s.clone()))
- .collect::<Vec<_>>(),
- ) && let Some(xdebug_pretty_version) = XdebugHandler::get_skipped_version()
- && !xdebug_pretty_version.is_empty()
- {
- self.add_extension("xdebug", &xdebug_pretty_version)?;
- }
+ // PHP re-adds the extension `XdebugHandler::getSkippedVersion()` names, since its restart
+ // runs on an ini file with Xdebug commented out. The worker is instead started with
+ // `xdebug.mode=off` (see docs/dev/xdebug.md), which leaves the extension loaded, so it is
+ // already among $loadedExtensions.
// Another quick loop, just for possible libraries
// Doing it this way to know that functions or constants exist before
diff --git a/crates/shirabe/src/util/ini_helper.rs b/crates/shirabe/src/util/ini_helper.rs
index 4ff9a5e2..08f5c816 100644
--- a/crates/shirabe/src/util/ini_helper.rs
+++ b/crates/shirabe/src/util/ini_helper.rs
@@ -4,10 +4,12 @@ pub struct IniHelper;
impl IniHelper {
/// Returns an array of php.ini locations with at least one entry.
+ ///
+ /// PHP asks `XdebugHandler::getAllIniFiles()`, which answers from `COMPOSER_ORIGINAL_INIS`
+ /// when it is set, because a restarted process runs on a generated ini file and has to name
+ /// the ones it replaced. Nothing here restarts PHP, so the worker always runs on the machine's
+ /// own ini files and the variable is not consulted.
pub fn get_all() -> Vec<String> {
- // PHP: XdebugHandler::getAllIniFiles(). shirabe_xdebug_handler::XdebugHandler's port
- // of that method is a stub that returns the PHP default, so query the real PHP
- // runtime's loaded/scanned ini files here instead.
shirabe_php_rpc::get_all_ini_files()
}