Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ext/standard/BuiltinIntrospectionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public static function functionIsAdvertised(string $functionName): bool
if ('mb_str_pad' === $lc) {
return CompilerVersion::advertisesMbStrPad();
}
if (\in_array($lc, ['str_increment', 'str_decrement'], true)) {
return CompilerVersion::advertisesStrIncrement();
}
if (str_starts_with($lc, 'bc')) {
return CompilerVersion::advertisesBcmath();
}
Expand Down
40 changes: 37 additions & 3 deletions lib/CompilerVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,47 @@ public static function languageProfileVersion(): string
}

/**
* PHP 8.3+ str_increment() / str_decrement() (ext/standard/string.c, issue #5697, #12378, #14518, #14709, #15026).
* PHP 8.3+ str_increment() / str_decrement() (ext/standard/string.c, issue #5697, #12378, #14518, #14709, #15026, #16292).
*
* Forward profile on 8.4.0-dev — advertisesBuiltinSince treats -dev as 8.4.0 (#13284 phantom withheld on 8.2).
* Withheld on 8.4.0-dev reference profile (matches Zend 8.2 function_exists gate). Enable via
* stable 8.4.0+ or explicit `PHP_COMPILER_PROFILE=8.3` / `8.4` forward profile.
*/
public static function supportsStrIncrement(): bool
{
return self::advertisesBuiltinSince('8.3.0');
if (version_compare(self::VERSION, '8.3', '<')) {
return false;
}

if (version_compare(self::VERSION, '8.4.0', '>=')) {
return true;
}

$raw = getenv('PHP_COMPILER_PROFILE');
if (!\is_string($raw) || '' === trim($raw)) {
return false;
}

return version_compare(self::languageProfileVersion(), '8.3.0', '>=');
}

/**
* str_increment()/str_decrement() visible to function_exists() — stable runtime or forward 8.3+ (#16292).
*
* Callable under forward profile via {@see supportsStrIncrement()}; withheld from introspection on 8.4.0-dev
* reference harness like Zend 8.2.
*/
public static function advertisesStrIncrement(): bool
{
if (version_compare(self::VERSION, '8.4.0', '>=')) {
return true;
}

$raw = getenv('PHP_COMPILER_PROFILE');
if (!\is_string($raw) || '' === trim($raw)) {
return false;
}

return version_compare(self::languageProfileVersion(), '8.3.0', '>=');
}

