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
116 changes: 61 additions & 55 deletions docs/bootstrap-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Regenerate: `php script/bootstrap-inventory.php`

| Metric | Count |
|--------|------:|
| PHP files on vm.php path | 6518 |
| Phase A inventory files (M2 ratio SSOT) | 6518 |
| PHP files on vm.php path | 6519 |
| Phase A inventory files (M2 ratio SSOT) | 6519 |
| Phase A ratio-deferred paths | 0 |
| Source constructs flagged (blockers) | 1 |
| Source constructs flagged (warnings) | 20930 |
| Source constructs flagged (warnings) | 20931 |

## Compiler CFG gaps (`lib/Compiler.php`)

Expand Down Expand Up @@ -738,6 +738,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
| `ext/gettext/ngettext.php` | 0 | 1 |
| `ext/gettext/textdomain.php` | 0 | 2 |
| `ext/gmp/GmpConstants.php` | 0 | 1 |
| `ext/gmp/GmpExtensionPolicy.php` | 0 | 1 |
| `ext/gmp/GmpFunction.php` | 0 | 1 |
| `ext/gmp/GmpToString.php` | 0 | 1 |
| `ext/gmp/Module.php` | 0 | 52 |
Expand Down Expand Up @@ -11691,6 +11692,11 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
**Warnings** (review for bootstrap subset):
- 1 class method(s)

### `ext/gmp/GmpExtensionPolicy.php`

**Warnings** (review for bootstrap subset):
- 4 class method(s)

### `ext/gmp/GmpFunction.php`

**Warnings** (review for bootstrap subset):
Expand All @@ -11704,57 +11710,57 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `ext/gmp/Module.php`

**Warnings** (review for bootstrap subset):
- new VM\Variable (line 20)
- new gmp_init (line 34)
- new gmp_add (line 35)
- new gmp_sub (line 36)
- new gmp_mul (line 37)
- new gmp_cmp (line 38)
- new gmp_strval (line 39)
- new gmp_pow (line 40)
- new gmp_mod (line 41)
- new gmp_div_q (line 42)
- new gmp_div_r (line 43)
- new gmp_div_qr (line 44)
- new gmp_abs (line 45)
- new gmp_neg (line 46)
- new gmp_and (line 47)
- new gmp_or (line 48)
- new gmp_xor (line 49)
- new gmp_intval (line 50)
- new gmp_powm (line 51)
- new gmp_fact (line 52)
- new gmp_binomial (line 53)
- new gmp_gcd (line 54)
- new gmp_lcm (line 55)
- new gmp_sqrt (line 56)
- new gmp_sqrtrem (line 57)
- new gmp_perfect_square (line 58)
- new gmp_com (line 59)
- new gmp_random_seed (line 60)
- new gmp_random_bits (line 61)
- new gmp_random_range (line 62)
- new gmp_import (line 63)
- new gmp_export (line 64)
- new gmp_sign (line 65)
- new gmp_prob_prime (line 66)
- new gmp_nextprime (line 67)
- new gmp_invert (line 68)
- new gmp_jacobi (line 69)
- new gmp_legendre (line 70)
- new gmp_kronecker (line 71)
- new gmp_divexact (line 72)
- new gmp_gcdext (line 73)
- new gmp_root (line 74)
- new gmp_rootrem (line 75)
- new gmp_perfect_power (line 76)
- new gmp_testbit (line 77)
- new gmp_setbit (line 78)
- new gmp_clrbit (line 79)
- new gmp_scan0 (line 80)
- new gmp_scan1 (line 81)
- new gmp_popcount (line 82)
- new gmp_hamdist (line 83)
- new VM\Variable (line 23)
- new gmp_init (line 44)
- new gmp_add (line 45)
- new gmp_sub (line 46)
- new gmp_mul (line 47)
- new gmp_cmp (line 48)
- new gmp_strval (line 49)
- new gmp_pow (line 50)
- new gmp_mod (line 51)
- new gmp_div_q (line 52)
- new gmp_div_r (line 53)
- new gmp_div_qr (line 54)
- new gmp_abs (line 55)
- new gmp_neg (line 56)
- new gmp_and (line 57)
- new gmp_or (line 58)
- new gmp_xor (line 59)
- new gmp_intval (line 60)
- new gmp_powm (line 61)
- new gmp_fact (line 62)
- new gmp_binomial (line 63)
- new gmp_gcd (line 64)
- new gmp_lcm (line 65)
- new gmp_sqrt (line 66)
- new gmp_sqrtrem (line 67)
- new gmp_perfect_square (line 68)
- new gmp_com (line 69)
- new gmp_random_seed (line 70)
- new gmp_random_bits (line 71)
- new gmp_random_range (line 72)
- new gmp_import (line 73)
- new gmp_export (line 74)
- new gmp_sign (line 75)
- new gmp_prob_prime (line 76)
- new gmp_nextprime (line 77)
- new gmp_invert (line 78)
- new gmp_jacobi (line 79)
- new gmp_legendre (line 80)
- new gmp_kronecker (line 81)
- new gmp_divexact (line 82)
- new gmp_gcdext (line 83)
- new gmp_root (line 84)
- new gmp_rootrem (line 85)
- new gmp_perfect_power (line 86)
- new gmp_testbit (line 87)
- new gmp_setbit (line 88)
- new gmp_clrbit (line 89)
- new gmp_scan0 (line 90)
- new gmp_scan1 (line 91)
- new gmp_popcount (line 92)
- new gmp_hamdist (line 93)
- 3 class method(s)

