php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #41712 [PATCH] Add CURL Progress Function Callback
Submitted: 2007-06-16 12:16 UTC Modified: 2009-05-03 15:18 UTC
Votes:16
Avg. Score:4.9 ± 0.3
Reproduced:15 of 15 (100.0%)
Same Version:7 (46.7%)
Same OS:3 (20.0%)
From: sdteffen at gmail dot com Assigned: pajoye (profile)
Status: Closed Package: Feature/Change Request
PHP Version: 5CVS-2007-06-16 (CVS) OS: SuSE Linux 10.2
Private report: No CVE-ID: None
 [2007-06-16 12:16 UTC] sdteffen at gmail dot com
Description:
------------
The current PHP version does not allow to use the CURL progress
function callback (CURL_PROGRESSFUNCTION).

Reproduce code:
---------------
<?php
$ch = curl_init("http://www.php.net");
$fp = fopen("index.html", "w");

function curl_progress_callback($a=0,$b=0,$c=0, $d=0) {
	echo "curl_progress_callback($a,$b,$c,$d)\n";
}

curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'curl_progress_callback');
curl_exec($ch);
echo curl_close($ch);
fclose($fp);
?>

Expected result:
----------------
curl_progress_callback(0,1054,0,0)
curl_progress_callback(0,35131,0,0)


Actual result:
--------------
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 35131    0 35131    0     0  32344      0 --:--:--  0:00:01 --:--:-- 52105

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2007-06-16 12:20 UTC] sdteffen at gmail dot com
Patch to implement CURL_PROGRESSFUNCTION (works against today's 5.2 CVS version):

diff -u php-5.2.3/ext/curl/interface.c php-5.2.3.patched/ext/curl/interface.c
--- php-5.2.3/ext/curl/interface.c	2007-05-22 10:39:20.000000000 +0200
+++ php-5.2.3.patched/ext/curl/interface.c	2007-06-16 13:30:05.000000000 +0200
@@ -368,6 +368,7 @@
 	REGISTER_CURL_CONSTANT(CURLOPT_HEADER);
 	REGISTER_CURL_CONSTANT(CURLOPT_HTTPHEADER);
 	REGISTER_CURL_CONSTANT(CURLOPT_NOPROGRESS);
+	REGISTER_CURL_CONSTANT(CURLOPT_PROGRESSFUNCTION);
 	REGISTER_CURL_CONSTANT(CURLOPT_NOBODY);
 	REGISTER_CURL_CONSTANT(CURLOPT_FAILONERROR);
 	REGISTER_CURL_CONSTANT(CURLOPT_UPLOAD);
@@ -777,6 +778,80 @@
 }
 /* }}} */
 
