# CurlHttpClient.class.php

Go to the documentation of this file.

    00001 <?php
    00002 /***************************************************************************
    00003  *   Copyright (C) 2007-2009 by Anton E. Lebedevich                        *
    00004  *                                                                         *
    00005  *   This program is free software; you can redistribute it and/or modify  *
    00006  *   it under the terms of the GNU Lesser General Public License as        *
    00007  *   published by the Free Software Foundation; either version 3 of the    *
    00008  *   License, or (at your option) any later version.                       *
    00009  *                                                                         *
    00010  ***************************************************************************/
    00011 /* $Id: CurlHttpClient.class.php 45 2009-05-08 07:41:33Z lom $ */
    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 ?>