Go to the documentation of this file.00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00016 final class CurlHttpClient implements HttpClient
00017 {
00018 private $options = array();
00019
00020 private $followLocation = null;
00021 private $maxFileSize = null;
00022 private $noBody = null;
00023 private $multiRequests = array();
00024 private $multiResponses = array();
00025 private $multiThreadOptions = array();
00026
00030 public static function create()
00031 {
00032 return new self;
00033 }
00034
00038 public function setOption($key, $value)
00039 {
00040 $this->options[$key] = $value;
00041
00042 return $this;
00043 }
00044
00048 public function dropOption($key)
00049 {
00050 unset($this->options[$key]);
00051
00052 return $this;
00053 }
00054
00055 public function getOption($key)
00056 {
00057 if (isset($this->options[$key]))
00058 return $this->options[$key];
00059
00060 throw new MissingElementException();
00061 }
00062
00067 public function setTimeout($timeout)
00068 {
00069 $this->options[CURLOPT_TIMEOUT] = $timeout;
00070
00071 return $this;
00072 }
00073
00077 public function getTimeout()
00078 {
00079 if (isset($this->options[CURLOPT_TIMEOUT]))
00080 return $this->options[CURLOPT_TIMEOUT];
00081
00082 return null;
00083 }
00084
00091 public function setFollowLocation($really)
00092 {
00093 Assert::isBoolean($really);
00094 $this->followLocation = $really;
00095 return $this;
00096 }
00097
00098 public function isFollowLocation()
00099 {
00100 return $this->followLocation;
00101 }
00102
00107 public function setNoBody($really)
00108 {
00109 Assert::isBoolean($really);
00110 $this->noBody = $really;
00111 return $this;
00112 }
00113
00114 public function hasNoBody()
00115 {
00116 return $this->noBody;
00117 }
00118
00122 public function setMaxRedirects($maxRedirects)
00123 {
00124 $this->options[CURLOPT_MAXREDIRS] = $maxRedirects;
00125
00126 return $this;
00127 }
00128
00129 public function getMaxRedirects()
00130 {
00131 if (isset($this->options[CURLOPT_MAXREDIRS]))
00132 return $this->options[CURLOPT_MAXREDIRS];
00133
00134 return null;
00135 }
00136
00140 public function setMaxFileSize($maxFileSize)
00141 {
00142 $this->maxFileSize = $maxFileSize;
00143 return $this;
00144 }
00145
00146 public function getMaxFileSize()
00147 {
00148 return $this->maxFileSize;
00149 }
00150
00154 public function addRequest(HttpRequest $request, $options = array())
00155 {
00156 Assert::isArray($options);
00157
00158 $key = $this->getRequestKey($request);
00159
00160 if (isset($this->multiRequests[$key]))
00161 throw new WrongArgumentException('There is allready such alias');
00162
00163 $this->multiRequests[$key] = $request;
00164
00165 foreach ($options as $k => $val)
00166 $this->multiThreadOptions[$key][$k] = $val;
00167
00168 return $this;
00169 }
00170
00174 public function getResponse(HttpRequest $request)
00175 {
00176 $key = $this->getRequestKey($request);
00177
00178 if (!isset($this->multiResponses[$key]))
00179 throw new WrongArgumentException('There is no response fo this alias');
00180
00181 return $this->multiResponses[$key];
00182 }
00183
00187 public function send(HttpRequest $request)
00188 {
00189 $response = CurlHttpResponse::create()->
00190 setMaxFileSize($this->maxFileSize);
00191
00192 $handle = $this->makeHandle($request, $response);
00193
00194 if (curl_exec($handle) === false) {
00195 $code = curl_errno($handle);
00196 throw new NetworkException(
00197 'curl error, code: '.$code
00198 .' description: '.curl_error($handle),
00199 $code
00200 );
00201 }
00202
00203 $this->makeResponse($handle, $response);
00204
00205 curl_close($handle);
00206
00207 return $response;
00208 }
00209
00210 public function multiSend()
00211 {
00212 Assert::isNotEmptyArray($this->multiRequests);
00213
00214 $handles = array();
00215 $mh = curl_multi_init();
00216
00217 foreach ($this->multiRequests as $alias => $request) {
00218 $this->multiResponses[$alias] = new CurlHttpResponse();
00219
00220 $handles[$alias] =
00221 $this->makeHandle(
00222 $request,
00223 $this->multiResponses[$alias]
00224 );
00225
00226 if (isset($this->multiThreadOptions[$alias]))
00227 foreach ($this->multiThreadOptions[$alias] as $key => $value)
00228 curl_setopt($handles[$alias], $key, $value);
00229
00230 curl_multi_add_handle($mh, $handles[$alias]);
00231 }
00232
00233 $running = null;
00234 do {
00235 curl_multi_exec($mh, $running);
00236 } while ($running > 0);
00237
00238 foreach ($this->multiResponses as $alias => $response) {
00239 $this->makeResponse($handles[$alias], $response);
00240 curl_multi_remove_handle($mh, $handles[$alias]);
00241 curl_close($handles[$alias]);
00242 }
00243
00244 curl_multi_close($mh);
00245
00246 return true;
00247 }
00248
00249 protected function getRequestKey(HttpRequest $request)
00250 {
00251 return md5(serialize($request));
00252 }
00253
00254 protected function makeHandle(HttpRequest $request, CurlHttpResponse $response)
00255 {
00256 $handle = curl_init();
00257 Assert::isNotNull($request->getMethod());
00258
00259 $options = array(
00260 CURLOPT_WRITEFUNCTION => array($response, 'writeBody'),
00261 CURLOPT_HEADERFUNCTION => array($response, 'writeHeader'),
00262 CURLOPT_URL => $request->getUrl()->toString(),
00263 CURLOPT_USERAGENT => 'onPHP::'.__CLASS__
00264 );
00265
00266 if ($this->noBody !== null)
00267 $options[CURLOPT_NOBODY] = $this->noBody;
00268
00269 if ($this->followLocation !== null)
00270 $options[CURLOPT_FOLLOWLOCATION] = $this->followLocation;
00271
00272 switch ($request->getMethod()->getId()) {
00273 case HttpMethod::GET:
00274 $options[CURLOPT_HTTPGET] = true;
00275
00276 if ($request->getGet())
00277 $options[CURLOPT_URL] .=
00278 ($request->getUrl()->getQuery() ? '&' : '?')
00279 .$this->argumentsToString($request->getGet());
00280 break;
00281
00282 case HttpMethod::POST:
00283 $options[CURLOPT_POST] = true;
00284 $options[CURLOPT_POSTFIELDS] =
00285 $this->argumentsToString($request->getPost());
00286 break;
00287
00288 default:
00289 $options[CURLOPT_CUSTOMREQUEST] = $request->getMethod()->getName();
00290 break;
00291 }
00292
00293 $headers = array();
00294 foreach ($request->getHeaderList() as $headerName => $headerValue) {
00295 $headers[] = "{$headerName}: $headerValue";
00296 }
00297
00298 if ($headers) {
00299 $options[CURLOPT_HTTPHEADER] = $headers;
00300 }
00301
00302 if ($request->getCookie()) {
00303 $cookies = array();
00304 foreach ($request->getCookie() as $name => $value)
00305 $cookies[] = $name.'='.urlencode($value);
00306
00307 $options[CURLOPT_COOKIE] = implode('; ', $cookies);
00308 }
00309
00310 foreach ($this->options as $key => $value) {
00311 $options[$key] = $value;
00312 }
00313
00314 curl_setopt_array($handle, $options);
00315
00316 return $handle;
00317 }
00318
00322 protected function makeResponse($handle, CurlHttpResponse $response)
00323 {
00324 Assert::isNotNull($handle);
00325
00326 $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
00327 try {
00328 $response->setStatus(
00329 new HttpStatus($httpCode)
00330 );
00331 } catch (MissingElementException $e) {
00332 throw new NetworkException(
00333 'curl error, strange http code: '.$httpCode
00334 );
00335 }
00336
00337 return $this;
00338 }
00339
00340 private function argumentsToString($array)
00341 {
00342 Assert::isArray($array);
00343 $result = array();
00344
00345 foreach ($array as $key => $value) {
00346 if (is_array($value)) {
00347 foreach ($value as $valueKey => $simpleValue) {
00348 $result[] =
00349 $key.'['.$valueKey.']='.urlencode($simpleValue);
00350 }
00351 } else {
00352 $result[] = $key.'='.urlencode($value);
00353 }
00354 }
00355
00356 return implode('&', $result);
00357 }
00358 }
00359 ?>