+/* {{{ curl_progress
+ */
+static size_t curl_progress(void *clientp,
+                        double dltotal,
+                        double dlnow,
+                        double ultotal,
+                        double ulnow)
+{
+	php_curl       *ch = (php_curl *) clientp;
+	php_curl_progress  *t  = ch->handlers->progress;
+	int             length = -1;
+
+#if PHP_CURL_DEBUG
+	fprintf(stderr, "curl_progress() called\n");
+	fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
+#endif
+
+	switch (t->method) {
+		case PHP_CURL_USER: {
+			zval **argv[4];
+			zval  *zdltotal = NULL;
+			zval  *zdlnow = NULL;
+			zval  *zultotal = NULL;
+			zval  *zulnow = NULL;
+			zval  *retval_ptr;
+			int   error;
+			zend_fcall_info fci;
+			TSRMLS_FETCH_FROM_CTX(ch->thread_ctx);
+
+			MAKE_STD_ZVAL(zdltotal);
+			MAKE_STD_ZVAL(zdlnow);
+			MAKE_STD_ZVAL(zultotal);
+			MAKE_STD_ZVAL(zulnow);
+			
+			ZVAL_LONG(zdltotal, dltotal);
+			ZVAL_LONG(zdlnow, dlnow);
+			ZVAL_LONG(zultotal, ultotal);
+			ZVAL_LONG(zulnow, ulnow);
+
+			argv[0] = &zdltotal;
+			argv[1] = &zdlnow;
+			argv[2] = &zultotal;
+			argv[3] = &zulnow;
+
+			fci.size = sizeof(fci);
+			fci.function_table = EG(function_table);
+			fci.function_name = t->func_name;
+			fci.object_pp = NULL;
+			fci.retval_ptr_ptr = &retval_ptr;
+			fci.param_count = 3;
+			fci.params = argv;
+			fci.no_separation = 0;
+			fci.symbol_table = NULL;
+
+			ch->in_callback = 1;
+			error = zend_call_function(&fci, &t->fci_cache TSRMLS_CC);
+			ch->in_callback = 0;
+			if (error == FAILURE) {
+				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot call the CURLOPT_READFUNCTION"); 
+				length = -1;
+			} 
+
+			zval_ptr_dtor(argv[0]);
+			zval_ptr_dtor(argv[1]);
+			zval_ptr_dtor(argv[2]);
+			zval_ptr_dtor(argv[3]);
+			break;
+		}
+	}
+	return 0;
+}
+/* }}} */
+
+
 /* {{{ curl_read
  */
 static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
@@ -1065,6 +1140,7 @@
 	(*ch)->handlers->write        = ecalloc(1, sizeof(php_curl_write));
 	(*ch)->handlers->write_header = ecalloc(1, sizeof(php_curl_write));
 	(*ch)->handlers->read         = ecalloc(1, sizeof(php_curl_read));
+	(*ch)->handlers->progress     = ecalloc(1, sizeof(php_curl_progress));
 
 	(*ch)->in_callback = 0;
 	(*ch)->header.str_len = 0;
@@ -1418,6 +1494,17 @@
 			ch->handlers->read->func_name = *zvalue;
 			ch->handlers->read->method = PHP_CURL_USER;
 			break;
+		case CURLOPT_PROGRESSFUNCTION:
+			curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION,	curl_progress);
+			curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);
+			if (ch->handlers->progress->func_name) {
+				zval_ptr_dtor(&ch->handlers->progress->func_name);
+				ch->handlers->progress->fci_cache = empty_fcall_info_cache;
+			}
+			zval_add_ref(zvalue);
+			ch->handlers->progress->func_name = *zvalue;
+			ch->handlers->progress->method = PHP_CURL_USER;
+			break;
 		case CURLOPT_HEADERFUNCTION:
 			if (ch->handlers->write_header->func_name) {
 				zval_ptr_dtor(&ch->handlers->write_header->func_name);
@@ -1948,6 +2035,9 @@
 	if (ch->handlers->write_header->func_name) {
 		zval_ptr_dtor(&ch->handlers->write_header->func_name);
 	}
+	if(ch->handlers->progress->func_name) {
+		zval_ptr_dtor(&ch->handlers->progress->func_name);
+	}
 	if (ch->handlers->passwd) {
 		zval_ptr_dtor(&ch->handlers->passwd);
 	}
diff -u php-5.2.3/ext/curl/php_curl.h php-5.2.3.patched/ext/curl/php_curl.h
--- php-5.2.3/ext/curl/php_curl.h	2007-01-01 10:35:48.000000000 +0100
+++ php-5.2.3.patched/ext/curl/php_curl.h	2007-06-16 13:31:07.000000000 +0200
@@ -97,10 +97,17 @@
 } php_curl_read;
 
 typedef struct {
+	zval 		*func_name;
+	zend_fcall_info_cache fci_cache;
+	int    	        method;
+} php_curl_progress;
+
+typedef struct {
 	php_curl_write *write;
 	php_curl_write *write_header;
 	php_curl_read  *read;
 	zval           *passwd;
+	php_curl_progress *progress;
 } php_curl_handlers;
 
 struct _php_curl_error  {
 [2007-06-25 18:08 UTC] tony2001@php.net
Assigned to the maintainer.
 [2007-09-24 21:26 UTC] sdteffen at gmail dot com
The progress function only returned 3 values instead of 4. The following revised patch fixes this problem (it completely replaces the previous patch):

diff -u php-5.2.3/ext/curl/interface.c php-5.2.3.patched/ext/curl/interface.c
--- php-5.2.3/ext/curl/interface.c	2007-05-22 10:39:20.000000000 +0200
+++ php-5.2.3.patched/ext/curl/interface.c	2007-06-16 13:30:05.000000000 +0200
@@ -368,6 +368,7 @@
 	REGISTER_CURL_CONSTANT(CURLOPT_HEADER);
 	REGISTER_CURL_CONSTANT(CURLOPT_HTTPHEADER);
 	REGISTER_CURL_CONSTANT(CURLOPT_NOPROGRESS);
+	REGISTER_CURL_CONSTANT(CURLOPT_PROGRESSFUNCTION);
 	REGISTER_CURL_CONSTANT(CURLOPT_NOBODY);
 	REGISTER_CURL_CONSTANT(CURLOPT_FAILONERROR);
 	REGISTER_CURL_CONSTANT(CURLOPT_UPLOAD);
@@ -777,6 +778,80 @@
 }
 /* }}} */
 
