aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe')
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs4
-rw-r--r--crates/shirabe/src/command/check_platform_reqs_command.rs2
-rw-r--r--crates/shirabe/src/command/completion_trait.rs6
-rw-r--r--crates/shirabe/src/command/create_project_command.rs2
-rw-r--r--crates/shirabe/src/command/diagnose_command.rs2
-rw-r--r--crates/shirabe/src/command/init_command.rs2
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs2
-rw-r--r--crates/shirabe/src/command/require_command.rs8
-rw-r--r--crates/shirabe/src/command/search_command.rs2
-rw-r--r--crates/shirabe/src/command/show_command.rs8
-rw-r--r--crates/shirabe/src/command/suggests_command.rs4
-rw-r--r--crates/shirabe/src/dependency_resolver/problem.rs2
-rw-r--r--crates/shirabe/src/installer.rs2
-rw-r--r--crates/shirabe/src/platform.rs6
-rw-r--r--crates/shirabe/src/platform/runtime.rs237
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs251
-rw-r--r--crates/shirabe/tests/package/version/version_selector_test.rs8
-rw-r--r--crates/shirabe/tests/platform/main.rs1
-rw-r--r--crates/shirabe/tests/platform/runtime_test.rs28
-rw-r--r--crates/shirabe/tests/repository/platform_repository_test.rs306
20 files changed, 262 insertions, 621 deletions
diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs
index c4399f34..46cc654f 100644
--- a/crates/shirabe/src/command/base_dependency_command.rs
+++ b/crates/shirabe/src/command/base_dependency_command.rs
@@ -78,7 +78,7 @@ pub trait BaseDependencyCommand: BaseCommand {
.map(|(k, v)| (k, PhpMixed::String(v)))
.collect();
repos.push(crate::repository::RepositoryInterfaceHandle::new(
- PlatformRepository::new(vec![], platform_overrides)?,
+ PlatformRepository::new(vec![], platform_overrides, None, None)?,
));
} else {
let repository_manager = composer.get_repository_manager().clone();
@@ -109,7 +109,7 @@ pub trait BaseDependencyCommand: BaseCommand {
.into_iter()
.collect();
repos.push(crate::repository::RepositoryInterfaceHandle::new(
- PlatformRepository::new(vec![], platform_overrides)?,
+ PlatformRepository::new(vec![], platform_overrides, None, None)?,
));
}
diff --git a/crates/shirabe/src/command/check_platform_reqs_command.rs b/crates/shirabe/src/command/check_platform_reqs_command.rs
index 903e0c4c..4e105e69 100644
--- a/crates/shirabe/src/command/check_platform_reqs_command.rs
+++ b/crates/shirabe/src/command/check_platform_reqs_command.rs
@@ -290,7 +290,7 @@ impl Command for CheckPlatformReqsCommand {
requires_sorted.sort_by(|a, b| a.0.cmp(&b.0));
installed_repo.add_repository(crate::repository::RepositoryInterfaceHandle::new(
- PlatformRepository::new(vec![], indexmap::IndexMap::new())?,
+ PlatformRepository::new(vec![], indexmap::IndexMap::new(), None, None)?,
));
let installed_repo_with_platform = installed_repo;
diff --git a/crates/shirabe/src/command/completion_trait.rs b/crates/shirabe/src/command/completion_trait.rs
index bdd42dff..67ab23be 100644
--- a/crates/shirabe/src/command/completion_trait.rs
+++ b/crates/shirabe/src/command/completion_trait.rs
@@ -80,14 +80,14 @@ pub trait CompletionTrait: BaseCommand {
.into_iter()
.map(|(k, v)| (k, PhpMixed::String(v)))
.collect();
- PlatformRepository::new(vec![], overrides)?
+ PlatformRepository::new(vec![], overrides, None, None)?
} else {
let platform_cfg = composer.get_config().borrow().get("platform");
let overrides: IndexMap<String, PhpMixed> = platform_cfg
.as_array()
.map(|m| m.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default();
- PlatformRepository::new(vec![], overrides)?
+ PlatformRepository::new(vec![], overrides, None, None)?
};
if input.get_completion_value().is_empty() {
// to reduce noise, when no text is yet entered we list only two entries for ext- and lib- prefixes
@@ -286,7 +286,7 @@ pub trait CompletionTrait: BaseCommand {
.as_array()
.map(|m| m.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default();
- let mut repos = PlatformRepository::new(vec![], overrides)?;
+ let mut repos = PlatformRepository::new(vec![], overrides, None, None)?;
let pattern =
base_package::package_name_to_regexp(&format!("{}*", input.get_completion_value()));
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index dcf0e0f8..aa1b79a8 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -684,6 +684,8 @@ impl CreateProjectCommand {
.collect(),
_ => indexmap::IndexMap::new(),
},
+ None,
+ None,
)?;
// find the latest version if there are multiple
diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs
index 8483eb56..65240e51 100644
--- a/crates/shirabe/src/command/diagnose_command.rs
+++ b/crates/shirabe/src/command/diagnose_command.rs
@@ -1223,7 +1223,7 @@ impl Command for DiagnoseCommand {
let platform_overrides_unboxed: indexmap::IndexMap<String, PhpMixed> =
platform_overrides.into_iter().collect();
let mut platform_repo =
- PlatformRepository::new(vec![], platform_overrides_unboxed).unwrap();
+ PlatformRepository::new(vec![], platform_overrides_unboxed, None, None).unwrap();
let php_pkg = <PlatformRepository as crate::repository::RepositoryInterface>::find_package(
&mut platform_repo,
"php",
diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs
index f2bf19d7..5168b2ba 100644
--- a/crates/shirabe/src/command/init_command.rs
+++ b/crates/shirabe/src/command/init_command.rs
@@ -830,7 +830,7 @@ impl Command for InitCommand {
let mut repos: Vec<crate::repository::RepositoryInterfaceHandle> =
vec![crate::repository::RepositoryInterfaceHandle::new(
- PlatformRepository::new(vec![], IndexMap::new())?,
+ PlatformRepository::new(vec![], IndexMap::new(), None, None)?,
)];
let mut create_default_packagist_repo = true;
for repo in &repositories {
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index 5c921e95..db7db488 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -42,7 +42,7 @@ pub trait PackageDiscoveryTrait: BaseCommand {
// PHP: array_merge([new PlatformRepository], RepositoryFactory::defaultReposWithDefaultManager($this->getIO()))
let mut repos: Vec<crate::repository::RepositoryInterfaceHandle> =
vec![crate::repository::RepositoryInterfaceHandle::new(
- PlatformRepository::new(vec![], IndexMap::new())
+ PlatformRepository::new(vec![], IndexMap::new(), None, None)
.expect("PlatformRepository::new should not fail"),
)];
let io_owned: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = self.get_io();
diff --git a/crates/shirabe/src/command/require_command.rs b/crates/shirabe/src/command/require_command.rs
index f4410c19..85e53a37 100644
--- a/crates/shirabe/src/command/require_command.rs
+++ b/crates/shirabe/src/command/require_command.rs
@@ -916,8 +916,12 @@ impl Command for RequireCommand {
.map(|m| m.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default();
// initialize self.repos as it is used by the PackageDiscoveryTrait
- let platform_repo =
- PlatformRepositoryHandle::new(PlatformRepository::new(vec![], platform_overrides_map)?);
+ let platform_repo = PlatformRepositoryHandle::new(PlatformRepository::new(
+ vec![],
+ platform_overrides_map,
+ None,
+ None,
+ )?);
let mut combined: Vec<crate::repository::RepositoryInterfaceHandle> =
vec![platform_repo.clone().into()];
for repo in repos {
diff --git a/crates/shirabe/src/command/search_command.rs b/crates/shirabe/src/command/search_command.rs
index 0b675109..4bf75c8b 100644
--- a/crates/shirabe/src/command/search_command.rs
+++ b/crates/shirabe/src/command/search_command.rs
@@ -111,7 +111,7 @@ impl Command for SearchCommand {
input: std::rc::Rc<std::cell::RefCell<dyn InputInterface>>,
output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
) -> anyhow::Result<i64> {
- let platform_repo = PlatformRepository::new4(vec![], IndexMap::new(), None, None)?;
+ let platform_repo = PlatformRepository::new(vec![], IndexMap::new(), None, None)?;
let io = self.get_io();
let format = input
diff --git a/crates/shirabe/src/command/show_command.rs b/crates/shirabe/src/command/show_command.rs
index 44733531..ff20f325 100644
--- a/crates/shirabe/src/command/show_command.rs
+++ b/crates/shirabe/src/command/show_command.rs
@@ -1784,8 +1784,12 @@ impl Command for ShowCommand {
platform_overrides = p.into_iter().collect();
}
}
- let platform_repo =
- PlatformRepositoryHandle::new(PlatformRepository::new(vec![], platform_overrides)?);
+ let platform_repo = PlatformRepositoryHandle::new(PlatformRepository::new(
+ vec![],
+ platform_overrides,
+ None,
+ None,
+ )?);
let mut locked_repo: Option<RepositoryInterfaceHandle> = None;
// The single-package $package binding from PHP gets surfaced here.
diff --git a/crates/shirabe/src/command/suggests_command.rs b/crates/shirabe/src/command/suggests_command.rs
index 5c73da9b..96989dc0 100644
--- a/crates/shirabe/src/command/suggests_command.rs
+++ b/crates/shirabe/src/command/suggests_command.rs
@@ -133,6 +133,8 @@ impl Command for SuggestsCommand {
installed_repos.push(RepositoryInterfaceHandle::new(PlatformRepository::new(
vec![],
platform_overrides,
+ None,
+ None,
)?));
let locked_repo = composer.get_locker().borrow_mut().get_locked_repository(
!input
@@ -151,6 +153,8 @@ impl Command for SuggestsCommand {
installed_repos.push(RepositoryInterfaceHandle::new(PlatformRepository::new(
vec![],
platform_overrides,
+ None,
+ None,
)?));
installed_repos.push(
composer
diff --git a/crates/shirabe/src/dependency_resolver/problem.rs b/crates/shirabe/src/dependency_resolver/problem.rs
index 09adafea..8ee4b816 100644
--- a/crates/shirabe/src/dependency_resolver/problem.rs
+++ b/crates/shirabe/src/dependency_resolver/problem.rs
@@ -458,7 +458,7 @@ impl Problem {
);
// Per-extension version info can't be known statically; query the real PHP
- // runtime via the RPC bridge, same as platform::runtime::Runtime::get_extension_version.
+ // runtime via the RPC bridge, as PHP's Composer\Platform\Runtime does.
let runtime_version = shirabe_php_rpc::phpversion(&ext);
let effective_version = match runtime_version {
None => "0".to_string(),
diff --git a/crates/shirabe/src/installer.rs b/crates/shirabe/src/installer.rs
index bda4e2f6..091fb35f 100644
--- a/crates/shirabe/src/installer.rs
+++ b/crates/shirabe/src/installer.rs
@@ -1323,6 +1323,8 @@ impl Installer {
Ok(PlatformRepositoryHandle::new(PlatformRepository::new(
vec![],
platform_overrides,
+ None,
+ None,
)?))
}
diff --git a/crates/shirabe/src/platform.rs b/crates/shirabe/src/platform.rs
index 6abbb769..11b00e3f 100644
--- a/crates/shirabe/src/platform.rs
+++ b/crates/shirabe/src/platform.rs
@@ -1,7 +1,9 @@
+//! `Composer\Platform\Runtime` has no Rust counterpart. Its work belongs to the running PHP
+//! interpreter, so it is ported as PHP into the RPC worker (`ShirabePlatformRuntime` in
+//! `shirabe-php-rpc`), and its callers read the answers off `shirabe_php_rpc::PlatformInfo`.
+
pub mod hhvm_detector;
-pub mod runtime;
pub mod version;
pub use hhvm_detector::*;
-pub use runtime::*;
pub use version::*;
diff --git a/crates/shirabe/src/platform/runtime.rs b/crates/shirabe/src/platform/runtime.rs
deleted file mode 100644
index 265f007c..00000000
--- a/crates/shirabe/src/platform/runtime.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-//! ref: composer/src/Composer/Platform/Runtime.php
-
-use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
-use shirabe_php_rpc::{PhpThrow, PluginValue};
-use shirabe_php_shim::{
- 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
-/// extension/constant/function probes. PHP has no such interface (the test mocks the
-/// concrete `Composer\Platform\Runtime` directly); it is introduced here to keep the
-/// consumer dependent only on trait methods.
-pub trait RuntimeInterface: std::fmt::Debug {
- fn has_constant(&self, constant_name: &str, class: Option<String>) -> bool;
- fn get_constant(&self, constant_name: &str, class: Option<String>) -> PhpMixed;
- /// `callable` carries the PHP callable spec (a function name string or a
- /// `[class, method]` list), matching PHP `invoke($callable, $arguments)`.
- fn invoke(&self, callable: PhpMixed, arguments: Vec<PhpMixed>) -> PhpMixed;
- fn has_class(&self, class: &str) -> bool;
- fn construct(&self, class: &str, arguments: Vec<PhpMixed>) -> anyhow::Result<PhpMixed>;
- fn get_extensions(&self) -> Vec<String>;
- fn get_extension_version(&self, extension: &str) -> String;
- fn get_extension_info(&self, extension: &str) -> anyhow::Result<String>;
-}
-
-#[derive(Debug)]
-pub struct Runtime;
-
-impl RuntimeInterface for Runtime {
- fn has_constant(&self, constant_name: &str, class: Option<String>) -> bool {
- shirabe_php_rpc::has_constant(&ltrim(
- &format!("{}::{}", class.as_deref().unwrap_or(""), constant_name),
- Some(":"),
- ))
- }
-
- fn get_constant(&self, constant_name: &str, class: Option<String>) -> PhpMixed {
- shirabe_php_rpc::get_constant(&ltrim(
- &format!("{}::{}", class.as_deref().unwrap_or(""), constant_name),
- Some(":"),
- ))
- }
-
- fn invoke(&self, callable: PhpMixed, arguments: Vec<PhpMixed>) -> PhpMixed {
- // PHP: return $callable(...$arguments);
- // Only the specific dynamic callables PlatformRepository actually reaches are
- // wired through php-rpc; arbitrary PHP callables are still unsupported.
- match (&callable, arguments.as_slice()) {
- (PhpMixed::String(name), [PhpMixed::String(arg)]) if name == "inet_pton" => {
- shirabe_php_rpc::inet_pton(arg)
- }
- (PhpMixed::String(name), []) if name == "curl_version" => {
- let mut version = IndexMap::new();
- if let Some(v) = shirabe_php_rpc::curl_version() {
- version.insert("version".to_string(), PhpMixed::String(v));
- }
- PhpMixed::Array(version)
- }
- (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 {
- shirabe_php_rpc::class_exists(class)
- }
-
- fn construct(&self, class: &str, arguments: Vec<PhpMixed>) -> anyhow::Result<PhpMixed> {
- 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,
- })),
- }
- }
-
- fn get_extensions(&self) -> Vec<String> {
- shirabe_php_rpc::get_loaded_extensions()
- }
-
- fn get_extension_version(&self, extension: &str) -> String {
- shirabe_php_rpc::phpversion(extension).unwrap_or_else(|| "0".to_string())
- }
-
- fn get_extension_info(&self, extension: &str) -> anyhow::Result<String> {
- Ok(shirabe_php_rpc::get_extension_info(extension))
- }
-}
-
-/// 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)
- }
-
- pub fn parse_html_extension_info(html: &str) -> String {
- let mut result: Vec<String> = vec![];
-
- let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
- if Preg::match3(
- php_regex!(r"~<h2>\s*<a[^>]*>([^<]+)</a>\s*</h2>~i"),
- html,
- Some(&mut matches),
- ) {
- result.push(trim(
- &html_entity_decode(
- matches
- .get(&CaptureKey::ByIndex(1))
- .map(|s| s.as_str())
- .unwrap_or(""),
- ),
- None,
- ));
- result.push(String::new());
- }
-
- let mut matches: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
- if Preg::match_all3(
- php_regex!(
- r#"~<tr>\s*<td class="e">\s*(.*?)\s*</td>\s*<td class="v">\s*(.*?)\s*</td>\s*</tr>~is"#
- ),
- html,
- Some(&mut matches),
- ) > 0
- {
- let group1 = matches
- .get(&CaptureKey::ByIndex(1))
- .cloned()
- .unwrap_or_default();
- let group2 = matches
- .get(&CaptureKey::ByIndex(2))
- .cloned()
- .unwrap_or_default();
- let count = std::cmp::min(group1.len(), group2.len());
-
- for i in 0..count {
- let key = trim(&html_entity_decode(&strip_tags(&group1[i])), None);
- let value = trim(&html_entity_decode(&strip_tags(&group2[i])), None);
- result.push(format!("{} => {}", key, value));
- }
- }
-
- implode("\n", &result)
- }
-}
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 5b205ba0..944bb4db 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -11,16 +11,14 @@ use crate::package::PackageInterfaceHandle;
use crate::package::version::VersionParser;
use crate::platform::HhvmDetector;
use crate::platform::HhvmDetectorInterface;
-use crate::platform::Runtime;
-use crate::platform::RuntimeInterface;
use crate::platform::Version;
use crate::plugin::plugin_interface::{self};
use crate::repository::ArrayRepository;
use crate::repository::RepositoryInterface;
-use crate::util::Silencer;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
use shirabe_external_packages::composer::xdebug_handler::XdebugHandler;
+use shirabe_php_rpc::PlatformInfo;
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_map_str_fn,
array_slice_strs, explode, get_class, implode, in_array_strict, is_string, php_regex,
@@ -47,27 +45,21 @@ pub struct PlatformRepository {
pub(crate) version_parser: Option<VersionParser>,
pub(crate) overrides: IndexMap<String, PlatformOverride>,
pub(crate) disabled_packages: IndexMap<String, CompletePackageInterfaceHandle>,
- pub(crate) runtime: Box<dyn RuntimeInterface>,
+ pub(crate) platform_info: Option<PlatformInfo>,
pub(crate) hhvm_detector: Box<dyn HhvmDetectorInterface>,
}
impl PlatformRepository {
const PLATFORM_PACKAGE_REGEX: &'static str = "{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer(?:-(?:plugin|runtime)-api)?)$}iD";
+ /// A `None` `platform_info` is resolved from the PHP worker on the first `initialize()`, so
+ /// constructing the repository on its own never starts the worker.
pub fn new(
packages: Vec<PackageInterfaceHandle>,
overrides: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<Self> {
- Self::new4(packages, overrides, None, None)
- }
-
- pub fn new4(
- packages: Vec<PackageInterfaceHandle>,
- overrides: IndexMap<String, PhpMixed>,
- runtime: Option<Box<dyn RuntimeInterface>>,
+ platform_info: Option<PlatformInfo>,
hhvm_detector: Option<Box<dyn HhvmDetectorInterface>>,
) -> anyhow::Result<Self> {
- let runtime: Box<dyn RuntimeInterface> = runtime.unwrap_or_else(|| Box::new(Runtime));
let hhvm_detector: Box<dyn HhvmDetectorInterface> =
hhvm_detector.unwrap_or_else(|| Box::new(HhvmDetector::new(None, None)));
let mut overrides_map: IndexMap<String, PlatformOverride> = IndexMap::new();
@@ -107,7 +99,7 @@ impl PlatformRepository {
version_parser: None,
overrides: overrides_map,
disabled_packages: IndexMap::new(),
- runtime,
+ platform_info,
hhvm_detector,
};
for package in packages {
@@ -139,6 +131,11 @@ impl PlatformRepository {
pub(crate) fn initialize(&mut self) -> anyhow::Result<()> {
self.inner.initialize();
+ let platform_info = self
+ .platform_info
+ .get_or_insert_with(|| shirabe_php_rpc::get_platform_info().clone())
+ .clone();
+
let mut libraries: IndexMap<String, bool> = IndexMap::new();
self.version_parser = Some(VersionParser::new());
@@ -214,7 +211,7 @@ impl PlatformRepository {
CompletePackageHandle::from_complete_package(composer_runtime_api).into(),
)?;
- let php_version_const = self.runtime.get_constant("PHP_VERSION", None);
+ let php_version_const = platform_info.get_constant("PHP_VERSION", None);
let php_version_str = match &php_version_const {
PhpMixed::String(s) => s.clone(),
_ => "".to_string(),
@@ -245,8 +242,7 @@ impl PlatformRepository {
php.set_description("The PHP interpreter".to_string());
self.add_package(CompletePackageHandle::from_complete_package(php).into())?;
- if self
- .runtime
+ if platform_info
.get_constant("PHP_DEBUG", None)
.as_bool()
.unwrap_or(false)
@@ -260,9 +256,8 @@ impl PlatformRepository {
self.add_package(CompletePackageHandle::from_complete_package(phpdebug).into())?;
}
- if self.runtime.has_constant("PHP_ZTS", None)
- && self
- .runtime
+ if platform_info.has_constant("PHP_ZTS", None)
+ && platform_info
.get_constant("PHP_ZTS", None)
.as_bool()
.unwrap_or(false)
@@ -276,8 +271,7 @@ impl PlatformRepository {
self.add_package(CompletePackageHandle::from_complete_package(phpzts).into())?;
}
- if self
- .runtime
+ if platform_info
.get_constant("PHP_INT_SIZE", None)
.as_int()
.map(|v| v == 8)
@@ -294,15 +288,8 @@ impl PlatformRepository {
// The AF_INET6 constant is only defined if ext-sockets is available but
// IPv6 support might still be available.
- let has_inet6 = self.runtime.has_constant("AF_INET6", None);
- // PHP: Silencer::call([$this->runtime, 'invoke'], 'inet_pton', ['::'])
- let inet_pton_check = Silencer::call(|| {
- Ok::<PhpMixed, anyhow::Error>(self.runtime.invoke(
- PhpMixed::String("inet_pton".to_string()),
- vec![PhpMixed::String("::".to_string())],
- ))
- })
- .unwrap_or(PhpMixed::Bool(false));
+ let has_inet6 = platform_info.has_constant("AF_INET6", None);
+ let inet_pton_check = &platform_info.inet_pton_ipv6;
if has_inet6 || !matches!(inet_pton_check, PhpMixed::Bool(false)) {
let mut php_ipv6 =
CompletePackage::new("php-ipv6".to_string(), version, pretty_version);
@@ -310,7 +297,7 @@ impl PlatformRepository {
self.add_package(CompletePackageHandle::from_complete_package(php_ipv6).into())?;
}
- let loaded_extensions = self.runtime.get_extensions();
+ let loaded_extensions = platform_info.get_extensions().to_vec();
// Extensions scanning
for name in &loaded_extensions {
@@ -318,7 +305,7 @@ impl PlatformRepository {
continue;
}
- self.add_extension(name, &self.runtime.get_extension_version(name))?;
+ self.add_extension(name, platform_info.get_extension_version(name))?;
}
// Check for Xdebug in a restarted process
@@ -340,13 +327,13 @@ impl PlatformRepository {
for name in &loaded_extensions {
match name.as_str() {
"amqp" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// librabbitmq version => 0.9.0
let mut librabbitmq_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^librabbitmq version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut librabbitmq_matches),
) {
self.add_library(
@@ -365,7 +352,7 @@ impl PlatformRepository {
let mut protocol_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^AMQP protocol version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut protocol_matches),
) {
let version_str = protocol_matches
@@ -384,13 +371,13 @@ impl PlatformRepository {
}
"bz2" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// BZip2 Version => 1.0.6, 6-Sept-2010
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^BZip2 Version => (?<version>.*),/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -407,9 +394,7 @@ impl PlatformRepository {
}
"curl" => {
- let curl_version = self
- .runtime
- .invoke(PhpMixed::String("curl_version".to_string()), vec![]);
+ let curl_version = &platform_info.curl_version;
let curl_version_str = curl_version
.as_array()
.and_then(|m| m.get("version"))
@@ -419,13 +404,13 @@ impl PlatformRepository {
self.add_library(&mut libraries, name, Some(cv), None, &[], &[])?;
}
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// SSL Version => OpenSSL/1.0.1t
let mut ssl_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("{^SSL Version => (?<library>[^/]+)/(?<version>.+)$}im"),
- &info,
+ info,
Some(&mut ssl_matches),
) {
let ssl_library_raw = ssl_matches
@@ -495,7 +480,7 @@ impl PlatformRepository {
php_regex!(
"{^libSSH Version => (?<library>[^/]+)/(?<version>.+?)(?:/.*)?$}im"
),
- &info,
+ info,
Some(&mut ssh_matches),
) {
let ssh_library = ssh_matches
@@ -520,7 +505,7 @@ impl PlatformRepository {
let mut zlib_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("{^ZLib Version => (?<version>.+)$}im"),
- &info,
+ info,
Some(&mut zlib_matches),
) {
self.add_library(
@@ -537,13 +522,13 @@ impl PlatformRepository {
}
"date" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// timelib version => 2018.03
let mut timelib_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^timelib version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut timelib_matches),
) {
self.add_library(
@@ -562,7 +547,7 @@ impl PlatformRepository {
let mut zoneinfo_source_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^Timezone Database => (?<source>internal|external)$/im"),
- &info,
+ info,
Some(&mut zoneinfo_source_matches),
) {
let external = zoneinfo_source_matches
@@ -574,7 +559,7 @@ impl PlatformRepository {
php_regex!(
"/^\"Olson\" Timezone Database Version => (?<version>.+?)(?:\\.system)?$/im"
),
- &info,
+ info,
Some(&mut zoneinfo_matches),
) {
let zoneinfo_version = zoneinfo_matches
@@ -608,13 +593,13 @@ impl PlatformRepository {
}
"fileinfo" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// libmagic => 537
let mut magic_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libmagic => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut magic_matches),
) {
self.add_library(
@@ -631,7 +616,7 @@ impl PlatformRepository {
}
"gd" => {
- let gd_version = self.runtime.get_constant("GD_VERSION", None);
+ let gd_version = platform_info.get_constant("GD_VERSION", None);
let gd_version_str = match &gd_version {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -645,12 +630,12 @@ impl PlatformRepository {
&[],
)?;
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut libjpeg_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im"),
- &info,
+ info,
Some(&mut libjpeg_matches),
) {
let libjpeg_version = libjpeg_matches
@@ -671,7 +656,7 @@ impl PlatformRepository {
let mut libpng_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libPNG Version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut libpng_matches),
) {
self.add_library(
@@ -689,7 +674,7 @@ impl PlatformRepository {
let mut freetype_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^FreeType Version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut freetype_matches),
) {
self.add_library(
@@ -707,7 +692,7 @@ impl PlatformRepository {
let mut libxpm_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libXpm Version => (?<versionId>\\d+)$/im"),
- &info,
+ info,
Some(&mut libxpm_matches),
) {
let version_id: i64 = libxpm_matches
@@ -727,7 +712,7 @@ impl PlatformRepository {
}
"gmp" => {
- let gmp_version = self.runtime.get_constant("GMP_VERSION", None);
+ let gmp_version = platform_info.get_constant("GMP_VERSION", None);
let gmp_version_str = match &gmp_version {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -743,7 +728,7 @@ impl PlatformRepository {
}
"iconv" => {
- let iconv_version = self.runtime.get_constant("ICONV_VERSION", None);
+ let iconv_version = platform_info.get_constant("ICONV_VERSION", None);
let iconv_version_str = match &iconv_version {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -759,12 +744,12 @@ impl PlatformRepository {
}
"intl" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let description = "The ICU unicode and globalization support library";
// Truthy check is for testing only so we can make the condition fail
- if self.runtime.has_constant("INTL_ICU_VERSION", None) {
- let intl_icu_version = self.runtime.get_constant("INTL_ICU_VERSION", None);
+ if platform_info.has_constant("INTL_ICU_VERSION", None) {
+ let intl_icu_version = platform_info.get_constant("INTL_ICU_VERSION", None);
let intl_icu_str = match &intl_icu_version {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -781,7 +766,7 @@ impl PlatformRepository {
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^ICU version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -801,7 +786,7 @@ impl PlatformRepository {
let mut zoneinfo_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^ICU TZData version => (?<version>.*)$/im"),
- &info,
+ info,
Some(&mut zoneinfo_matches),
) {
let zi_version = zoneinfo_matches
@@ -821,22 +806,11 @@ impl PlatformRepository {
}
// Add a separate version for the CLDR library version
- if self.runtime.has_class("ResourceBundle") {
- let resource_bundle = self.runtime.invoke(
- PhpMixed::List(vec![
- PhpMixed::String("ResourceBundle".to_string()),
- PhpMixed::String("create".to_string()),
- ]),
- vec![
- PhpMixed::String("root".to_string()),
- PhpMixed::String("ICUDATA".to_string()),
- PhpMixed::Bool(false),
- ],
- );
+ if platform_info.has_class("ResourceBundle") {
+ let resource_bundle = &platform_info.resource_bundle;
if !matches!(resource_bundle, PhpMixed::Null) {
- // TODO(plugin): `$resourceBundle->get('Version')` dynamic method call
let version_value =
- Self::resource_bundle_get(&resource_bundle, "Version");
+ Self::resource_bundle_get(resource_bundle, "Version");
let version_str = match version_value {
PhpMixed::String(s) => Some(s),
_ => None,
@@ -852,16 +826,10 @@ impl PlatformRepository {
}
}
- if self.runtime.has_class("IntlChar") {
- let intl_char_versions = self.runtime.invoke(
- PhpMixed::List(vec![
- PhpMixed::String("IntlChar".to_string()),
- PhpMixed::String("getUnicodeVersion".to_string()),
- ]),
- vec![],
- );
+ if platform_info.has_class("IntlChar") {
+ let intl_char_versions = &platform_info.intl_char_unicode_version;
let sliced =
- shirabe_php_shim::array_slice_mixed(&intl_char_versions, 0, Some(3));
+ shirabe_php_shim::array_slice_mixed(intl_char_versions, 0, Some(3));
let joined = implode(".", &Self::php_array_to_string_vec(&sliced));
self.add_library(
&mut libraries,
@@ -875,10 +843,9 @@ impl PlatformRepository {
}
"imagick" => {
- let image_magick_version = self.runtime.construct("Imagick", Vec::new())?;
- // TODO(plugin): `->getVersion()` is a dynamic method call on Imagick
+ let image_magick_version = &platform_info.imagick;
let image_magick_version_str =
- Self::imagick_get_version_string(&image_magick_version);
+ Self::imagick_get_version_string(image_magick_version);
// 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org
// 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
@@ -907,17 +874,17 @@ impl PlatformRepository {
}
"ldap" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
let mut vendor_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^Vendor Version => (?<versionId>\\d+)$/im"),
- &info,
+ info,
Some(&mut matches),
) && Preg::is_match3(
php_regex!("/^Vendor Name => (?<vendor>.+)$/im"),
- &info,
+ info,
Some(&mut vendor_matches),
) {
let version_id: i64 = matches
@@ -950,7 +917,7 @@ impl PlatformRepository {
.collect();
let libxml_provides: Vec<String> =
array_map_str_fn(|extension| format!("{}-libxml", extension), &intersected);
- let libxml_dotted = self.runtime.get_constant("LIBXML_DOTTED_VERSION", None);
+ let libxml_dotted = platform_info.get_constant("LIBXML_DOTTED_VERSION", None);
let libxml_dotted_str = match &libxml_dotted {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -966,13 +933,13 @@ impl PlatformRepository {
}
"mbstring" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// libmbfl version => 1.3.2
let mut libmbfl_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libmbfl version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut libmbfl_matches),
) {
self.add_library(
@@ -987,8 +954,8 @@ impl PlatformRepository {
)?;
}
- if self.runtime.has_constant("MB_ONIGURUMA_VERSION", None) {
- let oniguruma = self.runtime.get_constant("MB_ONIGURUMA_VERSION", None);
+ if platform_info.has_constant("MB_ONIGURUMA_VERSION", None) {
+ let oniguruma = platform_info.get_constant("MB_ONIGURUMA_VERSION", None);
let oniguruma_str = match &oniguruma {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -1010,7 +977,7 @@ impl PlatformRepository {
php_regex!(
"/^(?:oniguruma|Multibyte regex \\(oniguruma\\)) version => (?<version>.+)$/im"
),
- &info,
+ info,
Some(&mut oniguruma_matches),
) {
self.add_library(
@@ -1028,13 +995,13 @@ impl PlatformRepository {
}
"memcached" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// libmemcached version => 1.0.18
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libmemcached version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1051,7 +1018,7 @@ impl PlatformRepository {
}
"openssl" => {
- let openssl_text = self.runtime.get_constant("OPENSSL_VERSION_TEXT", None);
+ let openssl_text = platform_info.get_constant("OPENSSL_VERSION_TEXT", None);
let openssl_text_str = match &openssl_text {
PhpMixed::String(s) => s.clone(),
_ => "".to_string(),
@@ -1086,7 +1053,7 @@ impl PlatformRepository {
}
"pcre" => {
- let pcre_version = self.runtime.get_constant("PCRE_VERSION", None);
+ let pcre_version = platform_info.get_constant("PCRE_VERSION", None);
let pcre_version_str = match &pcre_version {
PhpMixed::String(s) => s.clone(),
_ => "".to_string(),
@@ -1095,13 +1062,13 @@ impl PlatformRepository {
Preg::replace(php_regex!("{^(\\S+).*}"), "$1", &pcre_version_str);
self.add_library(&mut libraries, name, Some(&stripped), None, &[], &[])?;
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// PCRE Unicode Version => 12.1.0
let mut pcre_unicode_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^PCRE Unicode Version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut pcre_unicode_matches),
) {
self.add_library(
@@ -1118,14 +1085,14 @@ impl PlatformRepository {
}
"mysqlnd" | "pdo_mysql" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!(
"/^(?:Client API version|Version) => mysqlnd (?<version>.+?) /mi"
),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1142,12 +1109,12 @@ impl PlatformRepository {
}
"mongodb" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut libmongoc_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libmongoc bundled version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut libmongoc_matches),
) {
self.add_library(
@@ -1165,7 +1132,7 @@ impl PlatformRepository {
let mut libbson_matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libbson bundled version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut libbson_matches),
) {
self.add_library(
@@ -1182,8 +1149,8 @@ impl PlatformRepository {
}
"pgsql" => {
- if self.runtime.has_constant("PGSQL_LIBPQ_VERSION", None) {
- let pq_version = self.runtime.get_constant("PGSQL_LIBPQ_VERSION", None);
+ if platform_info.has_constant("PGSQL_LIBPQ_VERSION", None) {
+ let pq_version = platform_info.get_constant("PGSQL_LIBPQ_VERSION", None);
let pq_version_str = match &pq_version {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -1198,12 +1165,12 @@ impl PlatformRepository {
)?;
} else {
// intentional fall-through to next case...
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^PostgreSQL\\(libpq\\) Version => (?<version>.*)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1221,12 +1188,12 @@ impl PlatformRepository {
}
"pdo_pgsql" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^PostgreSQL\\(libpq\\) Version => (?<version>.*)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1243,14 +1210,14 @@ impl PlatformRepository {
}
"pq" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
// Used Library => Compiled => Linked
// libpq => 14.3 (Ubuntu 14.3-1.pgdg22.04+1) => 15.0.2
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libpq => (?<compiled>.+) => (?<linked>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1267,7 +1234,7 @@ impl PlatformRepository {
}
"rdkafka" => {
- if self.runtime.has_constant("RD_KAFKA_VERSION", None) {
+ if platform_info.has_constant("RD_KAFKA_VERSION", None) {
// Interpreted as hex MM.mm.rr.xx:
// - MM = Major
// - mm = minor
@@ -1275,8 +1242,7 @@ impl PlatformRepository {
// - xx = pre-release id (0xff is the final release)
//
// pre-release ID in practice is always 0xff even for RCs etc, so we ignore it
- let lib_rd_kafka_version_int = self
- .runtime
+ let lib_rd_kafka_version_int = platform_info
.get_constant("RD_KAFKA_VERSION", None)
.as_int()
.unwrap_or(0);
@@ -1298,8 +1264,8 @@ impl PlatformRepository {
}
"libsodium" | "sodium" => {
- if self.runtime.has_constant("SODIUM_LIBRARY_VERSION", None) {
- let sodium = self.runtime.get_constant("SODIUM_LIBRARY_VERSION", None);
+ if platform_info.has_constant("SODIUM_LIBRARY_VERSION", None) {
+ let sodium = platform_info.get_constant("SODIUM_LIBRARY_VERSION", None);
let sodium_str = match &sodium {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -1324,12 +1290,12 @@ impl PlatformRepository {
}
"sqlite3" | "pdo_sqlite" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^SQLite Library => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1346,12 +1312,12 @@ impl PlatformRepository {
}
"ssh2" => {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^libssh2 version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1368,7 +1334,8 @@ impl PlatformRepository {
}
"xsl" => {
- let libxslt_version = self.runtime.get_constant("LIBXSLT_DOTTED_VERSION", None);
+ let libxslt_version =
+ platform_info.get_constant("LIBXSLT_DOTTED_VERSION", None);
let libxslt_str = match &libxslt_version {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -1382,13 +1349,13 @@ impl PlatformRepository {
&[],
)?;
- let info = self.runtime.get_extension_info("xsl")?;
+ let info = platform_info.get_extension_info("xsl");
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!(
"/^libxslt compiled against libxml Version => (?<version>.+)$/im"
),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1405,12 +1372,12 @@ impl PlatformRepository {
}
"yaml" => {
- let info = self.runtime.get_extension_info("yaml")?;
+ let info = platform_info.get_extension_info("yaml");
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^LibYAML Version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1427,13 +1394,9 @@ impl PlatformRepository {
}
"zip" => {
- if self
- .runtime
- .has_constant("LIBZIP_VERSION", Some("ZipArchive".to_string()))
- {
- let libzip = self
- .runtime
- .get_constant("LIBZIP_VERSION", Some("ZipArchive".to_string()));
+ if platform_info.has_constant("LIBZIP_VERSION", Some("ZipArchive")) {
+ let libzip =
+ platform_info.get_constant("LIBZIP_VERSION", Some("ZipArchive"));
let libzip_str = match &libzip {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -1450,8 +1413,8 @@ impl PlatformRepository {
}
"zlib" => {
- if self.runtime.has_constant("ZLIB_VERSION", None) {
- let zlib = self.runtime.get_constant("ZLIB_VERSION", None);
+ if platform_info.has_constant("ZLIB_VERSION", None) {
+ let zlib = platform_info.get_constant("ZLIB_VERSION", None);
let zlib_str = match &zlib {
PhpMixed::String(s) => Some(s.clone()),
_ => None,
@@ -1467,11 +1430,11 @@ impl PlatformRepository {
// Linked Version => 1.2.8
} else {
- let info = self.runtime.get_extension_info(name)?;
+ let info = platform_info.get_extension_info(name);
let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
if Preg::is_match3(
php_regex!("/^Linked Version => (?<version>.+)$/im"),
- &info,
+ info,
Some(&mut matches),
) {
self.add_library(
@@ -1840,7 +1803,7 @@ impl PlatformRepository {
}
/// 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.
+ /// [`PlatformInfo`] carries 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)
}
diff --git a/crates/shirabe/tests/package/version/version_selector_test.rs b/crates/shirabe/tests/package/version/version_selector_test.rs
index 13544399..16ad5fe2 100644
--- a/crates/shirabe/tests/package/version/version_selector_test.rs
+++ b/crates/shirabe/tests/package/version/version_selector_test.rs
@@ -120,7 +120,7 @@ fn test_latest_version_is_returned_that_matches_php_requirements() {
let mut overrides: IndexMap<String, PhpMixed> = IndexMap::new();
overrides.insert("php".to_string(), PhpMixed::String("5.5.0".to_string()));
- let mut platform = PlatformRepository::new(vec![], overrides).unwrap();
+ let mut platform = PlatformRepository::new(vec![], overrides, None, None).unwrap();
let package0 = get_package("foo/bar", "0.9.0");
package0.__set_requires(IndexMap::from([(
@@ -216,7 +216,7 @@ fn test_latest_version_is_returned_that_matches_ext_requirements() {
let mut overrides: IndexMap<String, PhpMixed> = IndexMap::new();
overrides.insert("ext-zip".to_string(), PhpMixed::String("5.3.0".to_string()));
- let mut platform = PlatformRepository::new(vec![], overrides).unwrap();
+ let mut platform = PlatformRepository::new(vec![], overrides, None, None).unwrap();
let package1 = get_package("foo/bar", "1.0.0");
package1.__set_requires(IndexMap::from([(
@@ -263,7 +263,7 @@ fn test_latest_version_is_returned_that_matches_ext_requirements() {
fn test_latest_version_is_returned_that_matches_platform_ext() {
let package_name = "foo/bar";
- let mut platform = PlatformRepository::new(vec![], IndexMap::new()).unwrap();
+ let mut platform = PlatformRepository::new(vec![], IndexMap::new(), None, None).unwrap();
let package1 = get_package("foo/bar", "1.0.0");
let package2 = get_package("foo/bar", "2.0.0");
@@ -311,7 +311,7 @@ fn test_latest_version_is_returned_that_matches_composer_requirements() {
"composer-runtime-api".to_string(),
PhpMixed::String("1.0.0".to_string()),
);
- let mut platform = PlatformRepository::new(vec![], overrides).unwrap();
+ let mut platform = PlatformRepository::new(vec![], overrides, None, None).unwrap();
let package1 = get_package("foo/bar", "1.0.0");
package1.__set_requires(IndexMap::from([(
diff --git a/crates/shirabe/tests/platform/main.rs b/crates/shirabe/tests/platform/main.rs
index 936049ac..3cf2cf48 100644
--- a/crates/shirabe/tests/platform/main.rs
+++ b/crates/shirabe/tests/platform/main.rs
@@ -1,3 +1,2 @@
mod hhvm_detector_test;
-mod runtime_test;
mod version_test;
diff --git a/crates/shirabe/tests/platform/runtime_test.rs b/crates/shirabe/tests/platform/runtime_test.rs
deleted file mode 100644
index ebdeaffa..00000000
--- a/crates/shirabe/tests/platform/runtime_test.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-//! ref: composer/tests/Composer/Test/Platform/RuntimeTest.php
-
-use shirabe::platform::runtime::Runtime;
-
-#[test]
-fn test_parse_extension_info() {
- for (html_input, expected_output) in provide_extension_infos() {
- assert_eq!(
- expected_output,
- Runtime::parse_html_extension_info(html_input)
- );
- }
-}
-
-fn provide_extension_infos() -> Vec<(&'static str, &'static str)> {
- vec![(
- // 'pdo_sqlite'
- "<h2><a name=\"module_pdo_sqlite\" href=\"#module_pdo_sqlite\">pdo_sqlite</a></h2>
-<table>
-<tr><td class=\"e\">PDO Driver for SQLite 3.x </td><td class=\"v\">enabled </td></tr>
-<tr><td class=\"e\">SQLite Library </td><td class=\"v\">3.40.1 </td></tr>
-</table>",
- "pdo_sqlite
-
-PDO Driver for SQLite 3.x => enabled
-SQLite Library => 3.40.1",
- )]
-}
diff --git a/crates/shirabe/tests/repository/platform_repository_test.rs b/crates/shirabe/tests/repository/platform_repository_test.rs
index 3a4bf45d..573f96ed 100644
--- a/crates/shirabe/tests/repository/platform_repository_test.rs
+++ b/crates/shirabe/tests/repository/platform_repository_test.rs
@@ -1,31 +1,15 @@
//! ref: composer/tests/Composer/Test/Repository/PlatformRepositoryTest.php
use indexmap::IndexMap;
-use mockall::predicate::eq;
use shirabe::package::{BasePackageHandle, Link};
-use shirabe::platform::{HhvmDetectorInterface, RuntimeInterface};
+use shirabe::platform::HhvmDetectorInterface;
use shirabe::repository::{
FindPackageConstraint, PlatformRepository, RepositoryInterface, SEARCH_NAME,
};
+use shirabe_php_rpc::PlatformInfo;
use shirabe_php_shim::PhpMixed;
use shirabe_semver::constraint::SimpleConstraint;
-// The Runtime/HhvmDetector seams are concrete structs in PHP; the tests mock them
-// directly.
-mockall::mock! {
- pub Runtime {}
- impl RuntimeInterface for Runtime {
- fn has_constant(&self, constant_name: &str, class: Option<String>) -> bool;
- fn get_constant(&self, constant_name: &str, class: Option<String>) -> PhpMixed;
- fn invoke(&self, callable: PhpMixed, arguments: Vec<PhpMixed>) -> PhpMixed;
- fn has_class(&self, class: &str) -> bool;
- fn construct(&self, class: &str, arguments: Vec<PhpMixed>) -> anyhow::Result<PhpMixed>;
- fn get_extensions(&self) -> Vec<String>;
- fn get_extension_version(&self, extension: &str) -> String;
- fn get_extension_info(&self, extension: &str) -> anyhow::Result<String>;
- }
-}
-
mockall::mock! {
pub HhvmDetector {}
impl HhvmDetectorInterface for HhvmDetector {
@@ -34,25 +18,71 @@ mockall::mock! {
}
}
-// The seam traits require `Debug` (so `PlatformRepository` can derive it); mockall does
+// The seam trait requires `Debug` (so `PlatformRepository` can derive it); mockall does
// not generate it for mocks.
-impl std::fmt::Debug for MockRuntime {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_str("MockRuntime")
- }
-}
-
impl std::fmt::Debug for MockHhvmDetector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("MockHhvmDetector")
}
}
-/// PHP: ltrim($class.'::'.$constant, ':')
-fn constant_key(constant_name: &str, class: Option<&str>) -> String {
- format!("{}::{}", class.unwrap_or(""), constant_name)
- .trim_start_matches(':')
- .to_string()
+/// The payload form of what a dataset's `Runtime` mock answers: the listed constants and classes
+/// exist, and each extension is loaded at `extension_version` with `info` as its info() output.
+fn platform_info(
+ constants: Vec<(String, Option<String>, PhpMixed)>,
+ extensions: Vec<String>,
+ extension_version: &str,
+ info: Option<&str>,
+ functions: &[(PhpMixed, Vec<PhpMixed>, PhpMixed)],
+ class_definitions: &[ClassDef],
+) -> PlatformInfo {
+ let mut platform_info = PlatformInfo::default();
+
+ for (constant_name, class, value) in constants {
+ platform_info.__set_constant(&constant_name, class.as_deref(), value);
+ }
+
+ for extension in &extensions {
+ platform_info.__set_extension_info(extension, info.unwrap_or_default());
+ }
+ platform_info.__set_extensions(extensions, extension_version);
+
+ for (callable, arguments, result) in functions {
+ match (callable, arguments.as_slice()) {
+ (PhpMixed::String(name), []) if name == "curl_version" => {
+ platform_info.curl_version = result.clone();
+ }
+ (PhpMixed::String(name), [PhpMixed::String(address)])
+ if name == "inet_pton" && address == "::" =>
+ {
+ platform_info.inet_pton_ipv6 = result.clone();
+ }
+ (PhpMixed::List(spec), _) => match spec.as_slice() {
+ [PhpMixed::String(class), PhpMixed::String(method)]
+ if class == "ResourceBundle" && method == "create" =>
+ {
+ platform_info.resource_bundle = result.clone();
+ }
+ [PhpMixed::String(class), PhpMixed::String(method)]
+ if class == "IntlChar" && method == "getUnicodeVersion" =>
+ {
+ platform_info.intl_char_unicode_version = result.clone();
+ }
+ other => panic!("the platform payload does not report {other:?}"),
+ },
+ other => panic!("the platform payload does not report {other:?}"),
+ }
+ }
+
+ for definition in class_definitions {
+ match (definition.class, &definition.construct) {
+ ("Imagick", Some((_arguments, result))) => platform_info.imagick = result.clone(),
+ (class, None) => platform_info.__set_class(class),
+ (class, Some(_)) => panic!("the platform payload does not construct {class}"),
+ }
+ }
+
+ platform_info
}
#[test]
@@ -64,7 +94,7 @@ fn test_hhvm_package() {
.returning(|| Some("2.1.0".to_string()));
let mut platform_repository =
- PlatformRepository::new4(vec![], IndexMap::new(), None, Some(Box::new(hhvm_detector)))
+ PlatformRepository::new(vec![], IndexMap::new(), None, Some(Box::new(hhvm_detector)))
.unwrap();
let hhvm = platform_repository
@@ -137,44 +167,20 @@ fn php_flavor_test_cases() -> Vec<(
#[test]
fn test_php_version() {
for (constants, packages, functions) in php_flavor_test_cases() {
- let constants_has = constants.clone();
- let constants_get = constants.clone();
-
- let mut runtime = MockRuntime::new();
- runtime
- .expect_get_extensions()
- .times(..)
- .returning(Vec::new);
- runtime
- .expect_has_constant()
- .times(..)
- .returning(move |constant, class| {
- constants_has.contains_key(&constant_key(constant, class.as_deref()))
- });
- runtime
- .expect_get_constant()
- .times(..)
- .returning(move |constant, class| {
- constants_get
- .get(&constant_key(constant, class.as_deref()))
- .cloned()
- .unwrap_or(PhpMixed::Null)
- });
- runtime
- .expect_invoke()
- .times(..)
- .returning(move |callable, arguments| {
- for (c, a, ret) in &functions {
- if *c == callable && *a == arguments {
- return ret.clone();
- }
- }
- PhpMixed::Null
- });
+ let platform_info = platform_info(
+ constants
+ .into_iter()
+ .map(|(constant_name, value)| (constant_name, None, value))
+ .collect(),
+ Vec::new(),
+ "",
+ None,
+ &functions,
+ &[],
+ );
let mut repository =
- PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None)
- .unwrap();
+ PlatformRepository::new(vec![], IndexMap::new(), Some(platform_info), None).unwrap();
for (package_name, version) in packages {
let package = repository
@@ -198,22 +204,16 @@ fn test_php_version() {
#[test]
fn test_inet_pton_regression() {
- let mut runtime = MockRuntime::new();
// PHP: ->expects(self::once())->method('invoke')->with('inet_pton', ['::'])->willReturn(false).
- runtime
- .expect_invoke()
- .with(
- eq(PhpMixed::String("inet_pton".to_string())),
- eq(vec![PhpMixed::String("::".to_string())]),
- )
- .times(1)
- .returning(|_callable, _arguments| PhpMixed::Bool(false));
- // suppressing PHP_ZTS & AF_INET6
- runtime
- .expect_has_constant()
- .times(..)
- .returning(|_, _| false);
+ // TODO(phase-d): the payload reports the result of `@inet_pton('::')` instead of answering a
+ // call, so there is nothing left for the once() call-count check to observe.
+ let functions = [(
+ PhpMixed::String("inet_pton".to_string()),
+ vec![PhpMixed::String("::".to_string())],
+ PhpMixed::Bool(false),
+ )];
+ // suppressing PHP_ZTS & AF_INET6 by leaving them undefined
let constants: IndexMap<String, PhpMixed> = IndexMap::from([
(
"PHP_VERSION".to_string(),
@@ -221,22 +221,21 @@ fn test_inet_pton_regression() {
),
("PHP_DEBUG".to_string(), PhpMixed::Bool(false)),
]);
- runtime
- .expect_get_constant()
- .times(..)
- .returning(move |constant, class| {
- constants
- .get(&constant_key(constant, class.as_deref()))
- .cloned()
- .unwrap_or(PhpMixed::Null)
- });
- runtime
- .expect_get_extensions()
- .times(..)
- .returning(Vec::new);
+
+ let platform_info = platform_info(
+ constants
+ .into_iter()
+ .map(|(constant_name, value)| (constant_name, None, value))
+ .collect(),
+ Vec::new(),
+ "",
+ None,
+ &functions,
+ &[],
+ );
let mut repository =
- PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None).unwrap();
+ PlatformRepository::new(vec![], IndexMap::new(), Some(platform_info), None).unwrap();
let package = repository
.find_package("php-ipv6", FindPackageConstraint::String("*".to_string()))
.unwrap();
@@ -1630,79 +1629,17 @@ fn test_library_information() {
PhpMixed::String("7.1.0".to_string()),
));
- let functions = case.functions.clone();
- let info = case.info.map(|s| s.to_string());
-
- let exts_for_get = extensions.clone();
- let constants_has = constants.clone();
- let constants_get = constants.clone();
-
- let mut runtime = MockRuntime::new();
- runtime
- .expect_get_extensions()
- .times(..)
- .returning(move || exts_for_get.clone());
- runtime
- .expect_get_extension_version()
- .times(..)
- .returning(move |_extension| extension_version.to_string());
- runtime
- .expect_get_extension_info()
- .times(..)
- .returning(move |_extension| Ok(info.clone().unwrap_or_default()));
- runtime
- .expect_invoke()
- .times(..)
- .returning(move |callable, arguments| {
- for (c, a, ret) in &functions {
- if *c == callable && *a == arguments {
- return ret.clone();
- }
- }
- PhpMixed::Null
- });
- runtime
- .expect_has_constant()
- .times(..)
- .returning(move |constant, class| {
- constants_has
- .iter()
- .any(|(n, c, _)| n == constant && c.as_deref() == class.as_deref())
- });
- runtime
- .expect_get_constant()
- .times(..)
- .returning(move |constant, class| {
- constants_get
- .iter()
- .find(|(n, c, _)| n == constant && c.as_deref() == class.as_deref())
- .map(|(_, _, v)| v.clone())
- .unwrap_or(PhpMixed::Null)
- });
- let class_definitions_has = case.class_definitions.clone();
- let class_definitions_construct = case.class_definitions.clone();
- runtime
- .expect_has_class()
- .times(..)
- .returning(move |class| class_definitions_has.iter().any(|d| d.class == class));
- runtime
- .expect_construct()
- .times(..)
- .returning(move |class, arguments| {
- for d in &class_definitions_construct {
- if d.class == class
- && let Some((args, ret)) = &d.construct
- && *args == arguments
- {
- return Ok(ret.clone());
- }
- }
- Ok(PhpMixed::Null)
- });
+ let platform_info = platform_info(
+ constants,
+ extensions.clone(),
+ extension_version,
+ case.info,
+ &case.functions,
+ &case.class_definitions,
+ );
let mut platform_repository =
- PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None)
- .unwrap();
+ PlatformRepository::new(vec![], IndexMap::new(), Some(platform_info), None).unwrap();
let libraries: Vec<String> = platform_repository
.search("lib".to_string(), SEARCH_NAME, None)
@@ -1794,33 +1731,22 @@ fn test_composer_platform_version() {
("PHP_DEBUG".to_string(), PhpMixed::Bool(false)),
]);
- let mut runtime = MockRuntime::new();
- runtime
- .expect_get_extensions()
- .times(..)
- .returning(Vec::new);
- runtime
- .expect_get_constant()
- .times(..)
- .returning(move |constant, class| {
- constants
- .get(&constant_key(constant, class.as_deref()))
- .cloned()
- .unwrap_or(PhpMixed::Null)
- });
// PHP only stubs getExtensions/getConstant; PHPUnit auto-returns null/false for the
- // other probed methods. Mirror that so initialize() does not hit unset expectations.
- runtime
- .expect_has_constant()
- .times(..)
- .returning(|_, _| false);
- runtime
- .expect_invoke()
- .times(..)
- .returning(|_, _| PhpMixed::Null);
+ // other methods, which is what the default payload reports.
+ let platform_info = platform_info(
+ constants
+ .into_iter()
+ .map(|(constant_name, value)| (constant_name, None, value))
+ .collect(),
+ Vec::new(),
+ "",
+ None,
+ &[],
+ &[],
+ );
let mut platform_repository =
- PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None).unwrap();
+ PlatformRepository::new(vec![], IndexMap::new(), Some(platform_info), None).unwrap();
let package = platform_repository
.find_package(