From f9a843e11c79c8320f61d2529b210d1e3d4ff912 Mon Sep 17 00:00:00 2001 From: yavon007 Date: Tue, 22 Sep 2026 15:23:55 +0800 Subject: [PATCH] fix(compiler): preserve NUL bytes in string interpolation (#126) --skip-tests * fix(compiler): preserve NUL bytes in string interpolation * fix(compiler): pool interpolated string literals --- phpunit/code/interpolated-null-string.php | 12 ++++ phpunit/src/InterpolatedStringCodegenTest.php | 60 +++++++++++++++++++ src/CompilerBase.php | 2 +- .../basic/interpolated-null-string.phpt | 27 +++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 phpunit/code/interpolated-null-string.php create mode 100644 phpunit/src/InterpolatedStringCodegenTest.php create mode 100644 tests/compiler/basic/interpolated-null-string.phpt diff --git a/phpunit/code/interpolated-null-string.php b/phpunit/code/interpolated-null-string.php new file mode 100644 index 00000000..5324b49d --- /dev/null +++ b/phpunit/code/interpolated-null-string.php @@ -0,0 +1,12 @@ +setBuildMode(CompilerBase::BUILD_MODE_EXT); + $compiler->setTargetName('interpolated_null_string'); + $source = TYPEPHP_ROOT_PATH . '/phpunit/code/interpolated-null-string.php'; + $compiler->addFiles([$source]); + $compiler->prepareFile($source); + $code = file_get_contents($compiler->convertFile($source)); + + self::assertIsString($code); + self::assertStringContainsString('php::concat({get_str(', $code); + + $literalStrings = (new ReflectionProperty($compiler, 'literalStrings'))->getValue($compiler); + self::assertArrayHasKey("before\0", $literalStrings); + self::assertArrayHasKey("after\0", $literalStrings); + + $extension = file_get_contents($compiler->genExtension()); + self::assertIsString($extension); + self::assertStringContainsString('ZEND_STRL("before\000")', $extension); + self::assertStringContainsString('ZEND_STRL("after\000")', $extension); + } + + public function testNoLiteralStringsFallsBackToBinarySafeInlineStrings(): void + { + global $translator; + $compiler = CompilerTest::create(TYPEPHP_ROOT_PATH); + $translator = $compiler; + (new ReflectionProperty($compiler, 'noLiteralStrings'))->setValue($compiler, true); + $source = TYPEPHP_ROOT_PATH . '/phpunit/code/interpolated-null-string.php'; + $compiler->addFiles([$source]); + $compiler->prepareFile($source); + $code = file_get_contents($compiler->convertFile($source)); + + self::assertIsString($code); + self::assertStringNotContainsString('get_str(', $code); + self::assertStringContainsString('php::Str{ZEND_STRL("before\000")}', $code); + self::assertStringContainsString('php::Str{ZEND_STRL("after\000")}', $code); + self::assertSame([], (new ReflectionProperty($compiler, 'literalStrings'))->getValue($compiler)); + } +} diff --git a/src/CompilerBase.php b/src/CompilerBase.php index e9b07da5..a9f95fd1 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -4590,7 +4590,7 @@ class CompilerBase implements PropertyAccessContext protected function parseInterpolatedStringPart(Node\InterpolatedStringPart $expr): string { - return '"' . $this->escapeString($expr->value) . '"'; + return $this->getLiteralString($expr->value); } protected function parseGlobal(Node\Stmt\Global_ $expr): string diff --git a/tests/compiler/basic/interpolated-null-string.phpt b/tests/compiler/basic/interpolated-null-string.phpt new file mode 100644 index 00000000..121febcf --- /dev/null +++ b/tests/compiler/basic/interpolated-null-string.phpt @@ -0,0 +1,27 @@ +--TEST-- +String interpolation preserves embedded NUL bytes in literal parts +--FILE-- + +--EXPECT-- +41004258430044 +005800 +580037 +4158005942 +41004258430044