diff --git a/.gitignore b/.gitignore index 5657f6e..4637cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -vendor \ No newline at end of file +vendor +.project \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 2774819..3d59880 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: php php: - - 5.4 - - 5.5 - 5.6 - - hhvm + - 7.0 + - 7.1 + - 7.2 before_script: - composer self-update diff --git a/README.markdown b/README.markdown index 36a6375..5967717 100644 --- a/README.markdown +++ b/README.markdown @@ -39,9 +39,9 @@ Basic usage can be done using the `addUrl($url/*, $options*/)` method. This call $mc = JMathai\PhpMultiCurl\MultiCurl::getInstance(); // Make a call to a URL. - $call1 = $mc->addUrl('http://slowapi.herokuapp.com/delay/2.0'); + $call1 = $mc->addUrl('http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.com'); // Make another call to a URL. - $call2 = $mc->addUrl('http://slowapi.herokuapp.com/delay/1.0'); + $call2 = $mc->addUrl('http://slowwly.robertomurray.co.uk/delay/1000/url/http://www.google.com'); // Access the response for $call2. // This blocks until $call2 is complete without waiting for $call1 @@ -59,9 +59,9 @@ This is what the output of that code will look like. ``` Call 2: consequatur id est Call 1: in maiores et -(http://slowapi.herokuapp.com/delay/2.0 :: code=200, start=1447701285.5536, end=1447701287.9512, total=2.397534) +(http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.com :: code=200, start=1447701285.5536, end=1447701287.9512, total=2.397534) [====================================================================================================] -(http://slowapi.herokuapp.com/delay/1.0 :: code=200, start=1447701285.5539, end=1447701287.0871, total=1.532997) +(http://slowwly.robertomurray.co.uk/delay/1000/url/http://www.google.com :: code=200, start=1447701285.5539, end=1447701287.0871, total=1.532997) [================================================================ ] ``` @@ -85,7 +85,7 @@ You'll most likely want to configure your cURL calls for your specific purpose. $code = $call->code; ``` -You can look at the [tests/example.php](https://github.com/jmathai/php-multi-curl/blob/master/src/example.php) file for working code and execute it from the command line. +You can look at the [example.php](https://github.com/jmathai/php-multi-curl/blob/master/example.php) file for working code and execute it from the command line. ## Documentation @@ -152,8 +152,9 @@ echo $mc->getSequence()->renderAscii(); ``` ## Authors - * jmathai + * jmathai ### Contributors - * Lewis Cowles (LewisCowles1986) - Usability for adding url's without needing to worry about CURL, but provisioning also for specifying additional parameters - * Sam Thomson (samthomson) - Packaged it up + * Lewis Cowles ([LewisCowles1986](https://github.com/LewisCowles1986)) - Usability for adding url's without needing to worry about CURL, but provisioning also for specifying additional parameters + * Sam Thomson ([Samthomson](https://github.com/samthomson)) - Packaged it up + * Sławek Kaleta ([Dusta](https://github.com/dusta)) - Updated it up diff --git a/src/example.php b/example.php old mode 100755 new mode 100644 similarity index 92% rename from src/example.php rename to example.php index 8c8dd63..2982ea9 --- a/src/example.php +++ b/example.php @@ -1,9 +1,10 @@ #!/usr/bin/env php + */ +class Manager +{ + private $_key; + private $_epiCurl; + + public function __construct($key) + { + $this->_key = $key; + $this->_epiCurl = MultiCurl::getInstance(); + } + + public function __get($name) + { + $responses = $this->_epiCurl->getResult($this->_key); + return isset($responses[$name]) ? $responses[$name] : null; + } + + public function __isset($name) + { + $val = self::__get($name); + return empty($val); + } +} diff --git a/src/MultiCurl.php b/src/MultiCurl.php index f6ce91d..1cf4e05 100644 --- a/src/MultiCurl.php +++ b/src/MultiCurl.php @@ -1,10 +1,10 @@ -mc = curl_multi_init(); - $this->properties = array( - 'code' => CURLINFO_HTTP_CODE, - 'time' => CURLINFO_TOTAL_TIME, - 'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD, - 'type' => CURLINFO_CONTENT_TYPE, - 'url' => CURLINFO_EFFECTIVE_URL - ); - } + $this->_mc = curl_multi_init(); + $this->_properties = array( + 'code' => CURLINFO_HTTP_CODE, + 'time' => CURLINFO_TOTAL_TIME, + 'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD, + 'type' => CURLINFO_CONTENT_TYPE, + 'url' => CURLINFO_EFFECTIVE_URL + ); + } - public function reset(){ - $this->requests = array(); - $this->responses = array(); - self::$timers = array(); - } - - public function addUrl($url, $options = array()) - { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - foreach($options as $option=>$value) + public function reset() { - curl_setopt($ch, $option, $value); + $this->_requests = array(); + $this->_responses = array(); + self::$_timers = array(); } - return $this->addCurl($ch); - } - public function addCurl($ch) - { - if(gettype($ch) !== 'resource') + public function addUrl($url, $options = array()) { - throw new MultiCurlInvalidParameterException('Parameter must be a valid curl handle'); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + foreach ($options as $option=>$value) { + curl_setopt($ch, $option, $value); + } + return $this->addCurl($ch); } - $key = $this->getKey($ch); - $this->requests[$key] = $ch; - curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'headerCallback')); + public function addCurl($ch) + { + if (gettype($ch) !== 'resource') { + throw new MultiInvalidParameterException('Parameter must be a valid curl handle'); + } - $code = curl_multi_add_handle($this->mc, $ch); - $this->startTimer($key); + $key = $this->_getKey($ch); + $this->_requests[$key] = $ch; + curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_headerCallback')); + + $code = curl_multi_add_handle($this->_mc, $ch); + $this->_startTimer($key); - // (1) - if($code === CURLM_OK || $code === CURLM_CALL_MULTI_PERFORM) + // (1) + if ($code === CURLM_OK || $code === CURLM_CALL_MULTI_PERFORM) { + do { + $this->_execStatus = curl_multi_exec($this->_mc, $this->_running); + } while ($this->_execStatus === CURLM_CALL_MULTI_PERFORM); + + return new Manager($key); + } else { + return $code; + } + } + + public function getResult($key = null) { - do - { - $this->execStatus = curl_multi_exec($this->mc, $this->running); - } while ($this->execStatus === CURLM_CALL_MULTI_PERFORM); + if ($key != null) { + if (isset($this->_responses[$key]['code'])) { + return $this->_responses[$key]; + } + + $innerSleepInt = $outerSleepInt = 1; + while ($this->_running && ($this->_execStatus == CURLM_OK || $this->_execStatus == CURLM_CALL_MULTI_PERFORM)) { + usleep(intval($outerSleepInt)); + $outerSleepInt = intval(max(1, ($outerSleepInt*$this->_sleepIncrement))); + $ms=curl_multi_select($this->_mc, 0); + + // bug in PHP 5.3.18+ where curl_multi_select can return -1 + // https://bugs.php.net/bug.php?id=63411 + if ($ms === -1) { + usleep(100000); + } + + // see pull request https://github.com/jmathai/php-multi-curl/pull/17 + // details here http://curl.haxx.se/libcurl/c/libcurl-errors.html + if ($ms >= CURLM_CALL_MULTI_PERFORM) { + do { + $this->_execStatus = curl_multi_exec($this->_mc, $this->_running); + usleep(intval($innerSleepInt)); + $innerSleepInt = intval(max(1, ($innerSleepInt*$this->_sleepIncrement))); + } while ($this->_execStatus==CURLM_CALL_MULTI_PERFORM); + $innerSleepInt = 1; + } + $this->_storeResponses(); + if (isset($this->_responses[$key]['data'])) { + return $this->_responses[$key]; + } + } + return null; + } + return false; + } - return new MultiCurlManager($key); + public static function getSequence() + { + return new Sequence(self::$_timers); } - else + + public static function getTimers() { - return $code; + return self::$_timers; } - } - public function getResult($key = null) - { - if($key != null) + public function inject($key, $value) { - if(isset($this->responses[$key]['code'])) - { - return $this->responses[$key]; - } - - $innerSleepInt = $outerSleepInt = 1; - while($this->running && ($this->execStatus == CURLM_OK || $this->execStatus == CURLM_CALL_MULTI_PERFORM)) - { - usleep(intval($outerSleepInt)); - $outerSleepInt = intval(max(1, ($outerSleepInt*$this->sleepIncrement))); - $ms=curl_multi_select($this->mc, 0); - - // bug in PHP 5.3.18+ where curl_multi_select can return -1 - // https://bugs.php.net/bug.php?id=63411 - if($ms === -1) - usleep(100000); - - // see pull request https://github.com/jmathai/php-multi-curl/pull/17 - // details here http://curl.haxx.se/libcurl/c/libcurl-errors.html - if($ms >= CURLM_CALL_MULTI_PERFORM) - { - do{ - $this->execStatus = curl_multi_exec($this->mc, $this->running); - usleep(intval($innerSleepInt)); - $innerSleepInt = intval(max(1, ($innerSleepInt*$this->sleepIncrement))); - }while($this->execStatus==CURLM_CALL_MULTI_PERFORM); - $innerSleepInt = 1; - } - $this->storeResponses(); - if(isset($this->responses[$key]['data'])) - { - return $this->responses[$key]; - } - } - return null; + $this->$key = $value; } - return false; - } - - public static function getSequence() - { - return new MultiCurlSequence(self::$timers); - } - - public static function getTimers() - { - return self::$timers; - } - - public function inject($key, $value) - { - $this->$key = $value; - } - - private function getKey($ch) - { - return (string)$ch; - } - - private function headerCallback($ch, $header) - { - $_header = trim($header); - $colonPos= strpos($_header, ':'); - if($colonPos > 0) + + private function _getKey($ch) { - $key = substr($_header, 0, $colonPos); - $val = preg_replace('/^\W+/','',substr($_header, $colonPos)); - $this->responses[$this->getKey($ch)]['headers'][$key] = $val; + return (string)$ch; } - return strlen($header); - } - private function storeResponses() - { - while($done = curl_multi_info_read($this->mc)) + private function _headerCallback($ch, $header) { - $this->storeResponse($done); + $_header = trim($header); + $colonPos= strpos($_header, ':'); + if ($colonPos > 0) { + $key = substr($_header, 0, $colonPos); + $val = preg_replace('/^\W+/', '', substr($_header, $colonPos)); + $this->_responses[$this->_getKey($ch)]['headers'][$key] = $val; + } + return strlen($header); } - } - private function storeResponse($done, $isAsynchronous = true) - { - $key = $this->getKey($done['handle']); - $this->stopTimer($key, $done); - if($isAsynchronous) - $this->responses[$key]['data'] = curl_multi_getcontent($done['handle']); - else - $this->responses[$key]['data'] = curl_exec($done['handle']); + private function _storeResponses() + { + while ($done = curl_multi_info_read($this->_mc)) { + $this->_storeResponse($done); + } + } - $this->responses[$key]['response'] = $this->responses[$key]['data']; + private function _storeResponse($done, $isAsynchronous = true) + { + $key = $this->_getKey($done['handle']); + $this->_stopTimer($key, $done); + if ($isAsynchronous) { + $this->_responses[$key]['data'] = curl_multi_getcontent($done['handle']); + } else { + $this->_responses[$key]['data'] = curl_exec($done['handle']); + } - foreach($this->properties as $name => $const) + $this->_responses[$key]['response'] = $this->_responses[$key]['data']; + + foreach ($this->_properties as $name => $const) { + $this->_responses[$key][$name] = curl_getinfo($done['handle'], $const); + } + + if ($isAsynchronous) { + curl_multi_remove_handle($this->_mc, $done['handle']); + } + curl_close($done['handle']); + } + + private function _startTimer($key) { - $this->responses[$key][$name] = curl_getinfo($done['handle'], $const); + self::$_timers[$key]['start'] = microtime(true); } - if($isAsynchronous) - curl_multi_remove_handle($this->mc, $done['handle']); - curl_close($done['handle']); - } - - private function startTimer($key) - { - self::$timers[$key]['start'] = microtime(true); - } - - private function stopTimer($key, $done) - { - self::$timers[$key]['end'] = microtime(true); - self::$timers[$key]['api'] = curl_getinfo($done['handle'], CURLINFO_EFFECTIVE_URL); - self::$timers[$key]['time'] = curl_getinfo($done['handle'], CURLINFO_TOTAL_TIME); - self::$timers[$key]['code'] = curl_getinfo($done['handle'], CURLINFO_HTTP_CODE); - } - - public static function getInstance() - { - if(self::$inst == null) + + private function _stopTimer($key, $done) { - self::$singleton = 1; - self::$inst = new MultiCurl(); + self::$_timers[$key]['end'] = microtime(true); + self::$_timers[$key]['api'] = curl_getinfo($done['handle'], CURLINFO_EFFECTIVE_URL); + self::$_timers[$key]['time'] = curl_getinfo($done['handle'], CURLINFO_TOTAL_TIME); + self::$_timers[$key]['code'] = curl_getinfo($done['handle'], CURLINFO_HTTP_CODE); } - return self::$inst; - } + public static function getInstance() + { + if (self::$_inst == null) { + self::$singleton = 1; + self::$_inst = new MultiCurl(); + } + + return self::$_inst; + } } /* diff --git a/src/MultiCurlException.php b/src/MultiCurlException.php deleted file mode 100644 index 0edc659..0000000 --- a/src/MultiCurlException.php +++ /dev/null @@ -1,3 +0,0 @@ - - */ -class MultiCurlManager -{ - private $key; - private $epiCurl; - - public function __construct($key) - { - $this->key = $key; - $this->epiCurl = MultiCurl::getInstance(); - } - - public function __get($name) - { - $responses = $this->epiCurl->getResult($this->key); - return isset($responses[$name]) ? $responses[$name] : null; - } - - public function __isset($name) - { - $val = self::__get($name); - return empty($val); - } -} diff --git a/src/MultiCurlSequence.php b/src/MultiCurlSequence.php deleted file mode 100644 index 549a562..0000000 --- a/src/MultiCurlSequence.php +++ /dev/null @@ -1,71 +0,0 @@ - - */ -class MultiCurlSequence -{ - private $width = 100; - private $timers; - private $min; - private $max; - private $range; - private $step; - - public function __construct($timers) - { - $this->timers = $timers; - - $min = PHP_INT_MAX; - $max = 0; - foreach($this->timers as $timer) - { - if(!isset($timer['start'])) - $timer['start'] = PHP_INT_MAX; - - if(!isset($timer['end'])) - $timer['end'] = 0; - - $min = min($timer['start'], $min); - $max = max($timer['end'], $max); - } - $this->min = $min; - $this->max = $max; - $this->range = $max-$min; - $this->step = floatval($this->range/$this->width); - } - - public function renderAscii() - { - $tpl = ''; - foreach($this->timers as $timer) - $tpl .= $this->tplAscii($timer); - - return $tpl; - } - - private function tplAscii($timer) - { - $lpad = $rpad = 0; - $lspace = $chars = $rspace = ''; - if($timer['start'] > $this->min) - $lpad = intval(($timer['start'] - $this->min) / $this->step); - if($timer['end'] < $this->max) - $rpad = intval(($this->max - $timer['end']) / $this->step); - $mpad = $this->width - $lpad - $rpad; - if($lpad > 0) - $lspace = str_repeat(' ', $lpad); - if($mpad > 0) - $chars = str_repeat('=', $mpad); - if($rpad > 0) - $rspace = str_repeat(' ', $rpad); - - $tpl = << + */ +class Sequence +{ + private $_width = 100; + private $_timers; + private $_min; + private $_max; + private $_range; + private $_step; + + public function __construct($timers) + { + $this->_timers = $timers; + + $min = PHP_INT_MAX; + $max = 0; + foreach ($this->_timers as $timer) { + if (!isset($timer['start'])) { + $timer['start'] = PHP_INT_MAX; + } + + if (!isset($timer['end'])) { + $timer['end'] = 0; + } + + $min = min($timer['start'], $min); + $max = max($timer['end'], $max); + } + $this->_min = $min; + $this->_max = $max; + $this->_range = $max-$min; + $this->_step = floatval($this->_range/$this->_width); + } + + public function renderAscii() + { + $tpl = ''; + foreach ($this->_timers as $timer) { + $tpl .= $this->_tplAscii($timer); + } + + return $tpl; + } + + private function _tplAscii($timer) + { + $lpad = $rpad = 0; + $lspace = $chars = $rspace = ''; + if ($timer['start'] > $this->_min) { + $lpad = intval(($timer['start'] - $this->_min) / $this->_step); + } + if ($timer['end'] < $this->_max) { + $rpad = intval(($this->_max - $timer['end']) / $this->_step); + } + $mpad = $this->_width - $lpad - $rpad; + if ($lpad > 0) { + $lspace = str_repeat(' ', $lpad); + } + if ($mpad > 0) { + $chars = str_repeat('=', $mpad); + } + if ($rpad > 0) { + $rspace = str_repeat(' ', $rpad); + } + + $tpl = <<addCurl($ch1); - $res2 = $mc->addCurl($ch2); - - $res2->response; - $res1->response; - - $sequenceGraph = $mc->getSequence()->renderAscii(); - - preg_match_all('/[ =]{25}={50}[ =]{25}/', $sequenceGraph, $matches); - $this->assertEquals(2, count($matches[0])); - } - - public function testSynchronousCalls() - { - $ch1 = curl_init('http://slowapi.herokuapp.com/delay/2.0'); - curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); - $ch2 = curl_init('http://slowapi.herokuapp.com/delay/2.0'); - curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); - - MultiCurl::$singleton = 0; - $mc = MultiCurl::getInstance(); - $res1 = $mc->addCurl($ch1); - $res1->response; - - $res2 = $mc->addCurl($ch2); - $res2->response; - - $sequenceGraph = $mc->getSequence()->renderAscii(); - - preg_match_all('/[ =]{25}={50}[ =]{25}/', $sequenceGraph, $matches); - $this->assertEquals(0, count($matches[0])); - } + public function testAsynchronousCalls() + { + $ch1 = curl_init('http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.com'); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); + $ch2 = curl_init('http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.com'); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); + + MultiCurl::$singleton = 0; + $mc = MultiCurl::getInstance(); + $res1 = $mc->addCurl($ch1); + $res2 = $mc->addCurl($ch2); + + $res2->response; + $res1->response; + + $sequenceGraph = $mc->getSequence()->renderAscii(); + + preg_match_all('/[ =]{25}={50}[ =]{25}/', $sequenceGraph, $matches); + $this->assertEquals(2, count($matches[0])); + } + + public function testSynchronousCalls() + { + $ch1 = curl_init('http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.com'); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); + $ch2 = curl_init('http://slowwly.robertomurray.co.uk/delay/2000/url/http://www.google.com'); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); + + MultiCurl::$singleton = 0; + $mc = MultiCurl::getInstance(); + $res1 = $mc->addCurl($ch1); + $res1->response; + + $res2 = $mc->addCurl($ch2); + $res2->response; + + $sequenceGraph = $mc->getSequence()->renderAscii(); + + preg_match_all('/[ =]{25}={50}[ =]{25}/', $sequenceGraph, $matches); + $this->assertEquals(0, count($matches[0])); + } } diff --git a/tests/CallsTest.php b/tests/CallsTest.php index b383d64..4a8a04c 100644 --- a/tests/CallsTest.php +++ b/tests/CallsTest.php @@ -1,45 +1,46 @@ -addURL('http://www.google.com'); // call google + public function testGetCode() + { + $mc = MultiCurl::getInstance(); + $google = $mc->addURL('http://www.google.com'); // call google - $this->assertInternalType('integer', $google->code); - } + $this->assertInternalType('integer', $google->code); + } - public function testGetCode200() - { - $mc = MultiCurl::getInstance(); - $google = $mc->addUrl('http://www.google.com'); + public function testGetCode200() + { + $mc = MultiCurl::getInstance(); + $github = $mc->addUrl('https://github.com'); - $this->assertEquals(200, $google->code); - } + $this->assertEquals(200, $github->code); + } - public function testGetCode404() - { - $mc = MultiCurl::getInstance(); - $google = $mc->addUrl('http://www.example.com/404'); + public function testGetCode404() + { + $mc = MultiCurl::getInstance(); + $google = $mc->addUrl('https://www.google.com/404'); - $this->assertEquals(404, $google->code); - } + $this->assertEquals(404, $google->code); + } - public function testResponseJson() - { - $mc = MultiCurl::getInstance(); - $call = $mc->addUrl('http://jsonplaceholder.typicode.com/users'); - $response = json_decode($call->response, true); - $this->assertInternalType('array', $response); - } + public function testResponseJson() + { + $mc = MultiCurl::getInstance(); + $call = $mc->addUrl('http://jsonplaceholder.typicode.com/users'); + $response = json_decode($call->response, true); + $this->assertInternalType('array', $response); + } - public function testResponseHeaders() - { - $mc = MultiCurl::getInstance(); - $google = $mc->addUrl('http://www.example.com/404'); + public function testResponseHeaders() + { + $mc = MultiCurl::getInstance(); + $google = $mc->addUrl('https://www.google.com'); - $this->assertInternalType('array', $google->headers); - $this->assertNotEmpty($google->headers['Etag']); - } + $this->assertInternalType('array', $google->headers); + $this->assertNotEmpty($google->headers['Date']); + } } diff --git a/tests/MultiCurlTest.php b/tests/MultiCurlTest.php index 73f824f..10a95a8 100644 --- a/tests/MultiCurlTest.php +++ b/tests/MultiCurlTest.php @@ -1,40 +1,41 @@ -setExpectedException('JMathai\PhpMultiCurl\MultiCurlException'); - new MultiCurl(); - } + public function testConstructorThrowsExceptionWhenInvokedWithNew() + { + MultiCurl::$singleton = 0; + $this->setExpectedException('JMathai\PhpMultiCurl\MultiException'); + new MultiCurl(); + } - public function testConstructorSucceedsAsSingleton() - { - MultiCurl::getInstance(); - } + public function testConstructorSucceedsAsSingleton() + { + MultiCurl::getInstance(); + } - public function testAddUrl() - { - $mc = MultiCurl::getInstance(); - $res = $mc->addUrl('http://google.com'); - $this->assertInstanceOf('Jmathai\PhpMultiCurl\MultiCurlManager', $res); - } + public function testAddUrl() + { + $mc = MultiCurl::getInstance(); + $res = $mc->addUrl('http://google.com'); + $this->assertInstanceOf('Jmathai\PhpMultiCurl\Manager', $res); + } - public function testAddCurl() - { - $ch = curl_init('https://www.google.com'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + public function testAddCurl() + { + $ch = curl_init('https://www.google.com'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $mc = MultiCurl::getInstance(); - $res = $mc->addCurl($ch); - $this->assertInstanceOf('Jmathai\PhpMultiCurl\MultiCurlManager', $res); - } + $mc = MultiCurl::getInstance(); + $res = $mc->addCurl($ch); + $this->assertInstanceOf('Jmathai\PhpMultiCurl\Manager', $res); + } - public function testAddCurlWithNull() - { - $this->setExpectedException('JMathai\PhpMultiCurl\MultiCurlInvalidParameterException'); - $mc = MultiCurl::getInstance(); - $res = $mc->addCurl(null); - } + public function testAddCurlWithNull() + { + $this->setExpectedException('JMathai\PhpMultiCurl\MultiInvalidParameterException'); + $mc = MultiCurl::getInstance(); + $res = $mc->addCurl(null); + } }