+/* {{{ curl_progress
+ */
+static size_t curl_progress(void *clientp,
+                        double dltotal,
+                        double dlnow,
+                        double ultotal,
+                        double ulnow)
+{
+	php_curl       *ch = (php_curl *) clientp;
+	php_curl_progress  *t  = ch->handlers->progress;
+	int             length = -1;
+
+#if PHP_CURL_DEBUG
+	fprintf(stderr, "curl_progress() called\n");
+	fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
+#endif
+
+	switch (t->method) {
+		case PHP_CURL_USER: {
+			zval **argv[4];
+			zval  *zdltotal = NULL;
+			zval  *zdlnow = NULL;
+			zval  *zultotal = NULL;
+			zval  *zulnow = NULL;
+			zval  *retval_ptr;
+			int   error;
+			zend_fcall_info fci;
+			TSRMLS_FETCH_FROM_CTX(ch->thread_ctx);
+
+			MAKE_STD_ZVAL(zdltotal);
+			MAKE_STD_ZVAL(zdlnow);
+			MAKE_STD_ZVAL(zultotal);
+			MAKE_STD_ZVAL(zulnow);
+			
+			ZVAL_LONG(zdltotal, dltotal);
+			ZVAL_LONG(zdlnow, dlnow);
+			ZVAL_LONG(zultotal, ultotal);
+			ZVAL_LONG(zulnow, ulnow);
+
+			argv[0] = &zdltotal;
+			argv[1] = &zdlnow;
+			argv[2] = &zultotal;
+			argv[3] = &zulnow;
+
+			fci.size = sizeof(fci);
+			fci.function_table = EG(function_table);
+			fci.function_name = t->func_name;
+			fci.object_pp = NULL;
+			fci.retval_ptr_ptr = &retval_ptr;
+			fci.param_count = 4;
+			fci.params = argv;
+			fci.no_separation = 0;
+			fci.symbol_table = NULL;
+
+			ch->in_callback = 1;
+			error = zend_call_function(&fci, &t->fci_cache TSRMLS_CC);
+			ch->in_callback = 0;
+			if (error == FAILURE) {
+				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot call the CURLOPT_READFUNCTION"); 
+				length = -1;
+			} 
+
+			zval_ptr_dtor(argv[0]);
+			zval_ptr_dtor(argv[1]);
+			zval_ptr_dtor(argv[2]);
+			zval_ptr_dtor(argv[3]);
+			break;
+		}
+	}
+	return 0;
+}
+/* }}} */
+
+
 /* {{{ curl_read
  */
 static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
