Skip to content

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

Draft
NattyNarwhal wants to merge 4 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 4 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.
Comment thread ext/zend_test/test.stub.php

@kocsismate kocsismate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but LGTM otherwise, thank you!

public static int $staticIntProp = 123;

/* If there's a problem with escapes in quotes in generated headers,
* the generated header won't compile. (tests/gh22169.phpt) */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: tab indenting should be switched to spaces

namespace {
require "Zend/zend_attributes.stub.php";


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this an unintended change?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, vim “smart” indentation is on by default and mangles these files. I’ll fix these when I’m back at a computer

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

4 participants