Skip to content

Commit 3af1cee

Browse files
committed
add ZipArchive::registerProgressCallback and ZipArchive::registerCancelCallback methods
1 parent 7e7a85e commit 3af1cee

File tree

7 files changed

+286
-0
lines changed

7 files changed

+286
-0
lines changed

ext/zip/config.m4

+16
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ if test "$PHP_ZIP" != "no"; then
3535
$LIBZIP_LIBS
3636
])
3737

38+
PHP_CHECK_LIBRARY(zip, zip_register_progress_callback_with_state,
39+
[
40+
AC_DEFINE(HAVE_PROGRESS_CALLBACK, 1, [Libzip >= 1.3.0 with zip_register_progress_callback_with_state function])
41+
], [
42+
], [
43+
$LIBZIP_LIBS
44+
])
45+
46+
PHP_CHECK_LIBRARY(zip, zip_register_cancel_callback_with_state,
47+
[
48+
AC_DEFINE(HAVE_CANCEL_CALLBACK, 1, [Libzip >= 1.6.0 with zip_register_cancel_callback_with_state function])
49+
], [
50+
], [
51+
$LIBZIP_LIBS
52+
])
53+
3854
AC_DEFINE(HAVE_ZIP,1,[ ])
3955

4056
PHP_ZIP_SOURCES="php_zip.c zip_stream.c"

ext/zip/php_zip.c

+156
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,30 @@ static HashTable *php_zip_get_properties(zend_object *object)/* {{{ */
933933
}
934934
/* }}} */
935935

936+
#ifdef HAVE_PROGRESS_CALLBACK
937+
static void _php_zip_progress_callback_free(void *ptr)
938+
{
939+
ze_zip_object *obj = ptr;
940+
941+
if (!Z_ISUNDEF(obj->progress_callback)) {
942+
zval_ptr_dtor(&obj->progress_callback);
943+
ZVAL_UNDEF(&obj->progress_callback);
944+
}
945+
}
946+
#endif
947+
948+
#ifdef HAVE_CANCEL_CALLBACK
949+
static void _php_zip_cancel_callback_free(void *ptr)
950+
{
951+
ze_zip_object *obj = ptr;
952+
953+
if (!Z_ISUNDEF(obj->cancel_callback)) {
954+
zval_ptr_dtor(&obj->cancel_callback);
955+
ZVAL_UNDEF(&obj->cancel_callback);
956+
}
957+
}
958+
#endif
959+
936960
static void php_zip_object_free_storage(zend_object *object) /* {{{ */
937961
{
938962
ze_zip_object * intern = php_zip_fetch_object(object);
@@ -959,6 +983,16 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */
959983
efree(intern->buffers);
960984
}
961985

986+
#ifdef HAVE_PROGRESS_CALLBACK
987+
/* if not properly called by libzip */
988+
_php_zip_progress_callback_free(intern);
989+
#endif
990+
991+
#ifdef HAVE_CANCEL_CALLBACK
992+
/* if not properly called by libzip */
993+
_php_zip_cancel_callback_free(intern);
994+
#endif
995+
962996
intern->za = NULL;
963997
zend_object_std_dtor(&intern->zo);
964998

@@ -2774,6 +2808,121 @@ static ZIPARCHIVE_METHOD(getStream)
27742808
}
27752809
/* }}} */
27762810

