Skip to content
Open
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
2 changes: 1 addition & 1 deletion ext/zip/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ if test "$PHP_ZIP" != "no"; then
AC_DEFINE([HAVE_ZIP], [1],
[Define to 1 if the PHP extension 'zip' is available.])

PHP_NEW_EXTENSION([zip], [php_zip.c zip_stream.c], [$ext_shared])
PHP_NEW_EXTENSION([zip], [php_zip.c zip_source.c zip_stream.c], [$ext_shared])
PHP_ADD_EXTENSION_DEP(zip, pcre)

PHP_SUBST([ZIP_SHARED_LIBADD])
Expand Down
2 changes: 1 addition & 1 deletion ext/zip/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if (PHP_ZIP != "no") {
(PHP_ZIP_SHARED && CHECK_LIB("libzip.lib", "zip", PHP_ZIP) ||
CHECK_LIB("libzip_a.lib", "zip", PHP_ZIP) && CHECK_LIB("libbz2_a.lib", "zip", PHP_ZIP) && CHECK_LIB("zlib_a.lib", "zip", PHP_ZIP) && CHECK_LIB("liblzma_a.lib", "zip", PHP_ZIP))
) {
EXTENSION('zip', 'php_zip.c zip_stream.c');
EXTENSION('zip', 'php_zip.c zip_source.c zip_stream.c');
ADD_EXTENSION_DEP('zip', 'pcre');

if (get_define("LIBS_ZIP").match("libzip_a(?:_debug)?\.lib")) {
Expand Down
91 changes: 53 additions & 38 deletions ext/zip/php_zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -575,30 +575,8 @@ static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */
}
/* }}} */

/* Add a string to the list of buffers to be released when the object is destroyed.*/
static void php_zipobj_add_buffer(ze_zip_object *obj, zend_string *str) /* {{{ */
{
size_t pos = obj->buffers_cnt++;
obj->buffers = safe_erealloc(obj->buffers, sizeof(*obj->buffers), obj->buffers_cnt, 0);
obj->buffers[pos] = zend_string_copy(str);
}
/* }}} */

static void php_zipobj_release_buffers(ze_zip_object *obj) /* {{{ */
{
if (obj->buffers_cnt > 0) {
for (size_t i = 0; i < obj->buffers_cnt; i++) {
zend_string_release(obj->buffers[i]);
}
efree(obj->buffers);
obj->buffers = NULL;
}
obj->buffers_cnt = 0;
}
/* }}} */

/* Close and free the zip_t */
static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */
static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ */
{
struct zip *intern = obj->za;
bool success = false;
Expand Down Expand Up @@ -630,9 +608,17 @@ static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */
obj->filename_len = 0;
}

php_zipobj_release_buffers(obj);
if (obj->out_str) {
if (out_str) {
*out_str = obj->out_str;
} else {
zend_string_release(obj->out_str);
}
obj->out_str = NULL;
}

obj->za = NULL;
obj->from_string = false;
return success;
}
/* }}} */
Expand Down Expand Up @@ -1084,7 +1070,7 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */
{
ze_zip_object * intern = php_zip_fetch_object(object);

php_zipobj_close(intern);
php_zipobj_close(intern, NULL);

#ifdef HAVE_PROGRESS_CALLBACK
/* if not properly called by libzip */
Expand Down Expand Up @@ -1491,7 +1477,7 @@ PHP_METHOD(ZipArchive, open)
}

/* If we already have an opened zip, free it */
php_zipobj_close(ze_obj);
php_zipobj_close(ze_obj, NULL);

/* open for write without option to empty the archive */
if ((flags & (ZIP_TRUNCATE | ZIP_RDONLY)) == 0) {
Expand All @@ -1515,26 +1501,34 @@ PHP_METHOD(ZipArchive, open)
ze_obj->filename = resolved_path;
ze_obj->filename_len = strlen(resolved_path);
ze_obj->za = intern;
ze_obj->from_string = false;
RETURN_TRUE;
}
/* }}} */

/* {{{ Create new read-only zip using given string */
PHP_METHOD(ZipArchive, openString)
{
zend_string *buffer;
zend_string *buffer = NULL;
zend_long flags = 0;
zval *self = ZEND_THIS;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &buffer) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &buffer, &flags) == FAILURE) {
RETURN_THROWS();
}

