Skip to content

Commit c673cab

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix GH-8469: Phar::decompressFiles() keeps compressed contents (#25045)
2 parents a4b0658 + 8d63e87 commit c673cab

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
@@ -197,6 +197,8 @@ PHP NEWS
197197
out-of-range column index). (Ilia Alshanetsky)
198198

199199
- Phar:
200+
. Fixed bug GH-8469 (Phar::decompressFiles() and Phar::compressFiles() retain
201+
compressed contents after changing compression). (Raphael Paez)
200202
. Fixed bug GH-24740 (Oversized tar metadata exhausts the memory limit).
201203
(Lazizbek Ergashev)
202204

‎ext/phar/phar_object.c‎

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

3176+
typedef struct {
3177+
uint32_t compress;
3178+
char *error;
3179+
} phar_compression_args;
3180+
3181+
static int phar_decompress_for_compression_change(zval *zv, void *argument) /* {{{ */
3182+
{
3183+
phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
3184+
phar_compression_args *args = (phar_compression_args *)argument;
3185+
uint32_t current = entry->flags & PHAR_ENT_COMPRESSION_MASK;
3186+
3187+
if (entry->is_deleted || entry->is_dir || !current || current == args->compress) {
3188+
return ZEND_HASH_APPLY_KEEP;
3189+
}
3190+
3191+
/* phar_flush() would otherwise copy the old compressed bytes as the new contents */
3192+
if (FAILURE == phar_open_entry_fp(entry, &args->error, 0)) {
3193+
return ZEND_HASH_APPLY_STOP;
3194+
}
3195+
3196+
return ZEND_HASH_APPLY_KEEP;
3197+
}
3198+
/* }}} */
3199+
31763200
static int phar_test_compression(zval *zv, void *argument) /* {{{ */
31773201
{
31783202
phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv);
@@ -3197,9 +3221,18 @@ static int phar_test_compression(zval *zv, void *argument) /* {{{ */
31973221
}
31983222
/* }}} */
31993223

3200-
static void pharobj_set_compression(HashTable *manifest, uint32_t compress) /* {{{ */
3224+
static zend_result pharobj_set_compression(HashTable *manifest, uint32_t compress, char **error) /* {{{ */
32013225
{
3226+
phar_compression_args args = { .compress = compress, .error = NULL };
3227+
3228+
zend_hash_apply_with_argument(manifest, phar_decompress_for_compression_change, &args);
3229+
if (args.error) {
3230+
*error = args.error;
3231+
return FAILURE;
3232+
}
3233+
32023234
zend_hash_apply_with_argument(manifest, phar_set_compression, &compress);
3235+
return SUCCESS;
32033236
}
32043237
/* }}} */
32053238

@@ -3390,7 +3423,11 @@ PHP_METHOD(Phar, compressFiles)
33903423
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
33913424
RETURN_THROWS();
33923425
}
3393-
pharobj_set_compression(&phar_obj->archive->manifest, flags);
3426+
if (FAILURE == pharobj_set_compression(&phar_obj->archive->manifest, flags, &error)) {
3427+
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3428+
efree(error);
3429+
RETURN_THROWS();
3430+
}
33943431
phar_obj->archive->is_modified = 1;
33953432
phar_flush(phar_obj->archive, &error);
33963433

@@ -3432,7 +3469,11 @@ PHP_METHOD(Phar, decompressFiles)
34323469
zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", phar_obj->archive->fname);
34333470
RETURN_THROWS();
34343471
}
3435-
pharobj_set_compression(&phar_obj->archive->manifest, PHAR_ENT_COMPRESSED_NONE);
3472+
if (FAILURE == pharobj_set_compression(&phar_obj->archive->manifest, PHAR_ENT_COMPRESSED_NONE, &error)) {
3473+
zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "%s", error);
3474+
efree(error);
3475+
RETURN_THROWS();
3476+
}
34363477
}
34373478

34383479
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)