2811+
#ifdef HAVE_PROGRESS_CALLBACK
2812+
static void _php_zip_progress_callback(zip_t *arch, double state, void *ptr)
2813+
{
2814+
zval cb_args[1];
2815+
zval cb_retval;
2816+
ze_zip_object *obj = ptr;
2817+
2818+
ZVAL_DOUBLE(&cb_args[0], state);
2819+
if (call_user_function_ex(EG(function_table), NULL, &obj->progress_callback, &cb_retval, 1, cb_args, 0, NULL) == SUCCESS && !Z_ISUNDEF(cb_retval)) {
2820+
zval_ptr_dtor(&cb_retval);
2821+
}
2822+
}
2823+
2824+
/* {{{ proto bool ZipArchive::registerProgressCallback(double rate, callable callback)
2825+
register a progression callback: void callback(double state); */
2826+
static ZIPARCHIVE_METHOD(registerProgressCallback)
2827+
{
2828+
struct zip *intern;
2829+
zval *self = getThis();
2830+
double rate;
2831+
zval *callback;
2832+
ze_zip_object *obj;
2833+
2834+
if (!self) {
2835+
RETURN_FALSE;
2836+
}
2837+
2838+
ZIP_FROM_OBJECT(intern, self);
2839+
2840+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "dz", &rate, &callback) == FAILURE) {
2841+
return;
2842+
}
2843+
2844+
/* callable? */
2845+
if (!zend_is_callable(callback, 0, NULL)) {
2846+
zend_string *callback_name = zend_get_callable_name(callback);
2847+
php_error_docref(NULL, E_WARNING, "Invalid callback '%s'", ZSTR_VAL(callback_name));
2848+
zend_string_release_ex(callback_name, 0);
2849+
RETURN_FALSE;
2850+
}
2851+
2852+
obj = Z_ZIP_P(self);
2853+
2854+
/* free if called twice */
2855+
_php_zip_progress_callback_free(obj);
2856+
2857+
/* register */
2858+
ZVAL_COPY(&obj->progress_callback, callback);
2859+
if (zip_register_progress_callback_with_state(intern, rate, _php_zip_progress_callback, _php_zip_progress_callback_free, obj)) {
2860+
RETURN_FALSE;
2861+
}
2862+
2863+
RETURN_TRUE;
2864+
}
2865+
/* }}} */
2866+
#endif
2867+
2868+
#ifdef HAVE_CANCEL_CALLBACK
2869+
static int _php_zip_cancel_callback(zip_t *arch, void *ptr)
2870+
{
2871+
zval cb_retval;
2872+
int retval = 0;
2873+
ze_zip_object *obj = ptr;
2874+
2875+
if (call_user_function_ex(EG(function_table), NULL, &obj->cancel_callback, &cb_retval, 0, NULL, 0, NULL) == SUCCESS && !Z_ISUNDEF(cb_retval)) {
2876+
retval = zval_get_long(&cb_retval);
2877+
zval_ptr_dtor(&cb_retval);
2878+
}
2879+
2880+
return retval;
2881+
}
2882+
2883+
/* {{{ proto bool ZipArchive::registerCancelCallback(callable callback)
2884+
register a progression callback: int callback(double state); */
2885+
static ZIPARCHIVE_METHOD(registerCancelCallback)
2886+
{
2887+
struct zip *intern;
2888+
zval *self = getThis();
2889+
zval *callback;
2890+
ze_zip_object *obj;
2891+
2892+
if (!self) {
2893+
RETURN_FALSE;
2894+
}
2895+
2896+
ZIP_FROM_OBJECT(intern, self);
2897+
2898+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callback) == FAILURE) {
2899+
return;
2900+
}
2901+
2902+
/* callable? */
2903+
if (!zend_is_callable(callback, 0, NULL)) {
2904+
zend_string *callback_name = zend_get_callable_name(callback);
2905+
php_error_docref(NULL, E_WARNING, "Invalid callback '%s'", ZSTR_VAL(callback_name));
2906+
zend_string_release_ex(callback_name, 0);
2907+
RETURN_FALSE;
2908+
}
2909+
2910+
obj = Z_ZIP_P(self);
2911+
2912+
/* free if called twice */
2913+
_php_zip_cancel_callback_free(obj);
2914+
2915+
/* register */
2916+
ZVAL_COPY(&obj->cancel_callback, callback);
2917+
if (zip_register_cancel_callback_with_state(intern, _php_zip_cancel_callback, _php_zip_cancel_callback_free, obj)) {
2918+
RETURN_FALSE;
2919+
}
2920+
2921+
RETURN_TRUE;
2922+
}
2923+
/* }}} */
2924+
#endif
2925+
27772926
/* {{{ ze_zip_object_class_functions */
27782927
static const zend_function_entry zip_class_functions[] = {
27792928
ZIPARCHIVE_ME(open, arginfo_class_ZipArchive_open, ZEND_ACC_PUBLIC)
@@ -2824,6 +2973,13 @@ static const zend_function_entry zip_class_functions[] = {
28242973
ZIPARCHIVE_ME(setEncryptionName, arginfo_class_ZipArchive_setEncryptionName, ZEND_ACC_PUBLIC)
28252974
ZIPARCHIVE_ME(setEncryptionIndex, arginfo_class_ZipArchive_setEncryptionIndex, ZEND_ACC_PUBLIC)
28262975
#endif
2976+
#ifdef HAVE_PROGRESS_CALLBACK
2977+
ZIPARCHIVE_ME(registerProgressCallback, arginfo_class_ZipArchive_registerProgressCallback, ZEND_ACC_PUBLIC)
2978+
#endif
2979+
#ifdef HAVE_CANCEL_CALLBACK
2980+
ZIPARCHIVE_ME(registerCancelCallback, arginfo_class_ZipArchive_registerCancelCallback, ZEND_ACC_PUBLIC)
2981+
#endif
2982+
28272983
PHP_FE_END
28282984
};
28292985
/* }}} */

ext/zip/php_zip.h

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ typedef struct _ze_zip_object {
6060
int filename_len;
6161
int buffers_cnt;
6262
zend_object zo;
63+
#ifdef HAVE_PROGRESS_CALLBACK
64+
zval progress_callback;
65+
#endif
66+
#ifdef HAVE_CANCEL_CALLBACK
67+
zval cancel_callback;
68+
#endif
6369
} ze_zip_object;
6470

6571
static inline ze_zip_object *php_zip_fetch_object(zend_object *obj) {

ext/zip/php_zip.stub.php

+12
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,13 @@ public function setCommentIndex(int $index, string $comment) {}
9090
/** @return null|false */
9191
public function setCommentName(string $name, string $comment) {}
9292

93+
#ifdef HAVE_SET_MTIME
9394
/** @return null|false */
9495
public function setMtimeIndex(int $index, int $timestamp, int $flags = 0) {}
9596

9697
/** @return null|false */
9798
public function setMtimeName(string $name, int $timestamp, int $flags = 0) {}
99+
#endif
98100

99101
/** @return string|false */
100102
public function getCommentIndex(int $index, int $flags = 0) {}
@@ -171,4 +173,14 @@ public function setEncryptionName(string $name, int $method, string $password =
171173
/** @return bool */
172174
public function setEncryptionIndex(int $index, int $method, string $password = UNKNOWN) {}
173175
#endif
176+
177+
#ifdef HAVE_PROGRESS_CALLBACK
178+
/** @return bool */
179+
public function registerProgressCallback(float $rate, callable $callback) {}
180+
#endif
181+
182+
#ifdef HAVE_CANCEL_CALLBACK
183+
/** @return bool */
184+
public function registerCancelCallback(callable $callback) {}
185+
#endif
174186
}

ext/zip/php_zip_arginfo.h

+17
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setCommentName, 0, 0, 2)
111111
ZEND_ARG_TYPE_INFO(0, comment, IS_STRING, 0)
112112
ZEND_END_ARG_INFO()
113113

114+
#if defined(HAVE_SET_MTIME)
114115
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setMtimeIndex, 0, 0, 2)
115116
ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
116117
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
117118
ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
118119
ZEND_END_ARG_INFO()
120+
#endif
119121