/**
Expand Down
4 changes: 3 additions & 1 deletion test/compliance/cases/stdlib/forward_profile_builtins.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
--TEST--
stdlib forward profile builtins str_increment registered (#14518); json_validate stable 8.4+ (#15196)
--ENV--
PHP_COMPILER_PROFILE=8.3
--FILE--
<?php
echo function_exists('json_validate') ? 'jv=yes' : 'jv=no', "\n";
Expand All @@ -8,7 +10,7 @@ echo function_exists('str_decrement') ? 'sd=yes' : 'sd=no', "\n";
echo str_increment('a'), "\n";
?>
--EXPECT--
jv=no
jv=yes
si=yes
sd=yes
b
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

$fail = [];
if (function_exists('str_increment')) {
$fail[] = 'str_increment';
}
if (function_exists('str_decrement')) {
$fail[] = 'str_decrement';
}
$internal = get_defined_functions()['internal'] ?? [];
if (\in_array('str_increment', $internal, true)) {
$fail[] = 'str_increment_internal';
}
if (\in_array('str_decrement', $internal, true)) {
$fail[] = 'str_decrement_internal';
}

echo [] === $fail ? "ok\n" : 'fail: '.implode(',', $fail)."\n";
19 changes: 18 additions & 1 deletion test/unit/CompilerVersionBuiltinAdvertisementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,26 @@ public function testMbStrPadWithheldOnReferenceProfileUntilStable84(): void
$this->assertFalse(CompilerVersion::supportsMbStrPad());
}

public function testStrIncrementWithheldOnReferenceProfile(): void
{
$this->assertFalse(CompilerVersion::supportsStrIncrement());
$this->assertFalse(CompilerVersion::advertisesStrIncrement());
}

public function testStrIncrementAdvertisedOnForwardProfile(): void
{
$this->assertTrue(CompilerVersion::supportsStrIncrement());
$prev = getenv('PHP_COMPILER_PROFILE');
putenv('PHP_COMPILER_PROFILE=8.3');
try {
$this->assertTrue(CompilerVersion::supportsStrIncrement());
$this->assertTrue(CompilerVersion::advertisesStrIncrement());
} finally {
if (false === $prev) {
putenv('PHP_COMPILER_PROFILE');
} else {
putenv('PHP_COMPILER_PROFILE='.$prev);
}
}
}

public function testClassHasFunctionsAdvertisedOnForwardProfile(): void
Expand Down
61 changes: 57 additions & 4 deletions test/unit/CompilerVersionGateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,44 @@ public function testVersionReports84Dev(): void
$this->assertSame('8.4.0-dev', CompilerVersion::VERSION);
}

public function testSupportsStrIncrementFalseOnReferenceProfile(): void
{
$this->assertFalse(CompilerVersion::supportsStrIncrement());
}

public function testSupportsStrIncrementTrueOnForwardProfile(): void
{
$this->assertTrue(CompilerVersion::supportsStrIncrement());
$prev = getenv('PHP_COMPILER_PROFILE');
putenv('PHP_COMPILER_PROFILE=8.3');
try {
$this->assertTrue(CompilerVersion::supportsStrIncrement());
} finally {
if (false === $prev) {
putenv('PHP_COMPILER_PROFILE');
} else {
putenv('PHP_COMPILER_PROFILE='.$prev);
}
}
}

public function testAdvertisesStrIncrementFalseOnReferenceProfile(): void
{
$this->assertFalse(CompilerVersion::advertisesStrIncrement());
}

public function testAdvertisesStrIncrementTrueOnForwardProfile(): void
{
$prev = getenv('PHP_COMPILER_PROFILE');
putenv('PHP_COMPILER_PROFILE=8.3');
try {
$this->assertTrue(CompilerVersion::advertisesStrIncrement());
} finally {
if (false === $prev) {
putenv('PHP_COMPILER_PROFILE');
} else {
putenv('PHP_COMPILER_PROFILE='.$prev);
}
}
}

public function testSupportsClassHasFunctionsTrueOnForwardProfile(): void
Expand Down Expand Up @@ -574,12 +609,30 @@ public function testSupportsFinalGlobalTypedConstantsAlwaysFalse(): void
$this->assertFalse(CompilerVersion::supportsFinalGlobalTypedConstants());
}

public function testVmRegistersStrIncrementOnForwardProfile(): void
public function testVmDoesNotRegisterStrIncrementOnReferenceProfile(): void
{
$runtime = new Runtime();
$ctx = $runtime->vmContext;
$this->assertTrue(isset($ctx->functions['str_decrement']));
$this->assertTrue(isset($ctx->functions['str_increment']));
$this->assertFalse(isset($ctx->functions['str_decrement']));
$this->assertFalse(isset($ctx->functions['str_increment']));
}

public function testVmRegistersStrIncrementOnForwardProfile(): void
{
$prev = getenv('PHP_COMPILER_PROFILE');
putenv('PHP_COMPILER_PROFILE=8.3');
try {
$runtime = new Runtime();
$ctx = $runtime->vmContext;
$this->assertTrue(isset($ctx->functions['str_decrement']));
$this->assertTrue(isset($ctx->functions['str_increment']));
} finally {
if (false === $prev) {
putenv('PHP_COMPILER_PROFILE');
} else {
putenv('PHP_COMPILER_PROFILE='.$prev);
}
}
}

public function testVmRegistersClassHasFunctionsOnForwardProfile(): void
Expand Down
24 changes: 24 additions & 0 deletions test/unit/ForwardProfilePhantomIntrospectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,28 @@ public function testBcmathCallableButExtensionNotAdvertisedOnForwardProfile(): v
}
}
}

public function testStrIncrementCallableAndAdvertisedOnForwardProfile83(): void
{
$prev = getenv('PHP_COMPILER_PROFILE');
putenv('PHP_COMPILER_PROFILE=8.3');
try {
$this->assertTrue(CompilerVersion::supportsStrIncrement());
$this->assertTrue(CompilerVersion::advertisesStrIncrement());
$this->assertTrue(BuiltinIntrospectionPolicy::functionIsAdvertised('str_increment'));

$runtime = new Runtime();
$ctx = $runtime->vmContext;
$this->assertTrue(isset($ctx->functions['str_increment']));
$this->assertTrue(
\PHPCompiler\ext\standard\VmReflection::functionExists($ctx, 'str_increment')
);
} finally {
if (false === $prev) {
putenv('PHP_COMPILER_PROFILE');
} else {
putenv('PHP_COMPILER_PROFILE='.$prev);
}
}
}
}