Parallelise http transfers for trip planner tester
--- a/labs/tripPlannerTester.kml.php
+++ b/labs/tripPlannerTester.kml.php
@@ -8,6 +8,50 @@
}
else {
return (($pBegin - $pEnd) * (1 - ($pStep / $pMax))) + $pEnd;
+ }
+}
+require ("../lib/rolling-curl/RollingCurl.php");
+function processResult_cb($response, $info, $request)
+{
+ global $testRegions, $regionTimes,$csv,$kml, $latdeltasize,$londeltasize;
+ $md = $request->metadata;
+ $tripplan = json_decode($response);
+ $plans = Array();
+ //var_dump(Array($info, $request));
+ if (is_array($tripplan->plan->itineraries->itinerary)) {
+ foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) {
+ $plans[floor($itinerary->duration / 60000) ] = $itinerary;
+ }
+ }
+ else {
+ $plans[floor($tripplan->plan->itineraries->itinerary->duration / 60000) ] = $tripplan->plan->itineraries->itinerary;
+ }
+ if ($csv) echo "{$md['i']},{$md['j']}," . min(array_keys($plans)) . ",$latdeltasize, $londeltasize,{$md['key']}\n";
+ if ($kml) {
+ $time = min(array_keys($plans));
+ $plan = "";
+ if (is_array($plans[min(array_keys($plans)) ]->legs->leg)) {
+ foreach ($plans[min(array_keys($plans)) ]->legs->leg as $legNumber => $leg) {
+ $plan.= processLeg($legNumber, $leg) . ",";
+ }
+ }
+ else {
+ $plan.= processLeg(0, $plans[min(array_keys($plans)) ]->legs->leg);
+ }
+ if (isset($tripplan->error) && $tripplan->error->id == 404) {
+ $time = 999;
+ $plan = "Trip not possible without excessive walking from nearest bus stop";
+ }
+ $testRegions[] = Array(
+ "lat" => $md['i'],
+ "lon" => $md['j'],
+ "time" => $time,
+ "latdeltasize" => $latdeltasize,
+ "londeltasize" => $londeltasize,
+ "regionname" => $md['key'],
+ "plan" => $plan . "<br/><a href='" . htmlspecialchars($url) . "'>original plan</a>"
+ );
+ $regionTimes[] = $time;
}
}
function Gradient($HexFrom, $HexTo, $ColorSteps)
@@ -49,18 +93,17 @@
//}
//$walkingstep.= floor($step->distance) . "m";
//return $walkingstep;
+
}
}
$csv = false;
$kml = true;
if ($kml) {
- //header('Content-Type: application/vnd.google-earth.kml+xml');
+ header('Content-Type: application/vnd.google-earth.kml+xml');
echo '<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"><Document>';
}
include ('../include/common.inc.php');
-//Test code to grab transit times
-// make sure to sleep(10);
$boundingBoxes = Array(
"belconnen" => Array(
"startlat" => - 35.1928,
@@ -105,72 +148,29 @@
$useragent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1";
if ($csv) echo "<pre>";
if ($csv) echo "lat,lon,time,latdeltasize, londeltasize, region key name\n";
+$rc = new RollingCurl("processResult_cb");
+$rc->window_size = 3;
foreach ($boundingBoxes as $key => $boundingBox) {
for ($i = $boundingBox['startlat']; $i >= $boundingBox['finishlat']; $i-= $latdeltasize) {
for ($j = $boundingBox['startlon']; $j <= $boundingBox['finishlon']; $j+= $londeltasize) {
$url = $otpAPIurl . "ws/plan?date=" . urlencode($startDate) . "&time=" . urlencode($startTime) . "&mode=TRANSIT%2CWALK&optimize=QUICK&maxWalkDistance=440&wheelchair=false&toPlace=" . $i . "," . $j . "&fromPlace=$fromPlace";
- $ch = curl_init($url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_HTTPHEADER, array(
+ $request = new RollingCurlRequest($url);
+ $request->headers = Array(
"Accept: application/json"
- ));
- curl_setopt($ch, CURLOPT_TIMEOUT, 5);
- $page = curl_exec($ch);
- if (curl_errno($ch)) {
- if ($csv) echo "Trip planner temporarily unavailable: " . curl_errno($ch) . " " . curl_error($ch);
- }
- else {
- $tripplan = json_decode($page);
- $plans = Array();
- if (is_array($tripplan->plan->itineraries->itinerary)) {
- foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) {
- $plans[floor($itinerary->duration / 60000) ] = $itinerary;
- }
- }
- else {
- $plans[floor($tripplan->plan->itineraries->itinerary->duration / 60000) ] = $tripplan->plan->itineraries->itinerary;
- }
- if ($csv) echo "$i,$j," . min(array_keys($plans)) . ",$latdeltasize, $londeltasize,$key\n";
- if ($kml) {
- $time = min(array_keys($plans));
- $plan = "";
- if (is_array($plans[min(array_keys($plans)) ]->legs->leg)) {
- foreach ($plans[min(array_keys($plans)) ]->legs->leg as $legNumber => $leg) {
- $plan .= processLeg($legNumber, $leg).",";
- }
- }
- else {
- $plan .= processLeg(0, $plans[min(array_keys($plans)) ]->legs->leg);
- }
- if (isset($tripplan->error) && $tripplan->error->id == 404) {
- $time = 999;
- $plan = "Trip not possible without excessive walking from nearest bus stop";
- }
- $testRegions[] = Array(
- "lat" => $i,
- "lon" => $j,
- "time" => $time,
- "latdeltasize" => $latdeltasize,
- "londeltasize" => $londeltasize,
- "regionname" => $key,
- "plan" => $plan . "<br/><a href='". htmlspecialchars($url)."'>original plan</a>"
- );
- $regionTimes[] = $time;
- }
- }
- flush(); @ob_flush();
- curl_close($ch);
- }
- }
-}
+ );
+ $request->metadata = Array( "i" => $i, "j" => $j, "key" => $key);
+ $rc->add($request);
+ }
+ }
+}
+$rc->execute();
if ($kml) {
$colorSteps = 9;
//$minTime = min($regionTimes);
//$maxTime = max($regionTimes);
//$rangeTime = $maxTime - $minTime;
//$deltaTime = $rangeTime / $colorSteps;
- $Gradients = Gradient(strrev("66FF00"), strrev("FF0000"), $colorSteps); // KML is BGR not RGB so strrev
+ $Gradients = Gradient(strrev("66FF00") , strrev("FF0000") , $colorSteps); // KML is BGR not RGB so strrev
foreach ($testRegions as $testRegion) {
//$band = (floor(($testRegion[time] - $minTime) / $deltaTime));
$band = (floor($testRegion[time] / 10));
--- /dev/null
+++ b/lib/rolling-curl/.svn/all-wcprops
@@ -1,1 +1,42 @@
+K 25
+svn:wc:ra_dav:version-url
+V 22
+/svn/!svn/ver/20/trunk
+END
+RollingCurlGroup.php
+K 25
+svn:wc:ra_dav:version-url
+V 43
+/svn/!svn/ver/20/trunk/RollingCurlGroup.php
+END
+example_groups.php
+K 25
+svn:wc:ra_dav:version-url
+V 41
+/svn/!svn/ver/20/trunk/example_groups.php
+END
+example.php
+K 25
+svn:wc:ra_dav:version-url
+V 34
+/svn/!svn/ver/20/trunk/example.php
+END
+RollingCurl.php
+K 25
+svn:wc:ra_dav:version-url
+V 38
+/svn/!svn/ver/20/trunk/RollingCurl.php
+END
+CHANGELOG.txt
+K 25
+svn:wc:ra_dav:version-url
+V 36
+/svn/!svn/ver/20/trunk/CHANGELOG.txt
+END
+README.txt
+K 25
+svn:wc:ra_dav:version-url
+V 33
+/svn/!svn/ver/20/trunk/README.txt
+END
--- /dev/null
+++ b/lib/rolling-curl/.svn/entries
@@ -1,1 +1,233 @@
-
+10
+
+dir
+20
+http://rolling-curl.googlecode.com/svn/trunk
+http://rolling-curl.googlecode.com/svn
+
+
+
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+74aa2acc-2e27-11de-b2a4-4f96ceaaac44
+
+RollingCurlGroup.php
+file
+
+
+
+
+2011-04-10T08:32:48.081650Z
+73c08d9e9e24b4adc89816624c7aca30
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+5152
+
+example_groups.php
+file
+
+
+
+
+2011-04-10T08:32:48.082650Z
+907ed82a47d346c39acbd5578e1d0230
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1367
+
+example.php
+file
+
+
+
+
+2011-04-10T08:32:48.083650Z
+87aa845abfaffc09ed4eca024f2a8b8a
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1860
+
+RollingCurl.php
+file
+
+
+
+
+2011-04-10T08:32:48.084650Z
+205391c449f3f3ee050004dadc374dc8
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+10444
+
+CHANGELOG.txt
+file
+
+
+
+
+2011-04-10T08:32:48.085650Z
+d0452f6f9530ed04580159121d0fd5f7
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+662
+
+README.txt
+file
+
+
+
+
+2011-04-10T08:32:48.085650Z
+60dd357081431c0f2b82989cdbce8615
+2010-09-12T20:39:22.711474Z
+20
+alexander.makarow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+6355
+
+
--- /dev/null
+++ b/lib/rolling-curl/.svn/prop-base/CHANGELOG.txt.svn-base
@@ -1,1 +1,6 @@
+K 13
+svn:eol-style
+V 6
+native
+END
--- /dev/null
+++ b/lib/rolling-curl/.svn/prop-base/RollingCurlGroup.php.svn-base
@@ -1,1 +1,6 @@
+K 13
+svn:eol-style
+V 6
+native
+END
--- /dev/null
+++ b/lib/rolling-curl/.svn/prop-base/example_groups.php.svn-base
@@ -1,1 +1,6 @@
+K 13
+svn:eol-style
+V 6
+native
+END
--- /dev/null
+++ b/lib/rolling-curl/.svn/text-base/CHANGELOG.txt.svn-base
@@ -1,1 +1,15 @@
+Rolling Curl changelog
+======================
+September 13, 2010
+------------------
+- Bug #12, #14: Fixed default options overriding (LionsAd)
+- Bug #10: Added use of curl_multi_select to avoid burning CPU (LionsAd)
+- Enh #6, #9: Added $request as parameter to callback function (LionsAd)
+- Chg: Request renamed to RollingCurlRequest (LionsAd)
+- Added RollingCurlGroup class that allows processing groups of requests (LionsAd)
+- More cleanup at unsetting a class (LionsAd)
+- Timeout parameter for curl_multi_select is now configurable (LionsAd)
+- single_curl now returns true (LionsAd)
+- Readme corrections (Alexander Makarov)
+- Code cleanup (Alexander Makarov)
--- /dev/null
+++ b/lib/rolling-curl/.svn/text-base/README.txt.svn-base
@@ -1,1 +1,210 @@
-
+Rolling Curl
+============
+
+RollingCurl allows you to process multiple HTTP requests in parallel using CURL PHP library.
+
+Released under the Apache License 2.0.
+
+Authors
+-------
+- Was originally written by [Josh Fraser](joshfraser.com).
+- Currently maintained by [Alexander Makarov](http://rmcreative.ru/).
+- Received significant updates and patched from [LionsAd](http://github.com/LionsAd/rolling-curl).
+
+Overview
+--------
+RollingCurl is a more efficient implementation of curl_multi() curl_multi is a great way to process multiple HTTP requests in parallel in PHP.
+curl_multi is particularly handy when working with large data sets (like fetching thousands of RSS feeds at one time). Unfortunately there is
+very little documentation on the best way to implement curl_multi. As a result, most of the examples around the web are either inefficient or
+fail entirely when asked to handle more than a few hundred requests.
+
+The problem is that most implementations of curl_multi wait for each set of requests to complete before processing them. If there are too many requests
+to process at once, they usually get broken into groups that are then processed one at a time. The problem with this is that each group has to wait for
+the slowest request to download. In a group of 100 requests, all it takes is one slow one to delay the processing of 99 others. The larger the number of
+requests you are dealing with, the more noticeable this latency becomes.
+
+The solution is to process each request as soon as it completes. This eliminates the wasted CPU cycles from busy waiting. Also there is a queue of
+cURL requests to allow for maximum throughput. Each time a request is completed, a new one is added from the queue. By dynamically adding and removing
+links, we keep a constant number of links downloading at all times. This gives us a way to throttle the amount of simultaneous requests we are sending.
+The result is a faster and more efficient way of processing large quantities of cURL requests in parallel.
+
+Callbacks
+---------
+
+Each of requests usually do have a callback to process results that is being executed when request is done
+(both successfully or not).
+
+Callback accepts three parameters and can look like the following one:
+~~~
+[php]
+function request_callback($response, $info, $request){
+ // doing something with the data received
+}
+~~~
+
+- $response contains received page body.
+- $info is an associative array that holds various information about response such as HTTP response code, content type,
+time taken to make request etc.
+- $request contains RollingCurlRequest that was used to make request.
+
+Examples
+--------
+### Hello world
+
+~~~
+[php]
+// an array of URL's to fetch
+$urls = array("http://www.google.com",
+ "http://www.facebook.com",
+ "http://www.yahoo.com");
+
+// a function that will process the returned responses
+function request_callback($response, $info, $request) {
+ // parse the page title out of the returned HTML
+ if (preg_match("~<title>(.*?)</title>~i", $response, $out)) {
+ $title = $out[1];
+ }
+ echo "<b>$title</b><br />";
+ print_r($info);
+ echo "<hr>";
+}
+
+// create a new RollingCurl object and pass it the name of your custom callback function
+$rc = new RollingCurl("request_callback");
+// the window size determines how many simultaneous requests to allow.
+$rc->window_size = 20;
+foreach ($urls as $url) {
+ // add each request to the RollingCurl object
+ $request = new RollingCurlRequest($url);
+ $rc->add($request);
+}
+$rc->execute();
+~~~
+
+
+### Setting custom options
+
+Set custom options for EVERY request:
+
+~~~
+[php]
+$rc = new RollingCurl("request_callback");
+$rc->options = array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true);
+$rc->execute();
+~~~
+
+Set custom options for A SINGLE request:
+
+~~~
+[php]
+$rc = new RollingCurl("request_callback");
+$request = new RollingCurlRequest($url);
+$request->options = array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true);
+$rc->add($request);
+$rc->execute();
+~~~
+
+### Shortcuts
+
+~~~
+[php]
+$rc = new RollingCurl("request_callback");
+$rc->get("http://www.google.com");
+$rc->get("http://www.yahoo.com");
+$rc->execute();
+~~~
+
+### Class callbacks
+
+~~~
+[php]
+class MyInfoCollector {
+ private $rc;
+
+ function __construct(){
+ $this->rc = new RollingCurl(array($this, 'processPage'));
+ }
+
+ function processPage($response, $info, $request){
+ //...
+ }
+
+ function run($urls){
+ foreach ($urls as $url){
+ $request = new RollingCurlRequest($url);
+ $this->rc->add($request);
+ }
+ $this->rc->execute();
+ }
+}
+
+$collector = new MyInfoCollector();
+$collector->run(array(
+ 'http://google.com/',
+ 'http://yahoo.com/'
+));
+~~~
+
+### Using RollingCurlGroup
+
+~~~
+[php]
+class TestCurlRequest extends RollingCurlGroupRequest {
+ public $test_verbose = true;
+
+ function process($output, $info) {
+ echo "Processing " . $this->url . "\n";
+ if ($this->test_verbose)
+ print_r($info);
+
+ parent::process($output, $info);
+ }
+}
+
+class TestCurlGroup extends RollingCurlGroup {
+ function process($output, $info, $request) {
+ echo "Group CB: Progress " . $this->name . " (" . ($this->finished_requests + 1) . "/" . $this->num_requests . ")\n";
+ parent::process($output, $info, $request);
+ }
+
+ function finished() {
+ echo "Group CB: Finished" . $this->name . "\n";
+ parent::finished();
+ }
+}
+
+$group = new TestCurlGroup("High");
+$group->add(new TestCurlRequest("www.google.de"));
+$group->add(new TestCurlRequest("www.yahoo.de"));
+$group->add(new TestCurlRequest("www.newyorktimes.com"));
+$reqs[] = $group;
+
+$group = new TestCurlGroup("Normal");
+$group->add(new TestCurlRequest("twitter.com"));
+$group->add(new TestCurlRequest("www.bing.com"));
+$group->add(new TestCurlRequest("m.facebook.com"));
+$reqs[] = $group;
+
+$reqs[] = new TestCurlRequest("www.kernel.org");
+
+// No callback here, as its done in Request class
+$rc = new GroupRollingCurl();
+
+foreach ($reqs as $req)
+$rc->add($req);
+
+$rc->execute();
+~~~
+
+The same function (add) can be used both for adding requests and groups of requests.
+The "callback" in request and groups is:
+
+process($output, $info)
+
+and
+
+process($output, $info, $request)
+
+Also you can override RollingCurlGroup::finished() that will be executed right after finishing group processing.
+
+$Id$
--- /dev/null
+++ b/lib/rolling-curl/.svn/text-base/RollingCurl.php.svn-base
@@ -1,1 +1,375 @@
-
+<?php
+/*
+Authored by Josh Fraser (www.joshfraser.com)
+Released under Apache License 2.0
+
+Maintained by Alexander Makarov, http://rmcreative.ru/
+
+$Id$
+*/
+
+/**
+ * Class that represent a single curl request
+ */
+class RollingCurlRequest {
+ public $url = false;
+ public $method = 'GET';
+ public $post_data = null;
+ public $headers = null;
+ public $options = null;
+
+ /**
+ * @param string $url
+ * @param string $method
+ * @param $post_data
+ * @param $headers
+ * @param $options
+ * @return void
+ */
+ function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
+ $this->url = $url;
+ $this->method = $method;
+ $this->post_data = $post_data;
+ $this->headers = $headers;
+ $this->options = $options;
+ }
+
+ /**
+ * @return void
+ */
+ public function __destruct() {
+ unset($this->url, $this->method, $this->post_data, $this->headers, $this->options);
+ }
+}
+
+/**
+ * RollingCurl custom exception
+ */
+class RollingCurlException extends Exception {
+}
+
+/**
+ * Class that holds a rolling queue of curl requests.
+ *
+ * @throws RollingCurlException
+ */
+class RollingCurl {
+ /**
+ * @var int
+ *
+ * Window size is the max number of simultaneous connections allowed.
+ *
+ * REMEMBER TO RESPECT THE SERVERS:
+ * Sending too many requests at one time can easily be perceived
+ * as a DOS attack. Increase this window_size if you are making requests
+ * to multiple servers or have permission from the receving server admins.
+ */
+ private $window_size = 5;
+
+ /**
+ * @var float
+ *
+ * Timeout is the timeout used for curl_multi_select.
+ */
+ private $timeout = 10;
+
+ /**
+ * @var string|array
+ *
+ * Callback function to be applied to each result.
+ */
+ private $callback;
+
+ /**
+ * @var array
+ *
+ * Set your base options that you want to be used with EVERY request.
+ */
+ protected $options = array(
+ CURLOPT_SSL_VERIFYPEER => 0,
+ CURLOPT_RETURNTRANSFER => 1,
+ CURLOPT_CONNECTTIMEOUT => 30,
+ CURLOPT_TIMEOUT => 30
+ );
+
+ /**
+ * @var array
+ */
+ private $headers = array();
+
+ /**
+ * @var Request[]
+ *
+ * The request queue
+ */
+ private $requests = array();
+
+ /**
+ * @var RequestMap[]
+ *
+ * Maps handles to request indexes
+ */
+ private $requestMap = array();
+
+ /**
+ * @param $callback
+ * Callback function to be applied to each result.
+ *
+ * Can be specified as 'my_callback_function'
+ * or array($object, 'my_callback_method').
+ *
+ * Function should take three parameters: $response, $info, $request.
+ * $response is response body, $info is additional curl info.
+ * $request is the original request
+ *
+ * @return void
+ */
+ function __construct($callback = null) {
+ $this->callback = $callback;
+ }
+
+ /**
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name) {
+ return (isset($this->{$name})) ? $this->{$name} : null;
+ }
+
+ /**
+ * @param string $name
+ * @param mixed $value
+ * @return bool
+ */
+ public function __set($name, $value) {
+ // append the base options & headers
+ if ($name == "options" || $name == "headers") {
+ $this->{$name} = $value + $this->{$name};
+ } else {
+ $this->{$name} = $value;
+ }
+ return true;
+ }
+
+ /**
+ * Add a request to the request queue
+ *
+ * @param Request $request
+ * @return bool
+ */
+ public function add($request) {
+ $this->requests[] = $request;
+ return true;
+ }
+
+ /**
+ * Create new Request and add it to the request queue
+ *
+ * @param string $url
+ * @param string $method
+ * @param $post_data
+ * @param $headers
+ * @param $options
+ * @return bool
+ */
+ public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
+ $this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
+ return true;
+ }
+
+ /**
+ * Perform GET request
+ *
+ * @param string $url
+ * @param $headers
+ * @param $options
+ * @return bool
+ */
+ public function get($url, $headers = null, $options = null) {
+ return $this->request($url, "GET", null, $headers, $options);
+ }
+
+ /**
+ * Perform POST request
+ *
+ * @param string $url
+ * @param $post_data
+ * @param $headers
+ * @param $options
+ * @return bool
+ */
+ public function post($url, $post_data = null, $headers = null, $options = null) {
+ return $this->request($url, "POST", $post_data, $headers, $options);
+ }
+
+ /**
+ * Execute processing
+ *
+ * @param int $window_size Max number of simultaneous connections
+ * @return string|bool
+ */
+ public function execute($window_size = null) {
+ // rolling curl window must always be greater than 1
+ if (sizeof($this->requests) == 1) {
+ return $this->single_curl();
+ } else {
+ // start the rolling curl. window_size is the max number of simultaneous connections
+ return $this->rolling_curl($window_size);
+ }
+ }
+
+ /**
+ * Performs a single curl request
+ *
+ * @access private
+ * @return string
+ */
+ private function single_curl() {
+ $ch = curl_init();
+ $request = array_shift($this->requests);
+ $options = $this->get_options($request);
+ curl_setopt_array($ch, $options);
+ $output = curl_exec($ch);
+ $info = curl_getinfo($ch);
+
+ // it's not neccesary to set a callback for one-off requests
+ if ($this->callback) {
+ $callback = $this->callback;
+ if (is_callable($this->callback)) {
+ call_user_func($callback, $output, $info, $request);
+ }
+ }
+ else
+ return $output;
+ return true;
+ }
+
+ /**
+ * Performs multiple curl requests
+ *
+ * @access private
+ * @throws RollingCurlException
+ * @param int $window_size Max number of simultaneous connections
+ * @return bool
+ */
+ private function rolling_curl($window_size = null) {
+ if ($window_size)
+ $this->window_size = $window_size;
+
+ // make sure the rolling window isn't greater than the # of urls
+ if (sizeof($this->requests) < $this->window_size)
+ $this->window_size = sizeof($this->requests);
+
+ if ($this->window_size < 2) {
+ throw new RollingCurlException("Window size must be greater than 1");
+ }
+
+ $master = curl_multi_init();
+
+ // start the first batch of requests
+ for ($i = 0; $i < $this->window_size; $i++) {
+ $ch = curl_init();
+
+ $options = $this->get_options($this->requests[$i]);
+
+ curl_setopt_array($ch, $options);
+ curl_multi_add_handle($master, $ch);
+
+ // Add to our request Maps
+ $key = (string) $ch;
+ $this->requestMap[$key] = $i;
+ }
+
+ do {
+ while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
+ if ($execrun != CURLM_OK)
+ break;
+ // a request was just completed -- find out which one
+ while ($done = curl_multi_info_read($master)) {
+
+ // get the info and content returned on the request
+ $info = curl_getinfo($done['handle']);
+ $output = curl_multi_getcontent($done['handle']);
+
+ // send the return values to the callback function.
+ $callback = $this->callback;
+ if (is_callable($callback)) {