diff options
Diffstat (limited to 'crates/shirabe')
| -rw-r--r-- | crates/shirabe/src/console/application.rs | 4 | ||||
| -rw-r--r-- | crates/shirabe/src/platform/runtime.rs | 110 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/platform_repository.rs | 27 | ||||
| -rw-r--r-- | crates/shirabe/tests/repository/platform_repository_test.rs | 9 |
4 files changed, 121 insertions, 29 deletions
diff --git a/crates/shirabe/src/console/application.rs b/crates/shirabe/src/console/application.rs index d6a287c2..4136a830 100644 --- a/crates/shirabe/src/console/application.rs +++ b/crates/shirabe/src/console/application.rs @@ -2466,10 +2466,6 @@ impl ApplicationHandle { // recognizes user classes, so the arm stays // unreachable until the checks and the instantiation // go through the worker. - let _ = shirabe_php_shim::instantiate_class( - &dummy_str, - vec![PhpMixed::String(script.clone())], - ); todo!( "plugin: import a user Command class as a live application command" ); diff --git a/crates/shirabe/src/platform/runtime.rs b/crates/shirabe/src/platform/runtime.rs index 28cfa095..265f007c 100644 --- a/crates/shirabe/src/platform/runtime.rs +++ b/crates/shirabe/src/platform/runtime.rs @@ -2,9 +2,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; +use shirabe_php_rpc::{PhpThrow, PluginValue}; use shirabe_php_shim::{ - PhpMixed, class_exists, function_exists, html_entity_decode, implode, instantiate_class, ltrim, - php_regex, strip_tags, trim, + PhpMixed, RuntimeException, function_exists, html_entity_decode, implode, ltrim, php_regex, + strip_tags, trim, }; /// Seam over the PHP runtime so PlatformRepository can be tested against mocked @@ -57,19 +58,35 @@ impl RuntimeInterface for Runtime { } PhpMixed::Array(version) } - _ => todo!(), + (PhpMixed::List(spec), _) => match class_callable(spec) { + ("ResourceBundle", "create") => resource_bundle_create(arguments), + ("IntlChar", "getUnicodeVersion") => { + php_value(shirabe_php_rpc::call_static_method( + "IntlChar", + "getUnicodeVersion", + Vec::new(), + None, + )) + } + (class, method) => panic!( + "the PHP callable `{class}::{method}` is not wired through the runtime seam" + ), + }, + _ => panic!("the PHP callable {callable:?} is not wired through the runtime seam"), } } fn has_class(&self, class: &str) -> bool { - class_exists(class) + shirabe_php_rpc::class_exists(class) } fn construct(&self, class: &str, arguments: Vec<PhpMixed>) -> anyhow::Result<PhpMixed> { - if arguments.is_empty() { - Ok(instantiate_class(class, vec![])) - } else { - Ok(instantiate_class(class, arguments)) + match class { + "Imagick" => imagick_version(arguments), + other => Err(anyhow::anyhow!(RuntimeException { + message: format!("the PHP class `{other}` is not wired through the runtime seam"), + code: 0, + })), } } @@ -86,6 +103,83 @@ impl RuntimeInterface for Runtime { } } +/// The `[class, method]` pair of a PHP callable given in array form. +fn class_callable(spec: &[PhpMixed]) -> (&str, &str) { + match spec { + [PhpMixed::String(class), PhpMixed::String(method)] => (class, method), + other => panic!("a PHP callable given as an array must be [class, method], got {other:?}"), + } +} + +/// Unwraps an RPC outcome whose failure means the runtime probe itself is broken, not that the +/// probed extension is absent. +fn php_value(outcome: anyhow::Result<Result<PluginValue, PhpThrow>>) -> PhpMixed { + match outcome { + Ok(Ok(value)) => value + .to_php_mixed() + .expect("a runtime probe answers with plain values"), + Ok(Err(throw)) => panic!("the PHP runtime probe failed: {}", throw.message), + Err(e) => panic!("the PHP runtime probe could not be sent: {e:#}"), + } +} + +/// PHP `ResourceBundle::create(...)`, whose result the caller reads `->get('Version')` off. +/// A live PHP object has no `PhpMixed` counterpart, so that entry crosses in its place. +fn resource_bundle_create(arguments: Vec<PhpMixed>) -> PhpMixed { + let bundle = match php_handle(shirabe_php_rpc::call_static_method( + "ResourceBundle", + "create", + arguments.iter().map(PluginValue::from_php_mixed).collect(), + None, + )) { + Some(phandle) => phandle, + // PHP returns null when the bundle cannot be opened. + None => return PhpMixed::Null, + }; + let version = php_value(shirabe_php_rpc::call_php_method( + bundle, + "get", + vec![PluginValue::string("Version")], + None, + )); + let _ = shirabe_php_rpc::release_php_handle(bundle); + PhpMixed::Object(IndexMap::from([("Version".to_string(), version)])) +} + +/// PHP `(new Imagick())->getVersion()`, reported as the entries the caller reads. +fn imagick_version(arguments: Vec<PhpMixed>) -> anyhow::Result<PhpMixed> { + let imagick = php_handle(shirabe_php_rpc::new_object( + "Imagick", + arguments.iter().map(PluginValue::from_php_mixed).collect(), + None, + )) + .ok_or_else(|| { + anyhow::anyhow!(RuntimeException { + message: "`new Imagick` did not answer with an object".to_string(), + code: 0, + }) + })?; + let version = php_value(shirabe_php_rpc::call_php_method( + imagick, + "getVersion", + Vec::new(), + None, + )); + let _ = shirabe_php_rpc::release_php_handle(imagick); + Ok(version) +} + +/// The handle of a PHP-side object an RPC answered with, or `None` when it answered with null. +fn php_handle(outcome: anyhow::Result<Result<PluginValue, PhpThrow>>) -> Option<u64> { + match outcome { + Ok(Ok(PluginValue::PhpHandle(handle))) => Some(handle.phandle), + Ok(Ok(PluginValue::Null)) => None, + Ok(Ok(other)) => panic!("the PHP runtime probe answered with {other:?}, not an object"), + Ok(Err(throw)) => panic!("the PHP runtime probe failed: {}", throw.message), + Err(e) => panic!("the PHP runtime probe could not be sent: {e:#}"), + } +} + impl Runtime { pub fn has_function(&self, f: &str) -> bool { function_exists(f) diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs index 3f22954c..5b205ba0 100644 --- a/crates/shirabe/src/repository/platform_repository.rs +++ b/crates/shirabe/src/repository/platform_repository.rs @@ -1378,8 +1378,8 @@ impl PlatformRepository { "libxslt", libxslt_str.as_deref(), None, - &[], &["xsl".to_string()], + &[], )?; let info = self.runtime.get_extension_info("xsl")?; @@ -1443,8 +1443,8 @@ impl PlatformRepository { &format!("{}-libzip", name), libzip_str.as_deref(), None, - &[], &["zip".to_string()], + &[], )?; } } @@ -1839,14 +1839,25 @@ impl PlatformRepository { package.as_complete().is_some() } - fn resource_bundle_get(_value: &PhpMixed, _key: &str) -> PhpMixed { - // TODO(plugin): proper ResourceBundle::get($key) dispatch on a PHP object. - PhpMixed::Null + /// PHP `$resourceBundle->get($key)`. A live PHP object has no `PhpMixed` counterpart, so + /// [`RuntimeInterface`] answers with the entries the caller reads instead of the object. + fn resource_bundle_get(value: &PhpMixed, key: &str) -> PhpMixed { + Self::php_object_field(value, key).unwrap_or(PhpMixed::Null) } - fn imagick_get_version_string(_value: &PhpMixed) -> String { - // TODO(plugin): proper Imagick->getVersion()['versionString'] dispatch. - "".to_string() + /// PHP `$imagick->getVersion()['versionString']`, read the same way. + fn imagick_get_version_string(value: &PhpMixed) -> String { + match Self::php_object_field(value, "versionString") { + Some(PhpMixed::String(version)) => version, + _ => String::new(), + } + } + + fn php_object_field(value: &PhpMixed, key: &str) -> Option<PhpMixed> { + match value { + PhpMixed::Object(fields) | PhpMixed::Array(fields) => fields.get(key).cloned(), + _ => None, + } } fn php_array_to_string_vec(value: &PhpMixed) -> Vec<String> { diff --git a/crates/shirabe/tests/repository/platform_repository_test.rs b/crates/shirabe/tests/repository/platform_repository_test.rs index 507938a0..3a4bf45d 100644 --- a/crates/shirabe/tests/repository/platform_repository_test.rs +++ b/crates/shirabe/tests/repository/platform_repository_test.rs @@ -1612,16 +1612,7 @@ fn assert_package_links( } } -// TODO(phase-d): blocked by the TODO(plugin) stubs below; re-check once the plugin RPC -// mechanism can dispatch method calls on PHP objects. #[test] -#[ignore = "all 59 provideLibraryTestCases datasets ported faithfully; blocked by two \ - TODO(plugin) stubs in PlatformRepository that need dynamic method dispatch on \ - PHP objects ($resourceBundle->get('Version'), $imagick->getVersion()): \ - resource_bundle_get returns Null so the intl dataset drops lib-icu-cldr, and \ - imagick_get_version_string returns \"\" so the imagick datasets drop \ - lib-imagick-imagemagick (confirmed current failure mode: package-set mismatch \ - on the intl dataset, not a panic)"] fn test_library_information() { let extension_version = "100.200.300"; |
