aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/platform')
-rw-r--r--crates/shirabe/src/platform/runtime.rs110
1 files changed, 102 insertions, 8 deletions
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)