diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3d843be0..41f461e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -237,7 +237,7 @@ jobs: - name: Run compiler PHPT suite with bootstrap compiler run: | mkdir -p build - php run-tests.php -q -j8 --compiler ./tpc \ + php run-tests.php -q -j4 --compiler ./tpc \ -w build/failed-tests.txt -W build/test-results.txt tests/compiler - name: Upload PHPT failure artifacts diff --git a/phpunit/src/GenStubVersionFlagsTest.php b/phpunit/src/GenStubVersionFlagsTest.php index ed7d46ec..658fbeb5 100644 --- a/phpunit/src/GenStubVersionFlagsTest.php +++ b/phpunit/src/GenStubVersionFlagsTest.php @@ -2,6 +2,27 @@ final class GenStubVersionFlagsTest extends BaseTest { + public function testPersistentStringMetadataUsesANonRefcountedInternedZval(): void + { + global $translator; + $translator = \TypePhp\CompilerTest::create(ROOT_PATH); + + $value = EvaluatedValue::createFromExpression( + new PhpParser\Node\Scalar\String_('PHP'), + null, + null, + [], + ); + + $code = $value->initializeZval('property_lang_default_value'); + + self::assertStringContainsString('zend_string_init_interned("PHP"', $code); + self::assertStringContainsString( + 'ZVAL_INTERNED_STR(&property_lang_default_value, property_lang_default_value_str);', + $code, + ); + } + public function testFlagIntroducedBeforeMinimumSupportedVersionRemainsEnabled(): void { $flags = new VersionFlags(['ZEND_ACC_PRIVATE']); diff --git a/src/gen_stub.php b/src/gen_stub.php index 00b2c04b..c39335b6 100755 --- a/src/gen_stub.php +++ b/src/gen_stub.php @@ -2753,8 +2753,12 @@ class EvaluatedValue } // getCExpr() emits a C string literal here. sizeof() preserves // embedded NUL bytes, unlike strlen(). - $code .= "\tzend_string *$forStringDef = zend_string_init($cExpr, sizeof($cExpr) - 1, 1);\n"; - $code .= "\tZVAL_STR(&$zvalName, $forStringDef);\n"; + // Internal class metadata is persistent. PHP 8.5 rejects + // refcounted zvals in persistent property defaults, so a + // persistent-but-refcounted zend_string is not sufficient. + // Interning also makes the value safe to share under ZTS. + $code .= "\tzend_string *$forStringDef = zend_string_init_interned($cExpr, sizeof($cExpr) - 1, 1);\n"; + $code .= "\tZVAL_INTERNED_STR(&$zvalName, $forStringDef);\n"; } } elseif ($this->type->isArray()) { $code .= "\tZVAL_EMPTY_ARRAY(&$zvalName);\n";