diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/shirabe-php-rpc/php/worker.php | 19 | ||||
| -rw-r--r-- | crates/shirabe-php-rpc/src/lib.rs | 50 | ||||
| -rw-r--r-- | crates/shirabe/src/command/diagnose_command.rs | 48 |
3 files changed, 114 insertions, 3 deletions
diff --git a/crates/shirabe-php-rpc/php/worker.php b/crates/shirabe-php-rpc/php/worker.php index 1e5b726d..6c3ab443 100644 --- a/crates/shirabe-php-rpc/php/worker.php +++ b/crates/shirabe-php-rpc/php/worker.php @@ -99,6 +99,24 @@ $dispatch = [ $ini[$setting] = $value === false ? null : $value; } + // curl_version() only exists while the extension is loaded; DiagnoseCommand reads these + // details only after its own extension_loaded('curl') check. + $curl = null; + if (extension_loaded('curl')) { + $version = curl_version(); + $curl = [ + 'version' => (string) ($version['version'] ?? ''), + 'libz_version' => $version['libz_version'] ?? null, + 'brotli_version' => $version['brotli_version'] ?? null, + 'ssl_version' => $version['ssl_version'] ?? null, + 'features' => $version['features'] ?? null, + 'version_zstd' => defined('CURL_VERSION_ZSTD') ? CURL_VERSION_ZSTD : null, + 'version_http2' => defined('CURL_VERSION_HTTP2') ? CURL_VERSION_HTTP2 : null, + 'has_http_version_2_0' => defined('CURL_HTTP_VERSION_2_0'), + 'version_http3' => defined('CURL_VERSION_HTTP3') ? CURL_VERSION_HTTP3 : null, + ]; + } + ob_start(); phpinfo(INFO_GENERAL); $phpinfo = (string) ob_get_clean(); @@ -115,6 +133,7 @@ $dispatch = [ 'ioncube_loader_iversion' => extension_loaded('ionCube Loader') ? ioncube_loader_iversion() : 0, 'ioncube_loader_version' => extension_loaded('ionCube Loader') ? ioncube_loader_version() : '', 'phpinfo_general' => $phpinfo, + 'curl' => $curl, 'extensions' => $extensions, 'functions' => $functions, 'ini' => $ini, diff --git a/crates/shirabe-php-rpc/src/lib.rs b/crates/shirabe-php-rpc/src/lib.rs index ab39c251..7ec1f925 100644 --- a/crates/shirabe-php-rpc/src/lib.rs +++ b/crates/shirabe-php-rpc/src/lib.rs @@ -60,6 +60,21 @@ pub fn get_extension_info(name: &str) -> String { } } +/// `curl_version()`, together with the `CURL_*` constants the `diagnose` command consults. Every +/// `Option` field is `None` when the corresponding array key or constant is absent. +#[derive(Debug)] +pub struct Curl { + pub version: String, + pub libz_version: Option<String>, + pub brotli_version: Option<String>, + pub ssl_version: Option<String>, + pub features: Option<i64>, + pub version_zstd: Option<i64>, + pub version_http2: Option<i64>, + pub has_http_version_2_0: bool, + pub version_http3: Option<i64>, +} + /// Everything the `diagnose` command needs to know about the PHP runtime, fetched in a single /// round trip because the command would otherwise probe the same runtime dozens of times. #[derive(Debug)] @@ -82,6 +97,8 @@ pub struct Diagnostics { pub ioncube_loader_version: String, /// `phpinfo(INFO_GENERAL)` output, captured via `ob_start()`/`ob_get_clean()`. pub phpinfo_general: String, + /// `None` when the curl extension is not loaded. + pub curl: Option<Curl>, extensions: IndexMap<String, bool>, functions: IndexMap<String, bool>, ini_settings: IndexMap<String, Option<String>>, @@ -138,6 +155,7 @@ pub fn get_diagnostics() -> &'static Diagnostics { ioncube_loader_iversion: int_field(payload, "ioncube_loader_iversion"), ioncube_loader_version: string_field(payload, "ioncube_loader_version"), phpinfo_general: string_field(payload, "phpinfo_general"), + curl: curl_field(payload, "curl"), extensions: map_field(payload, "extensions") .iter() .map(|(name, value)| (name.clone(), as_bool(value, name))) @@ -199,6 +217,38 @@ fn as_bool(value: &PhpMixed, key: &str) -> bool { } } +fn nullable_int_field(payload: &IndexMap<String, PhpMixed>, key: &str) -> Option<i64> { + match field(payload, key) { + PhpMixed::Int(n) => Some(*n), + PhpMixed::Null => None, + other => { + panic!("PHP RPC: `diagnose` payload entry `{key}` is not an int or null: {other:?}") + } + } +} + +fn curl_field(payload: &IndexMap<String, PhpMixed>, key: &str) -> Option<Curl> { + let curl = match field(payload, key) { + PhpMixed::Null => return None, + PhpMixed::Array(map) => map, + other => { + panic!("PHP RPC: `diagnose` payload entry `{key}` is not an array or null: {other:?}") + } + }; + + Some(Curl { + version: string_field(curl, "version"), + libz_version: nullable_string_field(curl, "libz_version"), + brotli_version: nullable_string_field(curl, "brotli_version"), + ssl_version: nullable_string_field(curl, "ssl_version"), + features: nullable_int_field(curl, "features"), + version_zstd: nullable_int_field(curl, "version_zstd"), + version_http2: nullable_int_field(curl, "version_http2"), + has_http_version_2_0: bool_field(curl, "has_http_version_2_0"), + version_http3: nullable_int_field(curl, "version_http3"), + }) +} + fn as_nullable_string(value: &PhpMixed, key: &str) -> Option<String> { match value { PhpMixed::String(s) => Some(s.clone()), diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index 178b4e23..3f69722a 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -1052,9 +1052,51 @@ impl DiagnoseCommand { return "<error>disabled via disable_functions, using php streams fallback, which reduces performance</error>".to_string(); } - // TODO(phase-d): Shirabe does not use cURL, we will consider what should be shown here - // later. - return "TODO: curl_version()".to_string(); + let version = shirabe_php_rpc::get_diagnostics() + .curl + .as_ref() + .expect("the diagnose payload carries curl details while the extension is loaded"); + let libz_version = version + .libz_version + .as_deref() + .filter(|v| !v.is_empty()) + .unwrap_or("missing"); + let brotli_version = version + .brotli_version + .as_deref() + .filter(|v| !v.is_empty()) + .unwrap_or("missing"); + let ssl_version = version + .ssl_version + .as_deref() + .filter(|v| !v.is_empty()) + .unwrap_or("missing"); + let has_zstd = match (version.features, version.version_zstd) { + (Some(features), Some(zstd)) => features & zstd != 0, + _ => false, + }; + let mut http_versions = "1.0, 1.1".to_string(); + if let (Some(features), Some(http2)) = (version.features, version.version_http2) + && version.has_http_version_2_0 + && http2 & features != 0 + { + http_versions.push_str(", 2"); + } + if let (Some(features), Some(http3)) = (version.features, version.version_http3) + && features & http3 != 0 + { + http_versions.push_str(", 3"); + } + + return format!( + "<comment>{}</comment> libz <comment>{}</comment> brotli <comment>{}</comment> zstd <comment>{}</comment> ssl <comment>{}</comment> HTTP <comment>{}</comment>", + version.version, + libz_version, + brotli_version, + if has_zstd { "supported" } else { "missing" }, + ssl_version, + http_versions, + ); } "<error>missing, using php streams fallback, which reduces performance</error>".to_string() |