@@ -1065,6 +1140,7 @@
 	(*ch)->handlers->write        = ecalloc(1, sizeof(php_curl_write));
 	(*ch)->handlers->write_header = ecalloc(1, sizeof(php_curl_write));
 	(*ch)->handlers->read         = ecalloc(1, sizeof(php_curl_read));
+	(*ch)->handlers->progress     = ecalloc(1, sizeof(php_curl_progress));
 
 	(*ch)->in_callback = 0;
 	(*ch)->header.str_len = 0;
@@ -1418,6 +1494,17 @@
 			ch->handlers->read->func_name = *zvalue;
 			ch->handlers->read->method = PHP_CURL_USER;
 			break;
+		case CURLOPT_PROGRESSFUNCTION:
+			curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION,	curl_progress);
+			curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);
+			if (ch->handlers->progress->func_name) {
+				zval_ptr_dtor(&ch->handlers->progress->func_name);
+				ch->handlers->progress->fci_cache = empty_fcall_info_cache;
+			}
+			zval_add_ref(zvalue);
+			ch->handlers->progress->func_name = *zvalue;
+			ch->handlers->progress->method = PHP_CURL_USER;
+			break;
 		case CURLOPT_HEADERFUNCTION:
 			if (ch->handlers->write_header->func_name) {
 				zval_ptr_dtor(&ch->handlers->write_header->func_name);
@@ -1948,6 +2035,9 @@
 	if (ch->handlers->write_header->func_name) {
 		zval_ptr_dtor(&ch->handlers->write_header->func_name);
 	}
+	if(ch->handlers->progress->func_name) {
+		zval_ptr_dtor(&ch->handlers->progress->func_name);
+	}
 	if (ch->handlers->passwd) {
 		zval_ptr_dtor(&ch->handlers->passwd);
 	}
diff -u php-5.2.3/ext/curl/php_curl.h php-5.2.3.patched/ext/curl/php_curl.h
--- php-5.2.3/ext/curl/php_curl.h	2007-01-01 10:35:48.000000000 +0100
+++ php-5.2.3.patched/ext/curl/php_curl.h	2007-06-16 13:31:07.000000000 +0200
@@ -97,10 +97,17 @@
 } php_curl_read;
 
 typedef struct {
+	zval 		*func_name;
+	zend_fcall_info_cache fci_cache;
+	int    	        method;
+} php_curl_progress;
+
+typedef struct {
 	php_curl_write *write;
 	php_curl_write *write_header;
 	php_curl_read  *read;
 	zval           *passwd;
+	php_curl_progress *progress;
 } php_curl_handlers;
 
 struct _php_curl_error  {
 [2007-09-25 09:22 UTC] top dot quack at freenet dot de
Thanks to Steffen, the patch is now working properly and displays the upload progress as expected:
curl_progress_callback Download: 0 / 0 bytes, Upload: 636 / 1179391 bytes 
curl_progress_callback Download: 0 / 0 bytes, Upload: 17020 / 1179391 bytes 
curl_progress_callback Download: 0 / 0 bytes, Upload: 33404 / 1179391 bytes

Please include that bugfix as soon as possible into the new php versions.
 [2008-02-05 15:56 UTC] renatobraga at gmail dot com
Hi,
this patch isn't working for return values
as you can see:

"Returning a  non-zero nonzero value  from  this callback will cause libcurl to abort the transfer and return CURLE_ABORTED_BY_CALLBACK."

Nothing happens if i return a nonzero value.

Can you fix it?

Regards,
Renato
 [2008-02-06 18:14 UTC] sdteffen at gmail dot com
I've updated the patch to pass the return value from the callback function as Renato suggested:

--- php-5.2.5.orig/ext/curl/interface.c	2007-10-13 13:35:35.000000000 +0200
+++ php-5.2.5.patch/ext/curl/interface.c	2008-02-06 18:43:41.000000000 +0100
@@ -368,6 +368,7 @@
 	REGISTER_CURL_CONSTANT(CURLOPT_HEADER);
 	REGISTER_CURL_CONSTANT(CURLOPT_HTTPHEADER);
 	REGISTER_CURL_CONSTANT(CURLOPT_NOPROGRESS);
+	REGISTER_CURL_CONSTANT(CURLOPT_PROGRESSFUNCTION);
 	REGISTER_CURL_CONSTANT(CURLOPT_NOBODY);
 	REGISTER_CURL_CONSTANT(CURLOPT_FAILONERROR);
 	REGISTER_CURL_CONSTANT(CURLOPT_UPLOAD);
@@ -780,6 +781,87 @@
 }
 /* }}} */
 
