Skip to content

Commit 63a7a30

Browse files
authored
Merge pull request php-curl-class#468 from zachborboa/master
Implement Curl::setRetry()
2 parents 20d718e + b0b0c5f commit 63a7a30

File tree

10 files changed

+524
-13
lines changed

10 files changed

+524
-13
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ More examples are available under [/examples](https://github.com/php-curl-class/
194194
Curl::__construct($base_url = null)
195195
Curl::__destruct()
196196
Curl::__get($name)
197+
Curl::attemptRetry()
197198
Curl::beforeSend($callback)
198199
Curl::buildPostData($data)
199200
Curl::call()
@@ -203,6 +204,7 @@ Curl::delete($url, $query_parameters = array(), $data = array())
203204
Curl::download($url, $mixed_filename)
204205
Curl::error($callback)
205206
Curl::exec($ch = null)
207+
Curl::execDone()
206208
Curl::get($url, $data = array())
207209
Curl::getCookie($key)
208210
Curl::getInfo($opt = null)
@@ -238,6 +240,7 @@ Curl::setOpts($options)
238240
Curl::setPort($port)
239241
Curl::setReferer($referer)
240242
Curl::setReferrer($referrer)
243+
Curl::setRetry($mixed)
241244
Curl::setTimeout($seconds)
242245
Curl::setUrl($url, $mixed_data = '')
243246
Curl::setUserAgent($user_agent)
@@ -280,6 +283,7 @@ MultiCurl::setOpts($options)
280283
MultiCurl::setPort($port)
281284
MultiCurl::setReferer($referer)
282285
MultiCurl::setReferrer($referrer)
286+
MultiCurl::setRetry($mixed)
283287
MultiCurl::setTimeout($seconds)
284288
MultiCurl::setUrl($url)
285289
MultiCurl::setUserAgent($user_agent)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
require __DIR__ . '/../vendor/autoload.php';
3+
4+
use \Curl\Curl;
5+
6+
$curl = new Curl();
7+
$curl->setRetry(function ($instance) {
8+
return $instance->retries < 3;
9+
});
10+
$curl->get('https://httpbin.org/status/503');
11+
12+
if ($curl->error) {
13+
echo 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n";
14+
echo 'attempts: ' . $curl->attempts . "\n";
15+
echo 'retries: ' . $curl->retries . "\n";
16+
} else {
17+
echo 'Response:' . "\n";
18+
var_dump($curl->response);
19+
}

examples/get_with_retry.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
require __DIR__ . '/../vendor/autoload.php';
3+
4+
use \Curl\Curl;
5+
6+
$max_retries = 3;
7+
8+
$curl = new Curl();
9+
$curl->setRetry($max_retries);
10+
$curl->get('https://httpbin.org/status/503');
11+
12+
if ($curl->error) {
13+
echo 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n";
14+
echo 'attempts: ' . $curl->attempts . "\n";
15+
echo 'retries: ' . $curl->retries . "\n";
16+
} else {
17+
echo 'Response:' . "\n";
18+
var_dump($curl->response);
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
require __DIR__ . '/../vendor/autoload.php';
3+
4+
use \Curl\MultiCurl;
5+
6+
$multi_curl = new MultiCurl();
7+
$multi_curl->setRetry(function ($instance) {
8+
return $instance->retries < 3;
9+
});
10+
$multi_curl->complete(function ($instance) {
11+
echo 'call to "' . $instance->url . '" completed.' . "\n";
12+
echo 'attempts: ' . $instance->attempts . "\n";
13+
echo 'retries: ' . $instance->retries . "\n";
14+
});
15+
16+
$multi_curl->addGet('https://httpbin.org/status/503?a');
17+
$multi_curl->addGet('https://httpbin.org/status/503?b');
18+
$multi_curl->addGet('https://httpbin.org/status/503?c');
19+
20+
$multi_curl->start();
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
require __DIR__ . '/../vendor/autoload.php';
3+
4+
use \Curl\MultiCurl;
5+
6+
$max_retries = 3;
7+
8+
$multi_curl = new MultiCurl();
9+
$multi_curl->setRetry($max_retries);
10+
$multi_curl->complete(function ($instance) {
11+
echo 'call to "' . $instance->url . '" completed.' . "\n";
12+
echo 'attempts: ' . $instance->attempts . "\n";
13+
echo 'retries: ' . $instance->retries . "\n";
14+
});
15+
16+
$multi_curl->addGet('https://httpbin.org/status/503?a');
17+
$multi_curl->addGet('https://httpbin.org/status/503?b');
18+
$multi_curl->addGet('https://httpbin.org/status/503?c');
19+
20+
$multi_curl->start();

src/Curl/Curl.php

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ class Curl
4141
public $completeFunction = null;
4242
public $fileHandle = null;
4343

44+
public $attempts = 0;
45+
public $retries = 0;
46+
public $isChildOfMultiCurl = false;
47+
public $remainingRetries = 0;
48+
public $retryDecider = null;
49+
4450
private $cookies = array();
4551
private $headers = array();
4652
private $options = array();
@@ -335,6 +341,8 @@ public function error($callback)
335341
*/
336342
public function exec($ch = null)
337343
{
344+
$this->attempts += 1;
345+
338346
if ($ch === null) {
339347
$this->responseCookies = array();
340348
$this->call($this->beforeSendFunction);
@@ -382,6 +390,22 @@ public function exec($ch = null)
382390
}
383391
$this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage;
384392

393+
// Allow multicurl to attempt retry as needed.
394+
if ($this->isChildOfMultiCurl) {
395+
return;
396+
}
397+
398+
if ($this->attemptRetry()) {
399+
return $this->exec($ch);
400+
}
401+
402+
$this->execDone();
403+
404+
return $this->response;
405+
}
406+
407+
public function execDone()
408+
{
385409
if ($this->error) {
386410
$this->call($this->errorFunction);
387411
} else {
@@ -394,8 +418,6 @@ public function exec($ch = null)
394418
if (!($this->fileHandle === null)) {
395419
$this->downloadComplete($this->fileHandle);
396420
}
397-
398-
return $this->response;
399421
}
400422

401423
/**
@@ -1010,6 +1032,25 @@ public function setReferrer($referrer)
10101032
$this->setOpt(CURLOPT_REFERER, $referrer);
10111033
}
10121034

1035+
/**
1036+
* Set Retry
1037+
*
1038+
* Number of retries to attempt or decider callable. Maximum number of
1039+
* attempts is $maximum_number_of_retries + 1.
1040+
*
1041+
* @access public
1042+
* @param $mixed
1043+
*/
1044+
public function setRetry($mixed)
1045+
{
1046+
if (is_callable($mixed)) {
1047+
$this->retryDecider = $mixed;
1048+
} elseif (is_int($mixed)) {
1049+
$maximum_number_of_retries = $mixed;
1050+
$this->remainingRetries = $maximum_number_of_retries;
1051+
}
1052+
}
1053+
10131054
/**
10141055
* Set Timeout
10151056
*
@@ -1046,6 +1087,31 @@ public function setUserAgent($user_agent)
10461087
$this->setOpt(CURLOPT_USERAGENT, $user_agent);
10471088
}
10481089

1090+
/**
1091+
* Attempt Retry
1092+
*
1093+
* @access public
1094+
*/
1095+
public function attemptRetry()
1096+
{
1097+
$attempt_retry = false;
1098+
if ($this->error) {
1099+
if ($this->retryDecider === null) {
1100+
$attempt_retry = $this->remainingRetries >= 1;
1101+
} else {
1102+
$func = $this->retryDecider;
1103+
$attempt_retry = $func($this);
1104+
}
1105+
if ($attempt_retry) {
1106+
$this->retries += 1;
1107+
if ($this->remainingRetries) {
1108+
$this->remainingRetries -= 1;
1109+
}
1110+
}
1111+
}
1112+
return $attempt_retry;
1113+
}
1114+
10491115
/**
10501116
* Success
10511117
*

src/Curl/MultiCurl.php

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class MultiCurl
1818
private $errorFunction = null;
1919
private $completeFunction = null;
2020

21+
private $retry = null;
22+
2123
private $cookies = array();
2224
private $headers = array();
2325
private $options = array();
@@ -587,6 +589,20 @@ public function setReferrer($referrer)
587589
$this->setOpt(CURLOPT_REFERER, $referrer);
588590
}
589591

592+
/**
593+
* Set Retry
594+
*
595+
* Number of retries to attempt or decider callable. Maximum number of
596+
* attempts is $maximum_number_of_retries + 1.
597+
*
598+
* @access public
599+
* @param $mixed
600+
*/
601+
public function setRetry($mixed)
602+
{
603+
$this->retry = $mixed;
604+
}
605+
590606
/**
591607
* Set Timeout
592608
*
@@ -661,17 +677,31 @@ public function start()
661677
$ch->curlErrorCode = $info_array['result'];
662678
$ch->exec($ch->curl);
663679

664-
// Remove completed instance from active curls.
665-
unset($this->activeCurls[$key]);
666-
667-
// Start a new request before removing the handle of the completed one.
668-
if (count($this->curls) >= 1) {
669-
$this->initHandle(array_shift($this->curls));
680+
if ($ch->attemptRetry()) {
681+
// Remove completed handle before adding again in order to retry request.
682+
curl_multi_remove_handle($this->multiCurl, $ch->curl);
683+
684+
$curlm_error_code = curl_multi_add_handle($this->multiCurl, $ch->curl);
685+
if (!($curlm_error_code === CURLM_OK)) {
686+
throw new \ErrorException(
687+
'cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code)
688+
);
689+
}
690+
} else {
691+
$ch->execDone();
692+
693+
// Remove completed instance from active curls.
694+
unset($this->activeCurls[$key]);
695+
696+
// Start a new request before removing the handle of the completed one.
697+
if (count($this->curls) >= 1) {
698+
$this->initHandle(array_shift($this->curls));
699+
}
700+
curl_multi_remove_handle($this->multiCurl, $ch->curl);
701+
702+
// Clean up completed instance.
703+
$ch->close();
670704
}
671-
curl_multi_remove_handle($this->multiCurl, $ch->curl);
672-
673-
// Clean up completed instance.
674-
$ch->close();
675705

676706
break;
677707
}
@@ -763,6 +793,7 @@ private function queueHandle($curl)
763793
{
764794
// Use sequential ids to allow for ordered post processing.
765795
$curl->id = $this->nextCurlId++;
796+
$curl->isChildOfMultiCurl = true;
766797
$this->curls[$curl->id] = $curl;
767798
}
768799

@@ -791,6 +822,7 @@ private function initHandle($curl)
791822

792823
$curl->setOpts($this->options);
793824
$curl->setHeaders($this->headers);
825+
$curl->setRetry($this->retry);
794826

795827
foreach ($this->cookies as $key => $value) {
796828
$curl->setCookie($key, $value);

0 commit comments

Comments
 (0)