From 3af1cee8840a1ccaca90a4392f79510274f84450 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 30 Jan 2020 11:14:53 +0100 Subject: [PATCH] add ZipArchive::registerProgressCallback and ZipArchive::registerCancelCallback methods --- ext/zip/config.m4 | 16 ++++ ext/zip/php_zip.c | 156 +++++++++++++++++++++++++++++++++ ext/zip/php_zip.h | 6 ++ ext/zip/php_zip.stub.php | 12 +++ ext/zip/php_zip_arginfo.h | 17 ++++ ext/zip/tests/oo_cancel.phpt | 39 +++++++++ ext/zip/tests/oo_progress.phpt | 40 +++++++++ 7 files changed, 286 insertions(+) create mode 100644 ext/zip/tests/oo_cancel.phpt create mode 100644 ext/zip/tests/oo_progress.phpt diff --git a/ext/zip/config.m4 b/ext/zip/config.m4 index 943852e4e6..abd84ff51d 100644 --- a/ext/zip/config.m4 +++ b/ext/zip/config.m4 @@ -35,6 +35,22 @@ if test "$PHP_ZIP" != "no"; then $LIBZIP_LIBS ]) + PHP_CHECK_LIBRARY(zip, zip_register_progress_callback_with_state, + [ + AC_DEFINE(HAVE_PROGRESS_CALLBACK, 1, [Libzip >= 1.3.0 with zip_register_progress_callback_with_state function]) + ], [ + ], [ + $LIBZIP_LIBS + ]) + + PHP_CHECK_LIBRARY(zip, zip_register_cancel_callback_with_state, + [ + AC_DEFINE(HAVE_CANCEL_CALLBACK, 1, [Libzip >= 1.6.0 with zip_register_cancel_callback_with_state function]) + ], [ + ], [ + $LIBZIP_LIBS + ]) + AC_DEFINE(HAVE_ZIP,1,[ ]) PHP_ZIP_SOURCES="php_zip.c zip_stream.c" diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 8f16f236c3..74d992a5f6 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -933,6 +933,30 @@ static HashTable *php_zip_get_properties(zend_object *object)/* {{{ */ } /* }}} */ +#ifdef HAVE_PROGRESS_CALLBACK +static void _php_zip_progress_callback_free(void *ptr) +{ + ze_zip_object *obj = ptr; + + if (!Z_ISUNDEF(obj->progress_callback)) { + zval_ptr_dtor(&obj->progress_callback); + ZVAL_UNDEF(&obj->progress_callback); + } +} +#endif + +#ifdef HAVE_CANCEL_CALLBACK +static void _php_zip_cancel_callback_free(void *ptr) +{ + ze_zip_object *obj = ptr; + + if (!Z_ISUNDEF(obj->cancel_callback)) { + zval_ptr_dtor(&obj->cancel_callback); + ZVAL_UNDEF(&obj->cancel_callback); + } +} +#endif + static void php_zip_object_free_storage(zend_object *object) /* {{{ */ { ze_zip_object * intern = php_zip_fetch_object(object); @@ -959,6 +983,16 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */ efree(intern->buffers); } +#ifdef HAVE_PROGRESS_CALLBACK + /* if not properly called by libzip */ + _php_zip_progress_callback_free(intern); +#endif + +#ifdef HAVE_CANCEL_CALLBACK + /* if not properly called by libzip */ + _php_zip_cancel_callback_free(intern); +#endif + intern->za = NULL; zend_object_std_dtor(&intern->zo); @@ -2774,6 +2808,121 @@ static ZIPARCHIVE_METHOD(getStream) } /* }}} */ +#ifdef HAVE_PROGRESS_CALLBACK +static void _php_zip_progress_callback(zip_t *arch, double state, void *ptr) +{ + zval cb_args[1]; + zval cb_retval; + ze_zip_object *obj = ptr; + + ZVAL_DOUBLE(&cb_args[0], state); + if (call_user_function_ex(EG(function_table), NULL, &obj->progress_callback, &cb_retval, 1, cb_args, 0, NULL) == SUCCESS && !Z_ISUNDEF(cb_retval)) { + zval_ptr_dtor(&cb_retval); + } +} + +/* {{{ proto bool ZipArchive::registerProgressCallback(double rate, callable callback) +register a progression callback: void callback(double state); */ +static ZIPARCHIVE_METHOD(registerProgressCallback) +{ + struct zip *intern; + zval *self = getThis(); + double rate; + zval *callback; + ze_zip_object *obj; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "dz", &rate, &callback) == FAILURE) { + return; + } + + /* callable? */ + if (!zend_is_callable(callback, 0, NULL)) { + zend_string *callback_name = zend_get_callable_name(callback); + php_error_docref(NULL, E_WARNING, "Invalid callback '%s'", ZSTR_VAL(callback_name)); + zend_string_release_ex(callback_name, 0); + RETURN_FALSE; + } + + obj = Z_ZIP_P(self); + + /* free if called twice */ + _php_zip_progress_callback_free(obj); + + /* register */ + ZVAL_COPY(&obj->progress_callback, callback); + if (zip_register_progress_callback_with_state(intern, rate, _php_zip_progress_callback, _php_zip_progress_callback_free, obj)) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ +#endif + +#ifdef HAVE_CANCEL_CALLBACK +static int _php_zip_cancel_callback(zip_t *arch, void *ptr) +{ + zval cb_retval; + int retval = 0; + ze_zip_object *obj = ptr; + + if (call_user_function_ex(EG(function_table), NULL, &obj->cancel_callback, &cb_retval, 0, NULL, 0, NULL) == SUCCESS && !Z_ISUNDEF(cb_retval)) { + retval = zval_get_long(&cb_retval); + zval_ptr_dtor(&cb_retval); + } + + return retval; +} + +/* {{{ proto bool ZipArchive::registerCancelCallback(callable callback) +register a progression callback: int callback(double state); */ +static ZIPARCHIVE_METHOD(registerCancelCallback) +{ + struct zip *intern; + zval *self = getThis(); + zval *callback; + ze_zip_object *obj; + + if (!self) { + RETURN_FALSE; + } + + ZIP_FROM_OBJECT(intern, self); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callback) == FAILURE) { + return; + } + + /* callable? */ + if (!zend_is_callable(callback, 0, NULL)) { + zend_string *callback_name = zend_get_callable_name(callback); + php_error_docref(NULL, E_WARNING, "Invalid callback '%s'", ZSTR_VAL(callback_name)); + zend_string_release_ex(callback_name, 0); + RETURN_FALSE; + } + + obj = Z_ZIP_P(self); + + /* free if called twice */ + _php_zip_cancel_callback_free(obj); + + /* register */ + ZVAL_COPY(&obj->cancel_callback, callback); + if (zip_register_cancel_callback_with_state(intern, _php_zip_cancel_callback, _php_zip_cancel_callback_free, obj)) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ +#endif + /* {{{ ze_zip_object_class_functions */ static const zend_function_entry zip_class_functions[] = { ZIPARCHIVE_ME(open, arginfo_class_ZipArchive_open, ZEND_ACC_PUBLIC) @@ -2824,6 +2973,13 @@ static const zend_function_entry zip_class_functions[] = { ZIPARCHIVE_ME(setEncryptionName, arginfo_class_ZipArchive_setEncryptionName, ZEND_ACC_PUBLIC) ZIPARCHIVE_ME(setEncryptionIndex, arginfo_class_ZipArchive_setEncryptionIndex, ZEND_ACC_PUBLIC) #endif +#ifdef HAVE_PROGRESS_CALLBACK + ZIPARCHIVE_ME(registerProgressCallback, arginfo_class_ZipArchive_registerProgressCallback, ZEND_ACC_PUBLIC) +#endif +#ifdef HAVE_CANCEL_CALLBACK + ZIPARCHIVE_ME(registerCancelCallback, arginfo_class_ZipArchive_registerCancelCallback, ZEND_ACC_PUBLIC) +#endif + PHP_FE_END }; /* }}} */ diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 1eb100d45f..b6617e81fb 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -60,6 +60,12 @@ typedef struct _ze_zip_object { int filename_len; int buffers_cnt; zend_object zo; +#ifdef HAVE_PROGRESS_CALLBACK + zval progress_callback; +#endif +#ifdef HAVE_CANCEL_CALLBACK + zval cancel_callback; +#endif } ze_zip_object; static inline ze_zip_object *php_zip_fetch_object(zend_object *obj) { diff --git a/ext/zip/php_zip.stub.php b/ext/zip/php_zip.stub.php index c88d2742ee..3e49c46413 100644 --- a/ext/zip/php_zip.stub.php +++ b/ext/zip/php_zip.stub.php @@ -90,11 +90,13 @@ class ZipArchive /** @return null|false */ public function setCommentName(string $name, string $comment) {} +#ifdef HAVE_SET_MTIME /** @return null|false */ public function setMtimeIndex(int $index, int $timestamp, int $flags = 0) {} /** @return null|false */ public function setMtimeName(string $name, int $timestamp, int $flags = 0) {} +#endif /** @return string|false */ public function getCommentIndex(int $index, int $flags = 0) {} @@ -171,4 +173,14 @@ class ZipArchive /** @return bool */ public function setEncryptionIndex(int $index, int $method, string $password = UNKNOWN) {} #endif + +#ifdef HAVE_PROGRESS_CALLBACK + /** @return bool */ + public function registerProgressCallback(float $rate, callable $callback) {} +#endif + +#ifdef HAVE_CANCEL_CALLBACK + /** @return bool */ + public function registerCancelCallback(callable $callback) {} +#endif } diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index 4839eb68b4..2f788d8c30 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -111,17 +111,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setCommentName, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, comment, IS_STRING, 0) ZEND_END_ARG_INFO() +#if defined(HAVE_SET_MTIME) ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setMtimeIndex, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, index, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() +#endif +#if defined(HAVE_SET_MTIME) ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_setMtimeName, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() +#endif ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_getCommentIndex, 0, 0, 1) 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) ZEND_ARG_TYPE_INFO(0, password, IS_STRING, 0) ZEND_END_ARG_INFO() #endif + +#if defined(HAVE_PROGRESS_CALLBACK) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_registerProgressCallback, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, rate, IS_DOUBLE, 0) + ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() +#endif + +#if defined(HAVE_CANCEL_CALLBACK) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZipArchive_registerCancelCallback, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) +ZEND_END_ARG_INFO() +#endif diff --git a/ext/zip/tests/oo_cancel.phpt b/ext/zip/tests/oo_cancel.phpt new file mode 100644 index 0000000000..c8653ec52e --- /dev/null +++ b/ext/zip/tests/oo_cancel.phpt @@ -0,0 +1,39 @@ +--TEST-- +registerCancelCallback +--SKIPIF-- + +--INI-- +date.timezone=UTC +--FILE-- +open($file, ZIPARCHIVE::CREATE)) { + exit('failed'); +} + +var_dump($zip->registerCancelCallback(function () { + // Always cancel + return -1; +})); +var_dump($zip->addFromString(PHP_BINARY, 'entry #1')); + +var_dump($zip->close()); +@unlink($file); +?> +Done +--EXPECTF-- +bool(true) +bool(true) + +Warning: ZipArchive::close(): Operation cancelled in %s +bool(false) +Done diff --git a/ext/zip/tests/oo_progress.phpt b/ext/zip/tests/oo_progress.phpt new file mode 100644 index 0000000000..480510d862 --- /dev/null +++ b/ext/zip/tests/oo_progress.phpt @@ -0,0 +1,40 @@ +--TEST-- +registerProgressCallback +--SKIPIF-- + +--INI-- +date.timezone=UTC +--FILE-- +open($file, ZIPARCHIVE::CREATE)) { + exit('failed'); +} + +var_dump($zip->registerProgressCallback(0.5, function ($r) { + // Only check start/end as intermediate is not reliable + if ($r == 0.0) echo "start\n"; + if ($r == 1.0) echo "end\n"; +})); +var_dump($zip->addFromString('foo', 'entry #1')); + +var_dump($zip->close()); +unlink($file); +?> +Done +--EXPECTF-- +bool(true) +bool(true) +start +end +bool(true) +Done -- 2.40.0