if (!buffer) {
buffer = ZSTR_EMPTY_ALLOC();
}

ze_zip_object *ze_obj = Z_ZIP_P(self);

php_zipobj_close(ze_obj, NULL);

zip_error_t err;
zip_error_init(&err);

zip_source_t * zip_source = zip_source_buffer_create(ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0, &err);
zip_source_t * zip_source = php_zip_create_string_source(buffer, &ze_obj->out_str, &err);

if (!zip_source) {
ze_obj->err_zip = zip_error_code_zip(&err);
Expand All @@ -1543,9 +1537,7 @@ PHP_METHOD(ZipArchive, openString)
RETURN_LONG(ze_obj->err_zip);
}

php_zipobj_close(ze_obj);

struct zip *intern = zip_open_from_source(zip_source, ZIP_RDONLY, &err);
struct zip *intern = zip_open_from_source(zip_source, flags, &err);
if (!intern) {
ze_obj->err_zip = zip_error_code_zip(&err);
ze_obj->err_sys = zip_error_code_system(&err);
Expand All @@ -1554,7 +1546,7 @@ PHP_METHOD(ZipArchive, openString)
RETURN_LONG(ze_obj->err_zip);
}

php_zipobj_add_buffer(ze_obj, buffer);
ze_obj->from_string = true;
ze_obj->za = intern;
zip_error_fini(&err);
RETURN_TRUE;
Expand Down Expand Up @@ -1593,11 +1585,36 @@ PHP_METHOD(ZipArchive, close)

ZIP_FROM_OBJECT(intern, self);

RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self)));
RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self), NULL));
}
/* }}} */

/* {{{ close the zip archive */
/* {{{ close the zip archive and get the result as a string */
PHP_METHOD(ZipArchive, closeToString)
{
struct zip *intern;
zval *self = ZEND_THIS;

ZEND_PARSE_PARAMETERS_NONE();

ZIP_FROM_OBJECT(intern, self);

if (!Z_ZIP_P(self)->from_string) {
zend_throw_error(NULL, "ZipArchive::closeToString can only be called on "
"an archive opened with ZipArchive::openString");
RETURN_THROWS();
}

zend_string * ret = NULL;
bool success = php_zipobj_close(Z_ZIP_P(self), &ret);
if (success) {
RETURN_STR(ret ? ret : ZSTR_EMPTY_ALLOC());
}
RETURN_FALSE;
}
/* }}} */

