]> granicus.if.org Git - php/commitdiff
add ZipArchive::registerProgressCallback and ZipArchive::registerCancelCallback methods
authorRemi Collet <remi@remirepo.net>
Thu, 30 Jan 2020 10:14:53 +0000 (11:14 +0100)
committerRemi Collet <remi@php.net>
Fri, 31 Jan 2020 08:06:42 +0000 (09:06 +0100)
ext/zip/config.m4
ext/zip/php_zip.c
ext/zip/php_zip.h
ext/zip/php_zip.stub.php
ext/zip/php_zip_arginfo.h
ext/zip/tests/oo_cancel.phpt [new file with mode: 0644]
ext/zip/tests/oo_progress.phpt [new file with mode: 0644]

index 943852e4e6102bc30eb77ed2650ff72b10c1e6fa..abd84ff51d6acbf9cce2a1c574bfcdf7aab43352 100644 (file)
@@ -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"
index 8f16f236c369cdc5e9fe9b36d77531098f28b9e7..74d992a5f608709fb44fffecaf31f8c0bac6c374 100644 (file)
@@ -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
 };
 /* }}} */
index 1eb100d45feb3f693c32ad4fdcd5d2da4006546c..b6617e81fb3f1eb895afcc0b10c686d6981ec494 100644 (file)
@@ -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) {
index c88d2742eeee8d4d6dac51d867f4b22ce09d9ca5..3e49c4641378581665a97c46e11099096520ab1f 100644 (file)
@@ -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
 }
index 4839eb68b4afed4682de3787b7017840b3254152..2f788d8c308de761b96838fcef8af7179ee9e2ec 100644 (file)
@@ -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 (file)
index 0000000..c8653ec
--- /dev/null
@@ -0,0 +1,39 @@
+--TEST--
+registerCancelCallback
+--SKIPIF--
+<?php
+/* $Id$ */
+if(!extension_loaded('zip')) die('skip');
+if (!method_exists('ZipArchive', 'registerCancelCallback')) die('skip libzip too old');
+?>
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+$dirname = dirname(__FILE__) . '/';
+$file = $dirname . '__tmp_oo_progress.zip';
+
+@unlink($file);
+
+$zip = new ZipArchive;
+if (!$zip->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 (file)
index 0000000..480510d
--- /dev/null
@@ -0,0 +1,40 @@
+--TEST--
+registerProgressCallback
+--SKIPIF--
+<?php
+/* $Id$ */
+if(!extension_loaded('zip')) die('skip');
+if (!method_exists('ZipArchive', 'registerProgressCallback')) die('skip libzip too old');
+?>
+--INI--
+date.timezone=UTC
+--FILE--
+<?php
+$dirname = dirname(__FILE__) . '/';
+$file = $dirname . '__tmp_oo_progress.zip';
+
+@unlink($file);
+
+$zip = new ZipArchive;
+if (!$zip->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