aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe')
-rw-r--r--crates/shirabe/src/console/application.rs4
-rw-r--r--crates/shirabe/src/platform/runtime.rs110
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs27
-rw-r--r--crates/shirabe/tests/repository/platform_repository_test.rs9
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";