From ed0da7fcc3056ce5a822ac0de63ccc2dc799006e Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 26 Jul 2026 00:18:54 +0900 Subject: feat(diagnose-command): query the real PHP runtime in one RPC call diagnose used to read hardcoded shim stubs, so it described a fictional runtime: OPENSSL_VERSION_NUMBER was always 0 and tripped the TLSv1.1/1.2 check, PHP_BINARY and OPENSSL_VERSION_TEXT were empty, and the extension, function and ini probes answered from a fixed table. The PHP worker gained a `diagnose` entry that returns every fact the command needs as one PHP array, cached in a OnceLock so the several call sites share a single round trip. Reading it back needed array support in the serialize() parser, which in turn lets get_loaded_extensions and get_all_ini_files return real lists instead of comma-joined strings. Also fixes the openssl_version message, which dropped strstr()'s before_needle argument during the port, and check_connectivity's allow_url_fopen test, which did not follow PHP string truthiness. Co-Authored-By: Claude Opus 5 (1M context) --- crates/shirabe-php-rpc/php/worker.php | 95 +++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 5 deletions(-) (limited to 'crates/shirabe-php-rpc/php') diff --git a/crates/shirabe-php-rpc/php/worker.php b/crates/shirabe-php-rpc/php/worker.php index 060373d5..1e5b726d 100644 --- a/crates/shirabe-php-rpc/php/worker.php +++ b/crates/shirabe-php-rpc/php/worker.php @@ -6,20 +6,53 @@ $client = @stream_socket_client('unix://' . $argv[1], $errno, $errstr); if ($client === false) { exit(1); } +// Port of Composer\XdebugHandler\XdebugHandler::setXdebugDetails(), which the diagnose payload +// reports as `xdebug_active`. +$xdebug_active = static function (): bool { + if (!extension_loaded('xdebug')) { + return false; + } + + $version = phpversion('xdebug'); + $version = $version !== false ? $version : 'unknown'; + + if (version_compare($version, '3.1', '>=')) { + $modes = xdebug_info('mode'); + return (count($modes) === 0 ? 'off' : implode(',', $modes)) !== 'off'; + } + + $ini_mode = ini_get('xdebug.mode'); + if ($ini_mode === false) { + return true; + } + + $env_mode = (string) getenv('XDEBUG_MODE'); + if ($env_mode !== '') { + $mode = $env_mode; + } else { + $mode = $ini_mode !== '' ? $ini_mode : 'off'; + } + + if (preg_match('/^,+$/', str_replace(' ', '', $mode)) === 1) { + $mode = 'off'; + } + + return $mode !== 'off'; +}; $dispatch = [ 'defined' => static fn($name) => defined($name), 'constant' => static fn($name) => defined($name) ? constant($name) : null, 'inet_pton' => static fn($arg) => @inet_pton($arg), 'curl_version' => static fn($arg) => function_exists('curl_version') ? (curl_version()['version'] ?? null) : null, 'phpversion' => static fn($name) => phpversion($name), - 'get_loaded_extensions' => static fn($arg) => implode(',', get_loaded_extensions()), + 'get_loaded_extensions' => static fn($arg) => get_loaded_extensions(), 'get_all_ini_files' => static function ($arg) { $paths = [(string) php_ini_loaded_file()]; $scanned = php_ini_scanned_files(); if ($scanned !== false) { $paths = array_merge($paths, array_map('trim', explode(',', $scanned))); } - return implode(',', $paths); + return $paths; }, 'extension_info' => static function ($name) { if (!extension_loaded($name)) { @@ -30,10 +63,62 @@ $dispatch = [ $re->info(); return (string) ob_get_clean(); }, - 'phpinfo' => static function ($what) { + 'diagnose' => static function ($arg) use ($xdebug_active) { + $extensions = []; + foreach ([ + 'apcu', + 'curl', + 'filter', + 'hash', + 'iconv', + 'ionCube Loader', + 'mbstring', + 'openssl', + 'Phar', + 'uopz', + 'zip', + 'zlib', + ] as $extension) { + $extensions[$extension] = extension_loaded($extension); + } + + $functions = []; + foreach (['disk_free_space', 'json_decode', 'proc_open'] as $function) { + $functions[$function] = function_exists($function); + } + + $ini = []; + foreach ([ + 'allow_url_fopen', + 'apc.enable_cli', + 'uopz.disable', + 'uopz.exit', + 'xdebug.profiler_enabled', + ] as $setting) { + $value = ini_get($setting); + $ini[$setting] = $value === false ? null : $value; + } + ob_start(); - phpinfo((int) $what); - return (string) ob_get_clean(); + phpinfo(INFO_GENERAL); + $phpinfo = (string) ob_get_clean(); + + return [ + 'php_version' => PHP_VERSION, + 'php_version_id' => PHP_VERSION_ID, + 'php_binary' => defined('PHP_BINARY') ? PHP_BINARY : null, + 'openssl_version_text' => defined('OPENSSL_VERSION_TEXT') ? OPENSSL_VERSION_TEXT : null, + 'openssl_version_number' => defined('OPENSSL_VERSION_NUMBER') ? OPENSSL_VERSION_NUMBER : 0, + 'has_hhvm_version' => defined('HHVM_VERSION'), + 'has_php_windows_version_build' => defined('PHP_WINDOWS_VERSION_BUILD'), + 'xdebug_active' => $xdebug_active(), + 'ioncube_loader_iversion' => extension_loaded('ionCube Loader') ? ioncube_loader_iversion() : 0, + 'ioncube_loader_version' => extension_loaded('ionCube Loader') ? ioncube_loader_version() : '', + 'phpinfo_general' => $phpinfo, + 'extensions' => $extensions, + 'functions' => $functions, + 'ini' => $ini, + ]; }, ]; $read_exact = static function ($conn, int $len): ?string { -- cgit v1.3.1