+/* {{{ curl_progress
+ */
+static size_t curl_progress(void *clientp,
+                        double dltotal,
+                        double dlnow,
+                        double ultotal,
+                        double ulnow)
+{
+	php_curl       *ch = (php_curl *) clientp;
+	php_curl_progress  *t  = ch->handlers->progress;
+	int             length = -1;
+	size_t	rval = 0;
+
+#if PHP_CURL_DEBUG
+	fprintf(stderr, "curl_progress() called\n");
+	fprintf(stderr, "clientp = %x, dltotal = %f, dlnow = %f, ultotal = %f, ulnow = %f\n", clientp, dltotal, dlnow, ultotal, ulnow);
+#endif
+
+	switch (t->method) {
+		case PHP_CURL_USER: {
+			zval **argv[4];
+			zval  *zdltotal = NULL;
+			zval  *zdlnow = NULL;
+			zval  *zultotal = NULL;
+			zval  *zulnow = NULL;
+			zval  *retval_ptr;
+			int   error;
+			zend_fcall_info fci;
+			TSRMLS_FETCH_FROM_CTX(ch->thread_ctx);
+
+			MAKE_STD_ZVAL(zdltotal);
+			MAKE_STD_ZVAL(zdlnow);
+			MAKE_STD_ZVAL(zultotal);
+			MAKE_STD_ZVAL(zulnow);
+			
+			ZVAL_LONG(zdltotal, dltotal);
+			ZVAL_LONG(zdlnow, dlnow);
+			ZVAL_LONG(zultotal, ultotal);
+			ZVAL_LONG(zulnow, ulnow);
+
+			argv[0] = &zdltotal;
+			argv[1] = &zdlnow;
+			argv[2] = &zultotal;
+			argv[3] = &zulnow;
+
+			fci.size = sizeof(fci);
+			fci.function_table = EG(function_table);
+			fci.function_name = t->func_name;
+			fci.object_pp = NULL;
+			fci.retval_ptr_ptr = &retval_ptr;
+			fci.param_count = 4;
+			fci.params = argv;
+			fci.no_separation = 0;
+			fci.symbol_table = NULL;
+
+			ch->in_callback = 1;
+			error = zend_call_function(&fci, &t->fci_cache TSRMLS_CC);
+			ch->in_callback = 0;
+			if (error == FAILURE) {
+				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot call the CURLOPT_READFUNCTION"); 
+				length = -1;
+			} else if (retval_ptr) {
+				if (Z_TYPE_P(retval_ptr) != IS_LONG) {
+					convert_to_long_ex(&retval_ptr);
+				}
+				if(0 != Z_LVAL_P(retval_ptr))
+					rval = 1;
+				zval_ptr_dtor(&retval_ptr);
+			}
+			zval_ptr_dtor(argv[0]);
+			zval_ptr_dtor(argv[1]);
+			zval_ptr_dtor(argv[2]);
+			zval_ptr_dtor(argv[3]);
+			break;
+		}
+	}
+	return rval;
+}
+/* }}} */
+
+
 /* {{{ curl_read
  */
 static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
