From d3e8298654fccaf2d3343e5f3214e701f0965105 Mon Sep 17 00:00:00 2001 From: PurHur Date: Wed, 20 May 2026 16:41:34 +0000 Subject: [PATCH] Fix VM string interpolation and add encapsed compliance tests (closes #261). ConcatList lowering was already present; close gaps for null/undefined toString, class property defaults in encapsed braces, and PHPT coverage for VM and JIT. Co-authored-by: Cursor --- lib/VM.php | 4 +-- lib/VM/Variable.php | 21 +++++++++------ .../cases/language/encapsed_string.phpt | 27 +++++++++++++++++++ .../cases/language/encapsed_string_jit.phpt | 16 +++++++++++ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 test/compliance/cases/language/encapsed_string.phpt create mode 100644 test/compliance/cases/language/encapsed_string_jit.phpt diff --git a/lib/VM.php b/lib/VM.php index 5327e988..a5603f3b 100755 --- a/lib/VM.php +++ b/lib/VM.php @@ -385,10 +385,10 @@ protected function defineClass(ClassEntry $entry, Block $block): void { switch ($op->type) { case OpCode::TYPE_DECLARE_PROPERTY: $name = $frame->scope[$op->arg1]; - assert(is_null($op->arg2)); // no defaults for now + $default = is_null($op->arg2) ? null : $frame->scope[$op->arg2]; $entry->properties[] = new VM\ClassProperty( $name->toString(), - null, + $default, $frame->scope[$op->arg3] ); break; diff --git a/lib/VM/Variable.php b/lib/VM/Variable.php index ab796f06..0165aa5c 100755 --- a/lib/VM/Variable.php +++ b/lib/VM/Variable.php @@ -224,23 +224,28 @@ public function string(string $value): void { } public function toString(): string { - switch ($this->type) { + $var = $this->resolveIndirect(); + switch ($var->type) { case self::TYPE_STRING: - return $this->string; + return $var->string; case self::TYPE_INTEGER: - return (string) $this->integer; + return (string) $var->integer; case self::TYPE_FLOAT: - return (string) $this->float; + return (string) $var->float; case self::TYPE_BOOLEAN: - return $this->bool ? '1' : ''; - case self::TYPE_INDIRECT: - return $this->indirect->toString(); + return $var->bool ? '1' : ''; case self::TYPE_STRING_OFFSET: - return $this->readStringOffset(); + return $var->readStringOffset(); + case self::TYPE_NULL: + case self::TYPE_UNDEFINED: + return ''; case self::TYPE_ARRAY: // todo: raise notice return 'Array'; + case self::TYPE_OBJECT: + return 'Object'; } + throw new \LogicException("Cannot convert type {$var->type} to string"); } public function object(ObjectEntry $value): void { diff --git a/test/compliance/cases/language/encapsed_string.phpt b/test/compliance/cases/language/encapsed_string.phpt new file mode 100644 index 00000000..0a685c93 --- /dev/null +++ b/test/compliance/cases/language/encapsed_string.phpt @@ -0,0 +1,27 @@ +--TEST-- +Language: double-quoted string interpolation (issue #261) +--FILE-- + 42]; +echo "id={$user['id']}\n"; + +$empty = null; +echo "null=|$empty|\n"; + +class C { + public $x = 9; +} +$o = new C; +echo "prop={$o->x}\n"; +--EXPECT-- +Hello, Dev +Route: /api +id=42 +null=|| +prop=9 diff --git a/test/compliance/cases/language/encapsed_string_jit.phpt b/test/compliance/cases/language/encapsed_string_jit.phpt new file mode 100644 index 00000000..00e406ce --- /dev/null +++ b/test/compliance/cases/language/encapsed_string_jit.phpt @@ -0,0 +1,16 @@ +--TEST-- +Language: double-quoted string interpolation JIT (issue #261) +--FILE-- + 42]; +echo "id={$user['id']}\n"; +--EXPECT-- +Hello, Dev +Route: /api +id=42