Skip to content

Commit 0f1e16a

Browse files
committed
Implement MultiCurl::setRetry()
1 parent 3c3741f commit 0f1e16a

File tree

5 files changed

+135
-12
lines changed

5 files changed

+135
-12
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ MultiCurl::setOpts($options)
281281
MultiCurl::setPort($port)
282282
MultiCurl::setReferer($referer)
283283
MultiCurl::setReferrer($referrer)
284+
MultiCurl::setRetry($maximum_number_of_retries = 0)
284285
MultiCurl::setTimeout($seconds)
285286
MultiCurl::setUrl($url)
286287
MultiCurl::setUserAgent($user_agent)

src/Curl/Curl.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ class Curl
4343

4444
public $attempts = 0;
4545
public $retries = 0;
46+
public $isChildOfMultiCurl = false;
47+
public $remainingRetries = 0;
4648
private $maximumNumberOfRetries = 0;
47-
private $remainingRetries = 0;
4849

4950
private $cookies = array();
5051
private $headers = array();
@@ -390,8 +391,14 @@ public function exec($ch = null)
390391
$this->errorMessage = $this->curlError ? $this->curlErrorMessage : $this->httpErrorMessage;
391392

392393
if ($this->error && $this->remainingRetries >= 1) {
393-
$this->remainingRetries -= 1;
394394
$this->retries += 1;
395+
396+
// Allow multicurl to update $remainingRetries and retry.
397+
if ($this->isChildOfMultiCurl) {
398+
return;
399+
}
400+
401+
$this->remainingRetries -= 1;
395402
return $this->exec($ch);
396403
}
397404

src/Curl/MultiCurl.php

Lines changed: 41 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 $maximumNumberOfRetries = 0;
22+
2123
private $cookies = array();
2224
private $headers = array();
2325
private $options = array();
@@ -587,6 +589,19 @@ public function setReferrer($referrer)
587589
$this->setOpt(CURLOPT_REFERER, $referrer);
588590
}
589591

592+
/**
593+
* Set Retry
594+
*
595+
* Number of retries to attempt. Maximum number of attempts is $maximum_number_of_retries + 1.
596+
*
597+
* @access public
598+
* @param $maximum_number_of_retries
599+
*/
600+
public function setRetry($maximum_number_of_retries = 0)
601+
{
602+
$this->maximumNumberOfRetries = $maximum_number_of_retries;
603+
}
604+
590605
/**
591606
* Set Timeout
592607
*
@@ -661,17 +676,31 @@ public function start()
661676
$ch->curlErrorCode = $info_array['result'];
662677
$ch->exec($ch->curl);
663678

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

676705
break;
677706
}
@@ -763,6 +792,7 @@ private function queueHandle($curl)
763792
{
764793
// Use sequential ids to allow for ordered post processing.
765794
$curl->id = $this->nextCurlId++;
795+
$curl->isChildOfMultiCurl = true;
766796
$this->curls[$curl->id] = $curl;
767797
}
768798

@@ -791,6 +821,7 @@ private function initHandle($curl)
791821

792822
$curl->setOpts($this->options);
793823
$curl->setHeaders($this->headers);
824+
$curl->setRetry($this->maximumNumberOfRetries);
794825

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

tests/PHPCurlClass/PHPCurlClassTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3293,6 +3293,13 @@ public function testRetry()
32933293
'expect_attempts' => 3,
32943294
'expect_retries' => 2,
32953295
),
3296+
array(
3297+
'maximum_number_of_retries' => 3,
3298+
'failures' => 3,
3299+
'expect_success' => true,
3300+
'expect_attempts' => 4,
3301+
'expect_retries' => 3,
3302+
),
32963303
);
32973304
foreach ($tests as $test) {
32983305
$maximum_number_of_retries = $test['maximum_number_of_retries'];

tests/PHPCurlClass/PHPMultiCurlClassTest.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,4 +2590,81 @@ public function testAscendingNumericalOrder()
25902590

25912591
$multi_curl->start();
25922592
}
2593+
2594+
public function testRetryMulti()
2595+
{
2596+
$tests = array(
2597+
array(
2598+
'maximum_number_of_retries' => null,
2599+
'failures' => 0,
2600+
'expect_success' => true,
2601+
'expect_attempts' => 1,
2602+
'expect_retries' => 0,
2603+
),
2604+
array(
2605+
'maximum_number_of_retries' => 0,
2606+
'failures' => 0,
2607+
'expect_success' => true,
2608+
'expect_attempts' => 1,
2609+
'expect_retries' => 0,
2610+
),
2611+
array(
2612+
'maximum_number_of_retries' => 0,
2613+
'failures' => 1,
2614+
'expect_success' => false,
2615+
'expect_attempts' => 1,
2616+
'expect_retries' => 0,
2617+
),
2618+
array(
2619+
'maximum_number_of_retries' => 1,
2620+
'failures' => 1,
2621+
'expect_success' => true,
2622+
'expect_attempts' => 2,
2623+
'expect_retries' => 1,
2624+
),
2625+
array(
2626+
'maximum_number_of_retries' => 1,
2627+
'failures' => 2,
2628+
'expect_success' => false,
2629+
'expect_attempts' => 2,
2630+
'expect_retries' => 1,
2631+
),
2632+
array(
2633+
'maximum_number_of_retries' => 2,
2634+
'failures' => 2,
2635+
'expect_success' => true,
2636+
'expect_attempts' => 3,
2637+
'expect_retries' => 2,
2638+
),
2639+
array(
2640+
'maximum_number_of_retries' => 3,
2641+
'failures' => 3,
2642+
'expect_success' => true,
2643+
'expect_attempts' => 4,
2644+
'expect_retries' => 3,
2645+
),
2646+
);
2647+
foreach ($tests as $test) {
2648+
$maximum_number_of_retries = $test['maximum_number_of_retries'];
2649+
$failures = $test['failures'];
2650+
$expect_success = $test['expect_success'];
2651+
$expect_attempts = $test['expect_attempts'];
2652+
$expect_retries = $test['expect_retries'];
2653+
2654+
$multi_curl = new MultiCurl();
2655+
$multi_curl->setOpt(CURLOPT_COOKIEJAR, '/dev/null');
2656+
$multi_curl->setHeader('X-DEBUG-TEST', 'retry');
2657+
2658+
if (!($maximum_number_of_retries === null)) {
2659+
$multi_curl->setRetry($maximum_number_of_retries);
2660+
}
2661+
2662+
$instance = $multi_curl->addGet(Test::TEST_URL, array('failures' => $failures));
2663+
$multi_curl->start();
2664+
2665+
$this->assertEquals($expect_success, !$instance->error);
2666+
$this->assertEquals($expect_attempts, $instance->attempts);
2667+
$this->assertEquals($expect_retries, $instance->retries);
2668+
}
2669+
}
25932670
}

0 commit comments

Comments
 (0)