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
1 change: 1 addition & 0 deletions ext/standard/ModuleRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ private static function functionBelongsToLogicalExtension(string $functionName,
'readline' => str_starts_with($functionName, 'readline'),
'bcmath' => str_starts_with($functionName, 'bc'),
'openssl' => str_starts_with($functionName, 'openssl_'),
'zip' => str_starts_with($functionName, 'zip_'),
default => false,
};
}
Expand Down
20 changes: 17 additions & 3 deletions ext/zip/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
use PHPCompiler\Runtime;

/**
* zip extension module entry (php-src ext/zip/php_zip.c; issues #5869, #3337, #6370).
* zip extension module entry (php-src ext/zip/php_zip.c; issues #5869, #3337, #6370, #18137).
*
* ZipArchive uses pure-PHP store engine ({@see ZipEngine}); procedural zip_* API in {@see VmZipProcedural}.
* Register under {@see standard}; advertise logical {@code zip} extension, ZipArchive, and
* zip_* procedural API when {@see ZipExtensionPolicy::advertisesExtension()} — withheld on
* reference profile (Zend 8.2 harness has no ext/zip).
*/
class Module extends ModuleAbstract
{
Expand All @@ -22,7 +24,19 @@ public function init(Runtime $runtime): void

public function getExtensionName(): string
{
return 'zip';
return 'standard';
}

/**
* @return list<string>
*/
public function getAdditionalExtensionNames(): array
{
if (!ZipExtensionPolicy::advertisesExtension()) {
return [];
}

return ['zip'];
}

public function getFunctions(): array
Expand Down
10 changes: 7 additions & 3 deletions ext/zip/ZipExtensionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

namespace PHPCompiler\ext\zip;

use PHPCompiler\CompilerVersion;

/**
* ext/zip surface advertisement — php-src ext/zip/php_zip.c (#11676, #3337).
* ext/zip surface advertisement — php-src ext/zip/php_zip.c (#11676, #18137, #3337).
*
* Pure-PHP ZipArchive ({@see VmZipArchive}) registers when the zip module loads.
* Pure-PHP ZipArchive ({@see VmZipArchive}) and zip_* procedural API stay compiled in-tree but
* are withheld from extension_loaded() and class_exists() on the reference profile until
* {@see CompilerVersion::supportsZip()}.
*/
final class ZipExtensionPolicy
{
public static function advertisesExtension(): bool
{
return class_exists(VmZipArchive::class);
return CompilerVersion::supportsZip();
}
}
11 changes: 11 additions & 0 deletions lib/CompilerVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -2284,6 +2284,17 @@ public static function supportsMsgpack(): bool
return version_compare(self::languageProfileVersion(), '8.4.0', '>=');
}

/**
* ext/zip via pure PHP {@see \PHPCompiler\ext\zip\VmZipArchive} — withheld on reference profile (#18137, #11676).
*
* Gated on stable 8.4.0 / {@see languageProfileVersion()} so 8.4.0-dev reference profile matches Zend 8.2
* phantom gate (host ext-zip absent). Enable forward profile via `PHP_COMPILER_PROFILE=8.4`.
*/
public static function supportsZip(): bool
{
return version_compare(self::languageProfileVersion(), '8.4.0', '>=');
}

