Skip to content

Commit 524e66b

Browse files
committed
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5: Fix GH-8469: Phar::decompressFiles() keeps compressed contents (#25045)
2 parents 63045d5 + c673cab commit 524e66b

3 files changed

Lines changed: 105 additions & 3 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ PHP NEWS
128128
PgBouncer's prepared statements). (David Carlier, Artem Ukrainskiy)
129129

130130
- Phar:
131+
. Fixed bug GH-8469 (Phar::decompressFiles() and Phar::compressFiles() retain
132+
compressed contents after changing compression). (Raphael Paez)
131133
. Fixed bug GH-24740 (Oversized tar metadata exhausts the memory limit).
132134
(Lazizbek Ergashev)
133135

‎ext/phar/phar_object.c‎

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3107,6 +3107,30 @@ static int phar_set_compression(zval *zv, void *argument) /* {{{ */
31073107
}
31083108
/* }}} */
31093109

3110+
typedef struct {
3111+
uint32_t compress;
3112+
char *error;
3113+
} phar_compression_args;
3114+
3115+
static int phar_decompress_for_compression_change(zval *zv, void *argument) /* {{{ */
3116+
{
3117+
phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
3118+
phar_compression_args *args = (phar_compression_args *)argument;
3119+
uint32_t current = entry->flags & PHAR_ENT_COMPRESSION_MASK;
3120+
3121+
if (entry->is_deleted || entry->is_dir || !current || current == args->compress) {
3122+
return ZEND_HASH_APPLY_KEEP;
3123+
}
3124+
3125+
/* phar_flush() would otherwise copy the old compressed bytes as the new contents */
3126+
if (FAILURE == phar_open_entry_fp(entry, &args->error, 0)) {
3127+
return ZEND_HASH_APPLY_STOP;
3128+
}
3129+
3130+
return ZEND_HASH_APPLY_KEEP;
3131+
}
3132+
/* }}} */
3133+
31103134
static int phar_test_compression(zval *zv, void *argument) /* {{{ */
31113135
{
31123136
const phar_entry_info *entry = Z_PTR_P(zv);
@@ -3133,9 +3157,18 @@ static int phar_test_compression(zval *zv, void *argument) /* {{{ */
31333157
}
31343158
/* }}} */
31353159

3136-
static void pharobj_set_compression(HashTable *manifest, uint32_t compress) /* {{{ */
3160+
static zend_result pharobj_set_compression(HashTable *manifest, uint32_t compress, char **error) /* {{{ */
31373161
{
3162+
phar_compression_args args = { .compress = compress, .error = NULL };
3163+
3164+
zend_hash_apply_with_argument(manifest, phar_decompress_for_compression_change, &args);
3165+
if (args.error) {
3166+
*error = args.error;
3167+
return FAILURE;
3168+
}
3169+
31383170
zend_hash_apply_with_argument(manifest, phar_set_compression, &compress);
3171+
return SUCCESS;
31393172
}
31403173
/* }}} */
31413174

@@ -3326,7 +3359,11 @@ PHP_METHOD(Phar, compressFiles)
33263359
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%pS\" is persistent, unable to copy on write", phar_obj->archive->fname);
33273360
RETURN_THROWS();
33283361
}
3329-
pharobj_set_compression(&phar_obj->archive->manifest, flags);
3362+
if (FAILURE == pharobj_set_compression(&phar_obj->archive->manifest, flags, &error)) {
3363+
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3364+
efree(error);
3365+
RETURN_THROWS();
3366+
}
33303367
phar_obj->archive->is_modified = 1;
33313368
phar_flush(phar_obj->archive, &error);
33323369

@@ -3365,7 +3402,11 @@ PHP_METHOD(Phar, decompressFiles)
33653402
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%pS\" is persistent, unable to copy on write", phar_obj->archive->fname);
33663403
RETURN_THROWS();
33673404
}
3368-
pharobj_set_compression(&phar_obj->archive->manifest, PHAR_ENT_COMPRESSED_NONE);
3405+
if (FAILURE == pharobj_set_compression(&phar_obj->archive->manifest, PHAR_ENT_COMPRESSED_NONE, &error)) {
3406+
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3407+
efree(error);
3408+
RETURN_THROWS();
3409+
}
33693410
}
33703411

33713412
phar_obj->archive->is_modified = 1;

‎ext/phar/tests/gh8469.phpt‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
GH-8469 (Phar::decompressFiles() and Phar::compressFiles() keep the old compressed contents)
3+
--EXTENSIONS--
4+
phar
5+
zlib
6+
bz2
7+
--INI--
8+
phar.require_hash=0
9+
phar.readonly=0
10+
--FILE--
11+
<?php
12+
$contents = str_repeat('four score and seven years ago ', 10);
13+
14+
foreach (['phar', 'phar.zip'] as $ext) {
15+
$fname = __DIR__ . "/gh8469.$ext";
16+
$phar = new Phar($fname);
17+
$phar['a.txt'] = $contents;
18+
$phar['b.txt'] = 'our fathers';
19+
$phar->addEmptyDir('dir');
20+
21+
$phar->compressFiles(Phar::GZ);
22+
$phar->compressFiles(Phar::BZ2);
23+
var_dump($phar['a.txt']->isCompressed(Phar::BZ2));
24+
var_dump($phar['a.txt']->getContent() === $contents);
25+
26+
var_dump($phar->decompressFiles());
27+
var_dump($phar['a.txt']->isCompressed());
28+
var_dump($phar['a.txt']->getContent() === $contents);
29+
var_dump($phar['b.txt']->getContent());
30+
unset($phar);
31+
32+
$phar = new Phar($fname);
33+
var_dump($phar['a.txt']->getContent() === $contents);
34+
var_dump(file_get_contents("phar://$fname/b.txt"));
35+
unset($phar);
36+
}
37+
?>
38+
--CLEAN--
39+
<?php
40+
@unlink(__DIR__ . '/gh8469.phar');
41+
@unlink(__DIR__ . '/gh8469.phar.zip');
42+
?>
43+
--EXPECT--
44+
bool(true)
45+
bool(true)
46+
bool(true)
47+
bool(false)
48+
bool(true)
49+
string(11) "our fathers"
50+
bool(true)
51+
string(11) "our fathers"
52+
bool(true)
53+
bool(true)
54+
bool(true)
55+
bool(false)
56+
bool(true)
57+
string(11) "our fathers"
58+
bool(true)
59+
string(11) "our fathers"

0 commit comments

Comments
 (0)