@@ -1068,6 +1150,7 @@
 	(*ch)->handlers->write        = ecalloc(1, sizeof(php_curl_write));
 	(*ch)->handlers->write_header = ecalloc(1, sizeof(php_curl_write));
 	(*ch)->handlers->read         = ecalloc(1, sizeof(php_curl_read));
+	(*ch)->handlers->progress     = ecalloc(1, sizeof(php_curl_progress));
 
 	(*ch)->in_callback = 0;
 	(*ch)->header.str_len = 0;
@@ -1422,6 +1505,17 @@
 			ch->handlers->read->func_name = *zvalue;
 			ch->handlers->read->method = PHP_CURL_USER;
 			break;
+		case CURLOPT_PROGRESSFUNCTION:
+			curl_easy_setopt(ch->cp, CURLOPT_PROGRESSFUNCTION,	curl_progress);
+			curl_easy_setopt(ch->cp, CURLOPT_PROGRESSDATA, ch);
+			if (ch->handlers->progress->func_name) {
+				zval_ptr_dtor(&ch->handlers->progress->func_name);
+				ch->handlers->progress->fci_cache = empty_fcall_info_cache;
+			}
+			zval_add_ref(zvalue);
+			ch->handlers->progress->func_name = *zvalue;
+			ch->handlers->progress->method = PHP_CURL_USER;
+			break;
 		case CURLOPT_HEADERFUNCTION:
 			if (ch->handlers->write_header->func_name) {
 				zval_ptr_dtor(&ch->handlers->write_header->func_name);
@@ -1953,6 +2047,9 @@
 	if (ch->handlers->write_header->func_name) {
 		zval_ptr_dtor(&ch->handlers->write_header->func_name);
 	}
+	if(ch->handlers->progress->func_name) {
+		zval_ptr_dtor(&ch->handlers->progress->func_name);
+	}
 	if (ch->handlers->passwd) {
 		zval_ptr_dtor(&ch->handlers->passwd);
 	}
 [2008-02-06 21:35 UTC] renatobraga at gmail dot com
Thanks Steffen,
Worked like a charm
 [2008-02-16 15:00 UTC] sdteffen at gmail dot com
The last patch is missing the changes to php_curl.h from the previous patch. A file containing the complete patch with changes for both files can be found under this URL:

http://sdteffen.de/php/php_curl_progress_callback.patch
 [2009-02-20 09:27 UTC] php41712 at brainpower dot no-ip dot org
can somewone submit this to the php code as this will help a lot of users
 [2009-04-28 15:09 UTC] frase at cs dot wisc dot edu
What happened here?  A patch was offered for this feature almost two years ago, and was assigned to the maintainer a week later.  The next six months saw some tweaks and corrections, and yet here we are two years later with this still languishing in "assigned" limbo, neither accepted and merged nor rejected and closed.

Ilia?  Tony?  Bueller?
 [2009-04-28 18:10 UTC] pajoye@php.net
Did you test your with 5.3? If not can you post a URL to an updated patch with a test script please? It can still make it this week for 5.3+.
 [2009-04-28 20:05 UTC] sdteffen at gmail dot com
Thanks for responding to this issue.

I've updated and tested the patch once more with the PHP_5_3 sources from CVS today:

http://sdteffen.de/php/php_curl_progress_callback.patch

The test script is the same as mentioned under "Reproduce code", but

I've added an explanatory comment to the version on my website:

http://sdteffen.de/php/index_en.html
 [2009-04-28 20:23 UTC] pajoye@php.net
Thanks for the update.

The PHP part looks and the curl as well (thx to cURL's Daniel for the quick review). I will test and apply it this week.
 [2009-05-03 15:18 UTC] pajoye@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.

Applied with a leak fix to 5.3 and 6.0
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Oct 05 22:01:26 2024 UTC