aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-21 19:03:39 +0900
committernsfisis <nsfisis@gmail.com>2026-06-21 19:03:39 +0900
commit9a60b4d6e2ca0c87bf767d31299caae0b91c2706 (patch)
treece8bbe3413ae527bb9dfda41caea19eac096444c /crates/shirabe-php-shim
parenta3171eefd8f8e520329bdd8f613e83ba6f0a7c13 (diff)
downloadphp-shirabe-9a60b4d6e2ca0c87bf767d31299caae0b91c2706.tar.gz
php-shirabe-9a60b4d6e2ca0c87bf767d31299caae0b91c2706.tar.zst
php-shirabe-9a60b4d6e2ca0c87bf767d31299caae0b91c2706.zip
feat(php-shim): implement http_build_query via serde_urlencoded
Diffstat (limited to 'crates/shirabe-php-shim')
-rw-r--r--crates/shirabe-php-shim/Cargo.toml1
-rw-r--r--crates/shirabe-php-shim/src/url.rs55
2 files changed, 52 insertions, 4 deletions
diff --git a/crates/shirabe-php-shim/Cargo.toml b/crates/shirabe-php-shim/Cargo.toml
index 797191e..bd285f5 100644
--- a/crates/shirabe-php-shim/Cargo.toml
+++ b/crates/shirabe-php-shim/Cargo.toml
@@ -12,6 +12,7 @@ regex.workspace = true
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
+serde_urlencoded.workspace = true
zip.workspace = true
[lints]
diff --git a/crates/shirabe-php-shim/src/url.rs b/crates/shirabe-php-shim/src/url.rs
index ddb46b6..92ed389 100644
--- a/crates/shirabe-php-shim/src/url.rs
+++ b/crates/shirabe-php-shim/src/url.rs
@@ -82,10 +82,57 @@ pub fn http_build_query_mixed(
numeric_prefix: &str,
arg_separator: &str,
) -> String {
- let _ = (data, numeric_prefix, arg_separator);
- todo!()
+ let mut pairs: Vec<(String, String)> = Vec::new();
+ for (key, value) in data {
+ // numeric_prefix is prepended only to integer keys at the top level.
+ let key = if key.parse::<i64>().is_ok() {
+ format!("{numeric_prefix}{key}")
+ } else {
+ key.clone()
+ };
+ append_query_pairs(&mut pairs, key, value);
+ }
+ encode_pairs(&pairs, arg_separator)
+}
+
+pub fn http_build_query(
+ data: &[(&str, &str)],
+ numeric_prefix: &str,
+ arg_separator: &str,
+) -> String {
+ // numeric_prefix only applies to integer keys, which a string-keyed slice never has.
+ let _ = numeric_prefix;
+ encode_pairs(data, arg_separator)
}
-pub fn http_build_query(_data: &[(&str, &str)], _sep_str: &str, _sep: &str) -> String {
- todo!()
+fn encode_pairs<T: serde::Serialize>(pairs: T, arg_separator: &str) -> String {
+ let encoded = serde_urlencoded::to_string(pairs).unwrap();
+ if arg_separator == "&" {
+ encoded
+ } else {
+ // serde_urlencoded percent-encodes any literal '&' in keys/values, so the
+ // only remaining '&' are the separators we are replacing.
+ encoded.replace('&', arg_separator)
+ }
+}
+
+fn append_query_pairs(pairs: &mut Vec<(String, String)>, key: String, value: &PhpMixed) {
+ match value {
+ // Null values are omitted from the query string entirely.
+ PhpMixed::Null => {}
+ PhpMixed::Bool(b) => pairs.push((key, if *b { "1" } else { "0" }.to_string())),
+ PhpMixed::Int(i) => pairs.push((key, i.to_string())),
+ PhpMixed::Float(f) => pairs.push((key, f.to_string())),
+ PhpMixed::String(s) => pairs.push((key, s.clone())),
+ PhpMixed::List(items) => {
+ for (i, item) in items.iter().enumerate() {
+ append_query_pairs(pairs, format!("{key}[{i}]"), item);
+ }
+ }
+ PhpMixed::Array(map) | PhpMixed::Object(map) => {
+ for (k, v) in map {
+ append_query_pairs(pairs, format!("{key}[{k}]"), v);
+ }
+ }
+ }
}