### `ext/gmp/VmGmp.php`
Expand Down Expand Up @@ -41583,7 +41589,7 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag
### `lib/CompilerVersion.php`

**Warnings** (review for bootstrap subset):
- 250 class method(s)
- 251 class method(s)

### `lib/CurlyBraceOffsetRejector.php`

Expand Down
53 changes: 53 additions & 0 deletions ext/gmp/GmpExtensionPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\ext\gmp;

/**
* ext/gmp advertisement — php-src ext/gmp/gmp.c (#3341 / #22860).
*
* GMP APIs stay in-tree (PHP-in-PHP) but are withheld from extension_loaded() /
* function_exists('gmp_*') / class_exists('GMP') on the reference harness when host
* Zend has no php-gmp — same shape as soap/yaml (#22859 / #6275). Enable via host
* ext/gmp or `PHP_COMPILER_PROFILE=8.4` ({@see \PHPCompiler\CompilerVersion::supportsGmp()}).
*/
final class GmpExtensionPolicy
{
/**
* extension_loaded('gmp') / CREDITS_MODULES — match Zend without phantom gmp (#22860).
*/
public static function advertisesExtension(): bool
{
if (\extension_loaded('gmp')) {
return true;
}

return \PHPCompiler\CompilerVersion::supportsGmp();
}

/** Compliance filenames that exercise gmp_* / GMP. */
public static function isGmpComplianceCase(string $testFileName): bool
{
return str_contains($testFileName, 'gmp_')
|| str_contains($testFileName, '/gmp/')
|| str_contains($testFileName, 'extension_loaded_gmp');
}

/** Phantom-registration guards that assert gmp is withheld (#22860). */
public static function isGmpPhantomComplianceCase(string $testFileName): bool
{
return str_contains($testFileName, 'gmp_phantom')
|| str_contains($testFileName, 'extension_loaded_gmp_phantom');
}

/** Run functional gmp compliance when advertised, else phantom only (#22860). */
public static function runsGmpCompliance(string $testFileName): bool
{
if (self::advertisesExtension()) {
return !self::isGmpPhantomComplianceCase($testFileName);
}

return self::isGmpPhantomComplianceCase($testFileName);
}
}
12 changes: 11 additions & 1 deletion ext/gmp/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
use PHPCompiler\VM;
use PHPCompiler\VM\Context;

