Skip to content

Commit 0857023

Browse files
committed
Fix php-curl-class#572,php-curl-class#582: Add MultiCurl::setProxies() to make multicurl requests with multiple proxies
1 parent 494d8a7 commit 0857023

File tree

5 files changed

+144
-0
lines changed

5 files changed

+144
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ MultiCurl::setJsonDecoder($mixed)
320320
MultiCurl::setOpt($option, $value)
321321
MultiCurl::setOpts($options)
322322
MultiCurl::setPort($port)
323+
MultiCurl::setProxies($proxies)
323324
MultiCurl::setProxy($proxy, $port = null, $username = null, $password = null)
324325
MultiCurl::setProxyAuth($auth)
325326
MultiCurl::setProxyTunnel($tunnel = true)

examples/multi_curl_proxies.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
require __DIR__ . '/../vendor/autoload.php';
3+
4+
use \Curl\MultiCurl;
5+
6+
$multi_curl = new MultiCurl();
7+
$multi_curl->setProxies(array(
8+
'someproxy.com:9999',
9+
'someproxy.com:80',
10+
'someproxy.com:443',
11+
'someproxy.com:1080',
12+
'someproxy.com:3128',
13+
'someproxy.com:8080',
14+
));
15+
$multi_curl->addGet('https://httpbin.org/ip');
16+
$multi_curl->addGet('https://httpbin.org/ip');
17+
$multi_curl->addGet('https://httpbin.org/ip');
18+
$multi_curl->complete(function ($instance) {
19+
echo
20+
'curl id ' . $instance->id . ' used proxy ' .
21+
$instance->getOpt(CURLOPT_PROXY) . ' and ' .
22+
'ip is ' . $instance->response->origin . "\n";
23+
});
24+
$multi_curl->start();

src/Curl/ArrayUtil.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,17 @@ public static function array_flatten_multidim($array, $prefix = false)
7777
}
7878
return $return;
7979
}
80+
81+
/**
82+
* Array Random
83+
*
84+
* @access public
85+
* @param $array
86+
*
87+
* @return mixed
88+
*/
89+
public static function array_random($array)
90+
{
91+
return $array[mt_rand(0, count($array) - 1)];
92+
}
8093
}

src/Curl/MultiCurl.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Curl;
44

5+
use Curl\ArrayUtil;
6+
57
class MultiCurl
68
{
79
public $baseUrl = null;
@@ -23,6 +25,7 @@ class MultiCurl
2325
private $cookies = array();
2426
private $headers = array();
2527
private $options = array();
28+
private $proxies = null;
2629

2730
private $jsonDecoder = null;
2831
private $xmlDecoder = null;
@@ -571,6 +574,21 @@ public function setProxy($proxy, $port = null, $username = null, $password = nul
571574
}
572575
}
573576