/**
* ext/brotli via pure PHP {@see \PHPCompiler\ext\brotli\VmBrotliNative} — withheld on reference profile (#17563).
*
Expand Down
6 changes: 6 additions & 0 deletions test/compliance/JITTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,12 @@ public static function providePHPTests(): \Generator
&& !str_contains($name, 'extension_loaded_msgpack')) {
continue;
}
if (!CompilerVersion::supportsZip()
&& (str_contains($name, 'zip')
|| str_contains($name, 'ziparchive'))
&& !str_contains($name, 'extension_loaded_zip')) {
continue;
}
if (!CompilerVersion::supportsUri()
&& str_contains($name, 'uri_rfc3986')
&& !str_contains($name, 'uri_phantom')) {
Expand Down
6 changes: 6 additions & 0 deletions test/compliance/VMTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,12 @@ public static function providePHPTests(): \Generator
&& !str_contains($name, 'extension_loaded_msgpack')) {
continue;
}
if (!CompilerVersion::supportsZip()
&& (str_contains($name, 'zip')
|| str_contains($name, 'ziparchive'))
&& !str_contains($name, 'extension_loaded_zip')) {
continue;
}
if (!CompilerVersion::supportsUri()
&& str_contains($name, 'uri_rfc3986')
&& !str_contains($name, 'uri_phantom')) {
Expand Down
12 changes: 7 additions & 5 deletions test/compliance/cases/stdlib/extension_loaded_zip_phantom.phpt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
--TEST--
stdlib extension_loaded('zip') true with pure-PHP ZipArchive (#3337, ext/zip/php_zip.c)
stdlib extension_loaded('zip') withheld on reference profile (#18137, ext/zip/php_zip.c)
--FILE--
<?php
declare(strict_types=1);

echo 'loaded=', (int) extension_loaded('zip'), "\n";
echo 'in_list=', (int) in_array('zip', get_loaded_extensions(), true), "\n";
echo 'class=', (int) class_exists('ZipArchive'), "\n";
echo 'class=', (int) class_exists('ZipArchive', false), "\n";
echo 'zip_open=', (int) function_exists('zip_open'), "\n";
--EXPECT--
loaded=1
in_list=1
class=1
loaded=0
in_list=0
class=0
zip_open=0
5 changes: 5 additions & 0 deletions test/compliance/cases/stdlib/ziparchive_open_add.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
--TEST--
stdlib ZipArchive open/addFromString/getFromName round-trip (#3337, ext/zip/php_zip.c)
--SKIPIF--
<?php
if (!\PHPCompiler\CompilerVersion::supportsZip()) {
die('skip zip withheld on reference profile (#18137)');
}
--FILE--
<?php
declare(strict_types=1);
Expand Down
15 changes: 15 additions & 0 deletions test/compliance/cases/zip/extension_loaded_zip_phantom.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
zip extension_loaded('zip') withheld on reference profile (#18137, ext/zip/php_zip.c)
--FILE--
<?php
declare(strict_types=1);

echo 'loaded=', (int) extension_loaded('zip'), "\n";
echo 'in_list=', (int) in_array('zip', get_loaded_extensions(), true), "\n";
echo 'class=', (int) class_exists('ZipArchive', false), "\n";
echo 'zip_open=', (int) function_exists('zip_open'), "\n";
--EXPECT--
loaded=0
in_list=0
class=0
zip_open=0
5 changes: 5 additions & 0 deletions test/compliance/cases/zip/zip_procedural_basic.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
--TEST--
zip procedural API round-trip (ext/zip/php_zip.c, #6370)
--SKIPIF--
<?php
if (!\PHPCompiler\CompilerVersion::supportsZip()) {
die('skip zip withheld on reference profile (#18137)');
}
--FILE--
<?php
$tmpdir = sys_get_temp_dir() . '/phpc_zip_proc_' . bin2hex(random_bytes(4));
Expand Down
31 changes: 31 additions & 0 deletions test/repro/maintainer_gap_zip_extension_phantom.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

/**
* Maintainer repro: extension_loaded('zip') must be false without ext/zip on reference profile (#18137).
*
* php-src: ext/standard/info.c — extension_loaded only true for linked modules.
*/

if (extension_loaded('zip')) {
echo 'FAIL extension_loaded(zip)=true';
exit(1);
}

if (\in_array('zip', get_loaded_extensions(), true)) {
echo 'FAIL zip in get_loaded_extensions()';
exit(1);
}

if (class_exists('ZipArchive', false)) {
echo 'FAIL ZipArchive class advertised';
exit(1);
}

if (\function_exists('zip_open')) {
echo 'FAIL zip_open advertised';
exit(1);
}

echo "ok\n";