/** gmp extension module entry (php-src ext/gmp/gmp.c; issues #3341, #19527, #19539, #19540, #20519). */
/** gmp extension module entry (php-src ext/gmp/gmp.c; issues #3341, #19527, #19539, #19540, #20519, #22860). */
class Module extends ModuleAbstract
{
public function init(Runtime $runtime): void
{
parent::init($runtime);
if (!GmpExtensionPolicy::advertisesExtension()) {
return;
}
VmGmpObject::registerClass($runtime->vmContext);
foreach (GmpConstants::registeredConstants() as $name => $value) {
$var = new VM\Variable();
Expand All @@ -25,11 +28,18 @@ public function init(Runtime $runtime): void

public static function registerClasses(Context $ctx): void
{
if (!GmpExtensionPolicy::advertisesExtension()) {
return;
}
VmGmpObject::registerClass($ctx);
}

public function getFunctions(): array
{
if (!GmpExtensionPolicy::advertisesExtension()) {
return [];
}

return [
new gmp_init(),
new gmp_add(),
Expand Down
6 changes: 6 additions & 0 deletions ext/standard/BuiltinIntrospectionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ public static function functionIsAdvertised(string $functionName): bool
if (\in_array($lc, ['is_soap_fault', 'use_soap_error_handler'], true)) {
return \PHPCompiler\ext\soap\SoapExtensionPolicy::advertisesExtension();
}
if (str_starts_with($lc, 'gmp_')) {
return \PHPCompiler\ext\gmp\GmpExtensionPolicy::advertisesExtension();
}
if (\in_array($lc, ['class_has_method', 'class_has_property', 'class_has_constant'], true)) {
return CompilerVersion::supportsClassHasFunctions();
}
Expand Down Expand Up @@ -294,6 +297,9 @@ public static function extensionIsAdvertised(string $extension): bool
if ('soap' === $ext) {
return \PHPCompiler\ext\soap\SoapExtensionPolicy::advertisesExtension();
}
if ('gmp' === $ext) {
return \PHPCompiler\ext\gmp\GmpExtensionPolicy::advertisesExtension();
}

return true;
}
Expand Down
8 changes: 6 additions & 2 deletions ext/standard/ExtensionConstantGroups.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,12 @@ public static function groups(): array
// Module buckets for extensions that register into Context::$constants (#22337 / re-#19113).
$groups['ftp'] = FtpConstants::registeredConstants();
$groups['mysqli'] = MysqliConstants::registeredConstants();
$groups['gmp'] = GmpConstants::registeredConstants();
$groups['soap'] = SoapConstants::registeredConstants();
if (\PHPCompiler\ext\gmp\GmpExtensionPolicy::advertisesExtension()) {
$groups['gmp'] = GmpConstants::registeredConstants();
}
if (\PHPCompiler\ext\soap\SoapExtensionPolicy::advertisesExtension()) {
$groups['soap'] = SoapConstants::registeredConstants();
}
$groups['tidy'] = TidyConstants::registeredConstants();
$groups['pgsql'] = PgsqlConstants::registeredConstants();
$groups['pspell'] = PspellConstants::registeredConstants();
Expand Down
6 changes: 4 additions & 2 deletions ext/standard/ModuleRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,10 @@ public static function registerModule(Module $module): void
&& !\PHPCompiler\ext\ffi\FfiExtensionPolicy::advertisesExtension();
$withholdSoapSurface = 'soap' === $primary
&& !\PHPCompiler\ext\soap\SoapExtensionPolicy::advertisesExtension();
$withholdGmpSurface = 'gmp' === $primary
&& !\PHPCompiler\ext\gmp\GmpExtensionPolicy::advertisesExtension();

if (!$withholdOpensslSurface && !$withholdSqlite3Surface && !$withholdLdapSurface && !$withholdInotifySurface && !$withholdXslSurface && !$withholdXmlrpcSurface && !$withholdWddxSurface && !$withholdYamlSurface && !$withholdRedisSurface && !$withholdMongodbSurface && !$withholdSnmpSurface && !$withholdFfiSurface && !$withholdSoapSurface) {
if (!$withholdOpensslSurface && !$withholdSqlite3Surface && !$withholdLdapSurface && !$withholdInotifySurface && !$withholdXslSurface && !$withholdXmlrpcSurface && !$withholdWddxSurface && !$withholdYamlSurface && !$withholdRedisSurface && !$withholdMongodbSurface && !$withholdSnmpSurface && !$withholdFfiSurface && !$withholdSoapSurface && !$withholdGmpSurface) {
self::register($module->getExtensionName(), $moduleVersion);
} elseif ($registerSqlite3ExtensionLoaded) {
self::register($module->getExtensionName(), $moduleVersion);
Expand All @@ -183,7 +185,7 @@ public static function registerModule(Module $module): void
continue;
}
$fnName = strtolower($func->getName());
if ($withholdOpensslSurface || $withholdSqlite3Surface || $withholdLdapSurface || $withholdInotifySurface || $withholdXslSurface || $withholdXmlrpcSurface || $withholdWddxSurface || $withholdYamlSurface || $withholdRedisSurface || $withholdMongodbSurface || $withholdSnmpSurface || $withholdFfiSurface || $withholdSoapSurface) {
if ($withholdOpensslSurface || $withholdSqlite3Surface || $withholdLdapSurface || $withholdInotifySurface || $withholdXslSurface || $withholdXmlrpcSurface || $withholdWddxSurface || $withholdYamlSurface || $withholdRedisSurface || $withholdMongodbSurface || $withholdSnmpSurface || $withholdFfiSurface || $withholdSoapSurface || $withholdGmpSurface) {
self::registerBuiltinLookup($fnName);

continue;
Expand Down
12 changes: 12 additions & 0 deletions lib/CompilerVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -2791,6 +2791,18 @@ public static function supportsWddx(): bool
}


/**
* ext/gmp GMP object + gmp_* — withheld on reference profile (#22860 / #3341).
*
* Gated on stable 8.4.0 / {@see languageProfileVersion()} so 8.4.0-dev reference profile matches
* Zend 8.2 phantom gate (host php-gmp absent). Enable forward profile via `PHP_COMPILER_PROFILE=8.4`
* or install host ext/gmp ({@see \PHPCompiler\ext\gmp\GmpExtensionPolicy::advertisesExtension()}).
*/
public static function supportsGmp(): bool
{
return version_compare(self::languageProfileVersion(), '8.4.0', '>=');
}

/**
* ext/soap SoapClient/SoapServer/SoapFault — withheld on reference profile (#22859 / #3724).
*
Expand Down
5 changes: 5 additions & 0 deletions test/compliance/JITTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public static function providePHPTests(): \Generator
&& \PHPCompiler\ext\soap\SoapExtensionPolicy::isSoapComplianceCase($name)) {
continue;
}
// Host/profile without gmp: withhold functional gmp_* cases; keep phantom (#22860).
if (!\PHPCompiler\ext\gmp\GmpExtensionPolicy::runsGmpCompliance($name)
&& \PHPCompiler\ext\gmp\GmpExtensionPolicy::isGmpComplianceCase($name)) {
continue;
}
// VM-first (#6212/#6248/#6064): JIT hangs/OOM on create_listen / datagram accept scripts; defer JIT PHPT.
if (str_contains($name, 'socket_create_listen')
|| str_contains($name, 'socket_datagram')
Expand Down
5 changes: 5 additions & 0 deletions test/compliance/VMTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public static function providePHPTests(): \Generator
&& \PHPCompiler\ext\soap\SoapExtensionPolicy::isSoapComplianceCase($name)) {
continue;
}
// Host/profile without gmp: withhold functional gmp_* cases; keep phantom (#22860).
if (!\PHPCompiler\ext\gmp\GmpExtensionPolicy::runsGmpCompliance($name)
&& \PHPCompiler\ext\gmp\GmpExtensionPolicy::isGmpComplianceCase($name)) {
continue;
}
if (!CompilerVersion::supportsStrIncrement()
&& (str_contains($name, 'str_increment') || str_contains($name, 'str_decrement'))
&& !str_contains($name, 'str_increment_phantom')) {
Expand Down
20 changes: 20 additions & 0 deletions test/compliance/cases/stdlib/extension_loaded_gmp_phantom.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
stdlib extension_loaded('gmp') false without host php-gmp / forward profile (#22860, ext/gmp/gmp.c)
--FILE--
<?php
declare(strict_types=1);

echo 'loaded=', (int) extension_loaded('gmp'), "\n";
echo 'in_list=', (int) in_array('gmp', get_loaded_extensions(), true), "\n";
echo 'funcs=', (int) (false !== get_extension_funcs('gmp')), "\n";
echo 'gmp_add=', (int) function_exists('gmp_add'), "\n";
echo 'gmp_init=', (int) function_exists('gmp_init'), "\n";
echo 'GMP=', (int) class_exists('GMP', false), "\n";
?>
--EXPECT--
loaded=0
in_list=0
funcs=0
gmp_add=0
gmp_init=0
GMP=0
5 changes: 5 additions & 0 deletions test/repro/extension_loaded_gmp_phantom.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
/** Repro #22860 — gmp withheld on default/reference profile without host php-gmp. */
echo 'ext=', extension_loaded('gmp') ? 'yes' : 'no', "\n";
echo 'gmp_add=', function_exists('gmp_add') ? 'yes' : 'no', "\n";
echo 'GMP=', class_exists('GMP', false) ? 'yes' : 'no', "\n";
Loading
Loading