Skip to content

gen_stub: Fix handling of escape sequences in generated C strings#22273

Draft
NattyNarwhal wants to merge 3 commits into
php:masterfrom
NattyNarwhal:gen-stub-string-escape
Draft

gen_stub: Fix handling of escape sequences in generated C strings#22273
NattyNarwhal wants to merge 3 commits into
php:masterfrom
NattyNarwhal:gen-stub-string-escape

Conversation

@NattyNarwhal

Copy link
Copy Markdown
Member

When handling sequences like this in a stub:

<?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:

        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.

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 phpGH-22169.
@NattyNarwhal NattyNarwhal force-pushed the gen-stub-string-escape branch from 5cb8e03 to b1e2fe2 Compare June 10, 2026 20:51
@TimWolla

Copy link
Copy Markdown
Member

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.

Add it to the ext/zend_test stub.

C doesn't have this, and will throw the following warning/error:

```
error: unknown escape sequence '\$' [-Werror,-Wunknown-escape-sequence]
```

Since adding this to zend_test for CI's sake, this will bomb out. Strip
this specific sequence out to make cc happy.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gen_stub.php generated wrong C string

2 participants