/* {{{ get the number of entries */
PHP_METHOD(ZipArchive, count)
{
struct zip *intern;
Expand Down Expand Up @@ -1911,9 +1928,7 @@ PHP_METHOD(ZipArchive, addFromString)
ZIP_FROM_OBJECT(intern, self);

ze_obj = Z_ZIP_P(self);
php_zipobj_add_buffer(ze_obj, buffer);

zs = zip_source_buffer(intern, ZSTR_VAL(buffer), ZSTR_LEN(buffer), 0);
zs = php_zip_create_string_source(buffer, NULL, NULL);

if (zs == NULL) {
RETURN_FALSE;
Expand Down
6 changes: 4 additions & 2 deletions ext/zip/php_zip.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ typedef struct _ze_zip_read_rsrc {
/* Extends zend object */
typedef struct _ze_zip_object {
struct zip *za;
zend_string **buffers;
HashTable *prop_handler;
char *filename;
size_t filename_len;
size_t buffers_cnt;
zend_string * out_str;
bool from_string;
zip_int64_t last_id;
int err_zip;
int err_sys;
Expand All @@ -96,6 +96,8 @@ php_stream *php_stream_zip_open(struct zip *arch, struct zip_stat *sb, const cha

extern const php_stream_wrapper php_stream_zip_wrapper;

zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest, zip_error_t *err);

#define LIBZIP_ATLEAST(m,n,p) (((m<<16) + (n<<8) + p) <= ((LIBZIP_VERSION_MAJOR<<16) + (LIBZIP_VERSION_MINOR<<8) + LIBZIP_VERSION_MICRO))

#endif /* PHP_ZIP_H */
4 changes: 3 additions & 1 deletion ext/zip/php_zip.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ class ZipArchive implements Countable
/** @tentative-return-type */
public function open(string $filename, int $flags = 0): bool|int {}

public function openString(string $data): bool|int {}
public function openString(string $data = '', int $flags = 0): bool|int {}

/**
* @tentative-return-type
Expand All @@ -656,6 +656,8 @@ public function setPassword(#[\SensitiveParameter] string $password): bool {}
/** @tentative-return-type */
public function close(): bool {}

public function closeToString(): bool|string {}

/** @tentative-return-type */
public function count(): int {}

Expand Down
12 changes: 9 additions & 3 deletions ext/zip/php_zip_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions ext/zip/tests/ZipArchive_closeToString_basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
ZipArchive::closeToString() basic
--EXTENSIONS--
zip
--FILE--
<?php
$zip = new ZipArchive();
$zip->openString();
$zip->addFromString('test1', '1');
$zip->addFromString('test2', '2');
$contents = $zip->closeToString();
echo $contents ? "OK\n" : "FAILED\n";

$zip = new ZipArchive();
$zip->openString($contents);
var_dump($zip->getFromName('test1'));
var_dump($zip->getFromName('test2'));
var_dump($zip->getFromName('nonexistent'));

?>
--EXPECT--
OK
string(1) "1"
string(1) "2"
bool(false)
45 changes: 45 additions & 0 deletions ext/zip/tests/ZipArchive_closeToString_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
--TEST--
ZipArchive::closeToString() error cases
--EXTENSIONS--
zip
--FILE--
<?php
echo "1.\n";
$zip = new ZipArchive();
$zip->openString();
var_dump($zip->open(__DIR__ . '/test.zip'));
try {
$zip->closeToString();
} catch (Error $e) {
echo $e->getMessage() . "\n";
}

echo "2.\n";
$zip = new ZipArchive();
$zip->openString('...');
echo $zip->getStatusString() . "\n";
try {
$zip->closeToString();
} catch (Error $e) {
echo $e->getMessage() . "\n";
}

echo "3.\n";
$input = file_get_contents(__DIR__ . '/test.zip');
$zip = new ZipArchive();
$zip->openString($input);
$zip->addFromString('entry1.txt', '');
$result = $zip->closeToString();
echo gettype($result) . "\n";
var_dump($input !== $result);
?>
--EXPECT--
1.
bool(true)
ZipArchive::closeToString can only be called on an archive opened with ZipArchive::openString
2.
Not a zip archive
Invalid or uninitialized Zip object
3.
string
bool(true)
22 changes: 22 additions & 0 deletions ext/zip/tests/ZipArchive_closeToString_false.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
ZipArchive::closeToString() false return
--EXTENSIONS--
zip
--FILE--
<?php
$zip = new ZipArchive();
// The "compressed size" fields are wrong, causing an error when reading the contents.
// The error is reported on close when we rewrite the member with setCompressionIndex().
// The error code is ER_DATA_LENGTH in libzip 1.10.0+ or ER_INCONS otherwise.
$input = file_get_contents(__DIR__ . '/wrong-file-size.zip');
var_dump($zip->openString($input));
$zip->setCompressionIndex(0, ZipArchive::CM_DEFLATE);
var_dump($zip->closeToString());
echo $zip->getStatusString() . "\n";
?>
--EXPECTREGEX--
bool\(true\)

Warning: ZipArchive::closeToString\(\): (Zip archive inconsistent|Unexpected length of data).*
bool\(false\)
(Zip archive inconsistent|Unexpected length of data)
14 changes: 14 additions & 0 deletions ext/zip/tests/ZipArchive_closeToString_variation.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
ZipArchive::closeToString() variations
--EXTENSIONS--
zip
--FILE--
<?php
$zip = new ZipArchive();
$zip->openString();
var_dump($zip->closeToString());
echo $zip->getStatusString() . "\n";
?>
--EXPECT--
string(0) ""
No error
2 changes: 1 addition & 1 deletion ext/zip/tests/ZipArchive_openString.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ zip
--FILE--
<?php
$zip = new ZipArchive();
$zip->openString(file_get_contents(__DIR__."/test_procedural.zip"));
$zip->openString(file_get_contents(__DIR__."/test_procedural.zip"), ZipArchive::RDONLY);

for ($i = 0; $i < $zip->numFiles; $i++) {
$stat = $zip->statIndex($i);
Expand Down
Binary file added ext/zip/tests/wrong-file-size.zip
Binary file not shown.
Loading
Loading