577+
/**
578+
* Set Proxies
579+
*
580+
* Set proxies to tunnel requests through. When set, a random proxy will be
581+
* used for the request.
582+
*
583+
* @access public
584+
* @param $proxies array - A list of HTTP proxies to tunnel requests
585+
* through. May include port number.
586+
*/
587+
public function setProxies($proxies)
588+
{
589+
$this->proxies = $proxies;
590+
}
591+
574592
/**
575593
* Set Proxy Auth
576594
*
@@ -931,6 +949,13 @@ private function initHandle($curl)
931949
$curl->setRetry($this->retry);
932950
$curl->setCookies($this->cookies);
933951

952+
// Use a random proxy for the curl instance when proxies have been set
953+
// and the curl instance doesn't already have a proxy set.
954+
if (is_array($this->proxies) && $curl->getOpt(CURLOPT_PROXY) === null) {
955+
$random_proxy = ArrayUtil::array_random($this->proxies);
956+
$curl->setProxy($random_proxy);
957+
}
958+
934959
$curlm_error_code = curl_multi_add_handle($this->multiCurl, $curl->curl);
935960
if (!($curlm_error_code === CURLM_OK)) {
936961
throw new \ErrorException('cURL multi add handle error: ' . curl_multi_strerror($curlm_error_code));

tests/PHPCurlClass/PHPMultiCurlClassTest.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2919,4 +2919,85 @@ public function testSetProxyTunnel()
29192919
$multi_curl->setProxyTunnel($tunnel);
29202920
$this->assertEquals($tunnel, $multi_curl->getOpt(CURLOPT_HTTPPROXYTUNNEL));
29212921
}
2922+
2923+
public function testSetProxiesRandomProxy()
2924+
{
2925+
$proxies = array(
2926+
'example.com:80',
2927+
'example.com:443',
2928+
'example.com:1080',
2929+
'example.com:3128',
2930+
'example.com:8080',
2931+
);
2932+
2933+
$multi_curl = new MultiCurl();
2934+
$multi_curl->setProxies($proxies);
2935+
$multi_curl->addGet(Test::TEST_URL);
2936+
$multi_curl->addGet(Test::TEST_URL);
2937+
$multi_curl->addGet(Test::TEST_URL);
2938+
2939+
// Make MultiCurl::curls accessible and MultiCurl::initHandle()
2940+
// callable.
2941+
$reflector = new \ReflectionClass('\Curl\MultiCurl');
2942+
$property = $reflector->getProperty('curls');
2943+
$property->setAccessible(true);
2944+
$multi_curl_curls = $property->getValue($multi_curl);
2945+
$multi_curl_initHandle = $reflector->getMethod('initHandle');
2946+
$multi_curl_initHandle->setAccessible(true);
2947+
2948+
// Ensure we have the requests queued.
2949+
$this->assertCount(3, $multi_curl_curls);
2950+
2951+
// Invoke MultiCurl::initHandle() so that proxies are set.
2952+
foreach ($multi_curl_curls as $curl) {
2953+
$multi_curl_initHandle->invoke($multi_curl, $curl);
2954+
}
2955+
2956+
// Ensure each request is set to one of the proxies.
2957+
foreach ($multi_curl_curls as $curl) {
2958+
$this->assertContains($curl->getOpt(CURLOPT_PROXY), $proxies);
2959+
}
2960+
}
2961+
2962+
public function testSetProxiesAlreadySet()
2963+
{
2964+
$proxies = array(
2965+
'example.com:80',
2966+
'example.com:443',
2967+
'example.com:1080',
2968+
'example.com:3128',
2969+
'example.com:8080',
2970+
);
2971+
2972+
$multi_curl = new MultiCurl();
2973+
$multi_curl->setProxies($proxies);
2974+
$get_1 = $multi_curl->addGet(Test::TEST_URL);
2975+
$get_2 = $multi_curl->addGet(Test::TEST_URL);
2976+
$get_2->setProxy('example.com:9999');
2977+
$get_3 = $multi_curl->addGet(Test::TEST_URL);
2978+
2979+
// Make MultiCurl::curls accessible and MultiCurl::initHandle()
2980+
// callable.
2981+
$reflector = new \ReflectionClass('\Curl\MultiCurl');
2982+
$property = $reflector->getProperty('curls');
2983+
$property->setAccessible(true);
2984+
$multi_curl_curls = $property->getValue($multi_curl);
2985+
$multi_curl_initHandle = $reflector->getMethod('initHandle');
2986+
$multi_curl_initHandle->setAccessible(true);
2987+
2988+
// Ensure we have the requests queued.
2989+
$this->assertCount(3, $multi_curl_curls);
2990+
2991+
// Invoke MultiCurl::initHandle() so that proxies are set.
2992+
foreach ($multi_curl_curls as $curl) {
2993+
$multi_curl_initHandle->invoke($multi_curl, $curl);
2994+
}
2995+
2996+
// Ensure requests are set to one of the random proxies.
2997+
$this->assertContains($get_1->getOpt(CURLOPT_PROXY), $proxies);
2998+
$this->assertContains($get_3->getOpt(CURLOPT_PROXY), $proxies);
2999+
3000+
// Ensure request with specific proxy is not set to one of the random proxies.
3001+
$this->assertNotContains($get_2->getOpt(CURLOPT_PROXY), $proxies);
3002+
}
29223003
}

0 commit comments

Comments
 (0)