122+
#if defined(HAVE_SET_MTIME)
120123
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setMtimeName, 0, 0, 2)
121124
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
122125
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
123126
ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
124127
ZEND_END_ARG_INFO()
128+
#endif
125129

126130
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_getCommentIndex, 0, 0, 1)
127131
ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0)
@@ -241,3 +245,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setEncryptionIndex, 0, 0, 2)
241245
ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0)
242246
ZEND_END_ARG_INFO()
243247
#endif
248+
249+
#if defined(HAVE_PROGRESS_CALLBACK)
250+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_registerProgressCallback, 0, 0, 2)
251+
ZEND_ARG_TYPE_INFO(0, rate, IS_DOUBLE, 0)
252+
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
253+
ZEND_END_ARG_INFO()
254+
#endif
255+
256+
#if defined(HAVE_CANCEL_CALLBACK)
257+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_registerCancelCallback, 0, 0, 1)
258+
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
259+
ZEND_END_ARG_INFO()
260+
#endif

ext/zip/tests/oo_cancel.phpt

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
registerCancelCallback
3+
--SKIPIF--
4+
<?php
5+
/* $Id$ */
6+
if(!extension_loaded('zip')) die('skip');
7+
if (!method_exists('ZipArchive', 'registerCancelCallback')) die('skip libzip too old');
8+
?>
9+
--INI--
10+
date.timezone=UTC
11+
--FILE--
12+
<?php
13+
$dirname = dirname(__FILE__) . '/';
14+
$file = $dirname . '__tmp_oo_progress.zip';
15+
16+
@unlink($file);
17+
18+
$zip = new ZipArchive;
19+
if (!$zip->open($file, ZIPARCHIVE::CREATE)) {
20+
exit('failed');
21+
}
22+
23+
var_dump($zip->registerCancelCallback(function () {
24+
// Always cancel
25+
return -1;
26+
}));
27+
var_dump($zip->addFromString(PHP_BINARY, 'entry #1'));
28+
29+
var_dump($zip->close());
30+
@unlink($file);
31+
?>
32+
Done
33+
--EXPECTF--
34+
bool(true)
35+
bool(true)
36+
37+
Warning: ZipArchive::close(): Operation cancelled in %s
38+
bool(false)
39+
Done

ext/zip/tests/oo_progress.phpt

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
registerProgressCallback
3+
--SKIPIF--
4+
<?php
5+
/* $Id$ */
6+
if(!extension_loaded('zip')) die('skip');
7+
if (!method_exists('ZipArchive', 'registerProgressCallback')) die('skip libzip too old');
8+
?>
9+
--INI--
10+
date.timezone=UTC
11+
--FILE--
12+
<?php
13+
$dirname = dirname(__FILE__) . '/';
14+
$file = $dirname . '__tmp_oo_progress.zip';
15+
16+
@unlink($file);
17+
18+
$zip = new ZipArchive;
19+
if (!$zip->open($file, ZIPARCHIVE::CREATE)) {
20+
exit('failed');
21+
}
22+
23+
var_dump($zip->registerProgressCallback(0.5, function ($r) {
24+
// Only check start/end as intermediate is not reliable
25+
if ($r == 0.0) echo "start\n";
26+
if ($r == 1.0) echo "end\n";
27+
}));
28+
var_dump($zip->addFromString('foo', 'entry #1'));
29+
30+
var_dump($zip->close());
31+
unlink($file);
32+
?>
33+
Done
34+
--EXPECTF--
35+
bool(true)
36+
bool(true)
37+
start
38+
end
39+
bool(true)
40+
Done

0 commit comments

Comments
 (0)