aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-04 06:33:15 +0900
committernsfisis <nsfisis@gmail.com>2026-08-04 06:33:15 +0900
commit901cbbf285ed4e9c1f7bf75b413643c117a6105a (patch)
treeb455299a58b64a3fcc5bafa40a7a640936891e93 /crates/shirabe-php-shim/src
parentb45e04865df335a9aedbf2bdd5b17353c1598fa5 (diff)
downloadphp-shirabe-901cbbf285ed4e9c1f7bf75b413643c117a6105a.tar.gz
php-shirabe-901cbbf285ed4e9c1f7bf75b413643c117a6105a.tar.zst
php-shirabe-901cbbf285ed4e9c1f7bf75b413643c117a6105a.zip
feat(php-shim): render stdClass-shaped objects in var_export
PhpMixed::Object hit a todo!(); the capability-name validation message in PluginManager (var_export of an invalid getCapabilities value) reaches it with a stdClass. Match the PHP 8.5.8 output byte for byte — the "(object) array(...)" shape, properties indented one space deeper than array elements, keys always quoted strings — verified against the real PHP as the oracle and pinned by a unit test. Other classes would render as \Class::__set_state(...), which stays out of reach because PhpMixed::Object carries no class name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim/src')
-rw-r--r--crates/shirabe-php-shim/src/var.rs78
1 files changed, 73 insertions, 5 deletions
diff --git a/crates/shirabe-php-shim/src/var.rs b/crates/shirabe-php-shim/src/var.rs
index f611462a..257dd8bd 100644
--- a/crates/shirabe-php-shim/src/var.rs
+++ b/crates/shirabe-php-shim/src/var.rs
@@ -418,7 +418,10 @@ fn var_export_into(out: &mut String, value: &PhpMixed, level: usize) {
for (i, item) in items.iter().enumerate() {
var_export_indent(out, level + 1);
out.push_str(&format!("{} => ", i));
- if matches!(item, PhpMixed::List(_) | PhpMixed::Array(_)) {
+ if matches!(
+ item,
+ PhpMixed::List(_) | PhpMixed::Array(_) | PhpMixed::Object(_)
+ ) {
out.push('\n');
var_export_indent(out, level + 1);
}
@@ -437,7 +440,34 @@ fn var_export_into(out: &mut String, value: &PhpMixed, level: usize) {
None => out.push_str(&var_export_string(k)),
}
out.push_str(" => ");
- if matches!(v, PhpMixed::List(_) | PhpMixed::Array(_)) {
+ if matches!(
+ v,
+ PhpMixed::List(_) | PhpMixed::Array(_) | PhpMixed::Object(_)
+ ) {
+ out.push('\n');
+ var_export_indent(out, level + 1);
+ }
+ var_export_into(out, v, level + 1);
+ out.push_str(",\n");
+ }
+ var_export_indent(out, level);
+ out.push(')');
+ }
+ // TODO(php-runtime): PhpMixed::Object carries no class name, so this renders the
+ // stdClass shape "(object) array(...)" (PHP 8.5 oracle); any other class would render
+ // as "\Class::__set_state(array(...))" and cannot be distinguished here.
+ PhpMixed::Object(entries) => {
+ out.push_str("(object) array(\n");
+ for (k, v) in entries {
+ var_export_indent(out, level + 1);
+ // Object property keys are always exported as quoted strings, never as ints.
+ out.push(' ');
+ out.push_str(&var_export_string(k));
+ out.push_str(" => ");
+ if matches!(
+ v,
+ PhpMixed::List(_) | PhpMixed::Array(_) | PhpMixed::Object(_)
+ ) {
out.push('\n');
var_export_indent(out, level + 1);
}
@@ -447,9 +477,6 @@ fn var_export_into(out: &mut String, value: &PhpMixed, level: usize) {
var_export_indent(out, level);
out.push(')');
}
- // TODO(php-runtime): PHP renders objects as "\Class::__set_state(array(...))"; PhpMixed::Object
- // carries no class name.
- PhpMixed::Object(_) => todo!(),
}
}
@@ -498,3 +525,44 @@ fn var_export_float(f: f64) -> String {
format!("{}.0", s)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ /// PHP 8.5.8 oracle: `var_export($v, true)` over stdClass-shaped objects, standalone and
+ /// nested in arrays (object properties indent one space deeper than array elements, and
+ /// keys stay quoted strings).
+ #[test]
+ fn test_var_export_object() {
+ assert_eq!(
+ "(object) array(\n)",
+ var_export(&PhpMixed::Object(IndexMap::new()), true)
+ );
+ let obj = PhpMixed::Object(IndexMap::from([
+ ("a".to_string(), PhpMixed::Int(1)),
+ (
+ "b".to_string(),
+ PhpMixed::List(vec![PhpMixed::Int(1), PhpMixed::Int(2)]),
+ ),
+ ]));
+ assert_eq!(
+ "(object) array(\n 'a' => 1,\n 'b' => \n array (\n 0 => 1,\n 1 => 2,\n ),\n)",
+ var_export(&obj, true)
+ );
+ let arr = PhpMixed::Array(IndexMap::from([(
+ "x".to_string(),
+ PhpMixed::Object(IndexMap::from([
+ ("a".to_string(), PhpMixed::Int(1)),
+ (
+ "o".to_string(),
+ PhpMixed::Object(IndexMap::from([("b".to_string(), PhpMixed::Int(2))])),
+ ),
+ ])),
+ )]));
+ assert_eq!(
+ "array (\n 'x' => \n (object) array(\n 'a' => 1,\n 'o' => \n (object) array(\n 'b' => 2,\n ),\n ),\n)",
+ var_export(&arr, true)
+ );
+ }
+}