Skip to content

Commit b1e2fe2

Browse files
committed
gen_stub: Fix handling of escape sequences in generated C strings
When handling sequences like this in a stub: ```php <?php class Whatever { public static string $foobar1 = "CCC \n\r\t\v\e\f\\\$\"\101\x41\u{41} CCC"; public static string $foobar2 = 'CCC \n\r\t\v\e\f\\\$\"\101\x41\u{41} CCC'; } ``` ...properly generate C headers that properly escape the string. Otherwise, the differing escaping rules and differences between PHP's single and double quoted strings could lead to mangled headers. The output of these strings after the stub has been generated: ``` string(20) "CCC $"AAA CCC" string(41) "CCC \n\r\t\v\e\f\\\\$\"\101\x41\u{41} CCC" ``` And the generated arginfo: ```c zval property_foobar1_default_value; zend_string *property_foobar1_default_value_str = zend_string_init("CCC \n\r\t\v\x1b\f\\\$\"AAA CCC", strlen("CCC \n\r\t\v\x1b\f\\\$\"AAA CCC"), 1); ZVAL_STR(&property_foobar1_default_value, property_foobar1_default_value_str); zend_string *property_foobar1_name = zend_string_init("foobar1", sizeof("foobar1") - 1, true); zend_declare_typed_property(class_entry, property_foobar1_name, &property_foobar1_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_foobar1_name, true); zval property_foobar2_default_value; zend_string *property_foobar2_default_value_str = zend_string_init("CCC \\n\\r\\t\\v\\e\\f\\\\\\\\$\\\"\\101\\x41\\u{41} CCC", strlen("CCC \\n\\r\\t\\v\\e\\f\\\\\\\\$\\\"\\101\\x41\\u{41} CCC"), 1); ZVAL_STR(&property_foobar2_default_value, property_foobar2_default_value_str); zend_string *property_foobar2_name = zend_string_init("foobar2", sizeof("foobar2") - 1, true); zend_declare_typed_property(class_entry, property_foobar2_name, &property_foobar2_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_foobar2_name, true); ``` Note that the PHP escape sequence "\$" will generate a warning with clang at least, as C doesn't have an equivalent: ``` /Users/calvin/src/php-src/ext/zend_test/test_arginfo.h:799:90: warning: unknown escape sequence '\$' [-Wunknown-escape-sequence] [...] ``` I'm also unsure how best to make tests for gen_stub, so I haven't included any beyond mentioning how to test manually with this commit. Fixes GH-22169.
1 parent 5170c01 commit b1e2fe2

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

build/gen_stub.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,9 +2344,21 @@ public function getCExpr(): ?string
23442344
// $this->expr has all its PHP constants replaced by C constants
23452345
$prettyPrinter = new Standard;
23462346
$expr = $prettyPrinter->prettyPrintExpr($this->expr);
2347-
// PHP single-quote to C double-quote string
23482347
if ($this->type->isString()) {
2349-
$expr = preg_replace("/(^'|'$)/", '"', $expr);
2348+
// The string in $expr has had the octal, hex, and unicode
2349+
// backslash sequences already applied for double-quoted strings,
2350+
// but not the other sequences.
2351+
// PHP has single quote strings, C doesn't (they're one char).
2352+
// Single-quoted strings need handling to replace their escapes
2353+
// with the double-quoted equivalent; namely single quote escapes.
2354+
// Double-quoted strings have similar escape sequences as C does,
2355+
// so we can pass them through directly.
2356+
if (preg_match("/(^'|'$)/", $expr)) {
2357+
$expr = substr($expr, 1, -1); // strip quotes, readd later
2358+
$expr = str_replace("\\'", "'", $expr);
2359+
$expr = addcslashes($expr, "\\\"");
2360+
$expr = "\"$expr\"";
2361+
}
23502362
}
23512363
return $expr[0] == '"' ? $expr : preg_replace('(\bnull\b)', 'NULL', str_replace('\\', '', $expr));
23522364
}

0 commit comments

Comments
 (0)