[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/zend/Zend/Http/ -> Client.php (source)

   1  <?php
   2  
   3  /**
   4   * Zend Framework
   5   *
   6   * LICENSE
   7   *
   8   * This source file is subject to the new BSD license that is bundled
   9   * with this package in the file LICENSE.txt.
  10   * It is also available through the world-wide-web at this URL:
  11   * http://framework.zend.com/license/new-bsd
  12   * If you did not receive a copy of the license and are unable to
  13   * obtain it through the world-wide-web, please send an email
  14   * to [email protected] so we can send you a copy immediately.
  15   *
  16   * @category   Zend
  17   * @package    Zend_Http
  18   * @subpackage Client
  19   * @version    $Id$
  20   * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  21   * @license    http://framework.zend.com/license/new-bsd     New BSD License
  22   */
  23  
  24  /**
  25   * @see Zend_Loader
  26   */
  27  require_once 'Zend/Loader.php';
  28  
  29  
  30  /**
  31   * @see Zend_Uri
  32   */
  33  require_once 'Zend/Uri.php';
  34  
  35  
  36  /**
  37   * @see Zend_Http_Client_Adapter_Interface
  38   */
  39  require_once 'Zend/Http/Client/Adapter/Interface.php';
  40  
  41  
  42  /**
  43   * @see Zend_Http_Response
  44   */
  45  require_once 'Zend/Http/Response.php';
  46  
  47  /**
  48   * @see Zend_Http_Response_Stream
  49   */
  50  require_once 'Zend/Http/Response/Stream.php';
  51  
  52  /**
  53   * Zend_Http_Client is an implemetation of an HTTP client in PHP. The client
  54   * supports basic features like sending different HTTP requests and handling
  55   * redirections, as well as more advanced features like proxy settings, HTTP
  56   * authentication and cookie persistance (using a Zend_Http_CookieJar object)
  57   *
  58   * @todo Implement proxy settings
  59   * @category   Zend
  60   * @package    Zend_Http
  61   * @subpackage Client
  62   * @throws     Zend_Http_Client_Exception
  63   * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  64   * @license    http://framework.zend.com/license/new-bsd     New BSD License
  65   */
  66  class Zend_Http_Client
  67  {
  68      /**
  69       * HTTP request methods
  70       */
  71      const GET     = 'GET';
  72      const POST    = 'POST';
  73      const PUT     = 'PUT';
  74      const HEAD    = 'HEAD';
  75      const DELETE  = 'DELETE';
  76      const TRACE   = 'TRACE';
  77      const OPTIONS = 'OPTIONS';
  78      const CONNECT = 'CONNECT';
  79      const MERGE   = 'MERGE';
  80  
  81      /**
  82       * Supported HTTP Authentication methods
  83       */
  84      const AUTH_BASIC = 'basic';
  85      //const AUTH_DIGEST = 'digest'; <-- not implemented yet
  86  
  87      /**
  88       * HTTP protocol versions
  89       */
  90      const HTTP_1 = '1.1';
  91      const HTTP_0 = '1.0';
  92  
  93      /**
  94       * Content attributes
  95       */
  96      const CONTENT_TYPE   = 'Content-Type';
  97      const CONTENT_LENGTH = 'Content-Length';
  98  
  99      /**
 100       * POST data encoding methods
 101       */
 102      const ENC_URLENCODED = 'application/x-www-form-urlencoded';
 103      const ENC_FORMDATA   = 'multipart/form-data';
 104  
 105      /**
 106       * Configuration array, set using the constructor or using ::setConfig()
 107       *
 108       * @var array
 109       */
 110      protected $config = array(
 111          'maxredirects'    => 5,
 112          'strictredirects' => false,
 113          'useragent'       => 'Zend_Http_Client',
 114          'timeout'         => 10,
 115          'adapter'         => 'Zend_Http_Client_Adapter_Socket',
 116          'httpversion'     => self::HTTP_1,
 117          'keepalive'       => false,
 118          'storeresponse'   => true,
 119          'strict'          => true,
 120          'output_stream'   => false,
 121          'encodecookies'   => true,
 122      );
 123  
 124      /**
 125       * The adapter used to preform the actual connection to the server
 126       *
 127       * @var Zend_Http_Client_Adapter_Interface
 128       */
 129      protected $adapter = null;
 130  
 131      /**
 132       * Request URI
 133       *
 134       * @var Zend_Uri_Http
 135       */
 136      protected $uri = null;
 137  
 138      /**
 139       * Associative array of request headers
 140       *
 141       * @var array
 142       */
 143      protected $headers = array();
 144  
 145      /**
 146       * HTTP request method
 147       *
 148       * @var string
 149       */
 150      protected $method = self::GET;
 151  
 152      /**
 153       * Associative array of GET parameters
 154       *
 155       * @var array
 156       */
 157      protected $paramsGet = array();
 158  
 159      /**
 160       * Assiciative array of POST parameters
 161       *
 162       * @var array
 163       */
 164      protected $paramsPost = array();
 165  
 166      /**
 167       * Request body content type (for POST requests)
 168       *
 169       * @var string
 170       */
 171      protected $enctype = null;
 172  
 173      /**
 174       * The raw post data to send. Could be set by setRawData($data, $enctype).
 175       *
 176       * @var string
 177       */
 178      protected $raw_post_data = null;
 179  
 180      /**
 181       * HTTP Authentication settings
 182       *
 183       * Expected to be an associative array with this structure:
 184       * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic')
 185       * Where 'type' should be one of the supported authentication types (see the AUTH_*
 186       * constants), for example 'basic' or 'digest'.
 187       *
 188       * If null, no authentication will be used.
 189       *
 190       * @var array|null
 191       */
 192      protected $auth;
 193  
 194      /**
 195       * File upload arrays (used in POST requests)
 196       *
 197       * An associative array, where each element is of the format:
 198       *   'name' => array('filename.txt', 'text/plain', 'This is the actual file contents')
 199       *
 200       * @var array
 201       */
 202      protected $files = array();
 203  
 204      /**
 205       * The client's cookie jar
 206       *
 207       * @var Zend_Http_CookieJar
 208       */
 209      protected $cookiejar = null;
 210  
 211      /**
 212       * The last HTTP request sent by the client, as string
 213       *
 214       * @var string
 215       */
 216      protected $last_request = null;
 217  
 218      /**
 219       * The last HTTP response received by the client
 220       *
 221       * @var Zend_Http_Response
 222       */
 223      protected $last_response = null;
 224  
 225      /**
 226       * Redirection counter
 227       *
 228       * @var int
 229       */
 230      protected $redirectCounter = 0;
 231  
 232      /**
 233       * Fileinfo magic database resource
 234       *
 235       * This varaiable is populated the first time _detectFileMimeType is called
 236       * and is then reused on every call to this method
 237       *
 238       * @var resource
 239       */
 240      static protected $_fileInfoDb = null;
 241  
 242      /**
 243       * Contructor method. Will create a new HTTP client. Accepts the target
 244       * URL and optionally configuration array.
 245       *
 246       * @param Zend_Uri_Http|string $uri
 247       * @param array $config Configuration key-value pairs.
 248       */
 249      public function __construct($uri = null, $config = null)
 250      {
 251          if ($uri !== null) {
 252              $this->setUri($uri);
 253          }
 254          if ($config !== null) {
 255              $this->setConfig($config);
 256          }
 257      }
 258  
 259      /**
 260       * Set the URI for the next request
 261       *
 262       * @param  Zend_Uri_Http|string $uri
 263       * @return Zend_Http_Client
 264       * @throws Zend_Http_Client_Exception
 265       */
 266      public function setUri($uri)
 267      {
 268          if (is_string($uri)) {
 269              $uri = Zend_Uri::factory($uri);
 270          }
 271  
 272          if (!$uri instanceof Zend_Uri_Http) {
 273              /** @see Zend_Http_Client_Exception */
 274              require_once 'Zend/Http/Client/Exception.php';
 275              throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.');
 276          }
 277  
 278          // Set auth if username and password has been specified in the uri
 279          if ($uri->getUsername() && $uri->getPassword()) {
 280              $this->setAuth($uri->getUsername(), $uri->getPassword());
 281          }
 282  
 283          // We have no ports, set the defaults
 284          if (! $uri->getPort()) {
 285              $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80));
 286          }
 287  
 288          $this->uri = $uri;
 289  
 290          return $this;
 291      }
 292  
 293      /**
 294       * Get the URI for the next request
 295       *
 296       * @param boolean $as_string If true, will return the URI as a string
 297       * @return Zend_Uri_Http|string
 298       */
 299      public function getUri($as_string = false)
 300      {
 301          if ($as_string && $this->uri instanceof Zend_Uri_Http) {
 302              return $this->uri->__toString();
 303          } else {
 304              return $this->uri;
 305          }
 306      }
 307  
 308      /**
 309       * Set configuration parameters for this HTTP client
 310       *
 311       * @param  Zend_Config | array $config
 312       * @return Zend_Http_Client
 313       * @throws Zend_Http_Client_Exception
 314       */
 315      public function setConfig($config = array())
 316      {
 317          if ($config instanceof Zend_Config) {
 318              $config = $config->toArray();
 319  
 320          } elseif (! is_array($config)) {
 321              /** @see Zend_Http_Client_Exception */
 322              require_once 'Zend/Http/Client/Exception.php';
 323              throw new Zend_Http_Client_Exception('Array or Zend_Config object expected, got ' . gettype($config));
 324          }
 325  
 326          foreach ($config as $k => $v) {
 327              $this->config[strtolower($k)] = $v;
 328          }
 329  
 330          // Pass configuration options to the adapter if it exists
 331          if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
 332              $this->adapter->setConfig($config);
 333          }
 334  
 335          return $this;
 336      }
 337  
 338      /**
 339       * Set the next request's method
 340       *
 341       * Validated the passed method and sets it. If we have files set for
 342       * POST requests, and the new method is not POST, the files are silently
 343       * dropped.
 344       *
 345       * @param string $method
 346       * @return Zend_Http_Client
 347       * @throws Zend_Http_Client_Exception
 348       */
 349      public function setMethod($method = self::GET)
 350      {
 351          if (! preg_match('/^[^\x00-\x1f\x7f-\xff\(\)<>@,;:\\\\"\/\[\]\?={}\s]+$/', $method)) {
 352              /** @see Zend_Http_Client_Exception */
 353              require_once 'Zend/Http/Client/Exception.php';
 354              throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method.");
 355          }
 356  
 357          if ($method == self::POST && $this->enctype === null) {
 358              $this->setEncType(self::ENC_URLENCODED);
 359          }
 360  
 361          $this->method = $method;
 362  
 363          return $this;
 364      }
 365  
 366      /**
 367       * Set one or more request headers
 368       *
 369       * This function can be used in several ways to set the client's request
 370       * headers:
 371       * 1. By providing two parameters: $name as the header to set (eg. 'Host')
 372       *    and $value as it's value (eg. 'www.example.com').
 373       * 2. By providing a single header string as the only parameter
 374       *    eg. 'Host: www.example.com'
 375       * 3. By providing an array of headers as the first parameter
 376       *    eg. array('host' => 'www.example.com', 'x-foo: bar'). In This case
 377       *    the function will call itself recursively for each array item.
 378       *
 379       * @param string|array $name Header name, full header string ('Header: value')
 380       *     or an array of headers
 381       * @param mixed $value Header value or null
 382       * @return Zend_Http_Client
 383       * @throws Zend_Http_Client_Exception
 384       */
 385      public function setHeaders($name, $value = null)
 386      {
 387          // If we got an array, go recusive!
 388          if (is_array($name)) {
 389              foreach ($name as $k => $v) {
 390                  if (is_string($k)) {
 391                      $this->setHeaders($k, $v);
 392                  } else {
 393                      $this->setHeaders($v, null);
 394                  }
 395              }
 396          } else {
 397              // Check if $name needs to be split
 398              if ($value === null && (strpos($name, ':') > 0)) {
 399                  list($name, $value) = explode(':', $name, 2);
 400              }
 401  
 402              // Make sure the name is valid if we are in strict mode
 403              if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) {
 404                  /** @see Zend_Http_Client_Exception */
 405                  require_once 'Zend/Http/Client/Exception.php';
 406                  throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name");
 407              }
 408  
 409              $normalized_name = strtolower($name);
 410  
 411              // If $value is null or false, unset the header
 412              if ($value === null || $value === false) {
 413                  unset($this->headers[$normalized_name]);
 414  
 415              // Else, set the header
 416              } else {
 417                  // Header names are stored lowercase internally.
 418                  if (is_string($value)) {
 419                      $value = trim($value);
 420                  }
 421                  $this->headers[$normalized_name] = array($name, $value);
 422              }
 423          }
 424  
 425          return $this;
 426      }
 427  
 428      /**
 429       * Get the value of a specific header
 430       *
 431       * Note that if the header has more than one value, an array
 432       * will be returned.
 433       *
 434       * @param string $key
 435       * @return string|array|null The header value or null if it is not set
 436       */
 437      public function getHeader($key)
 438      {
 439          $key = strtolower($key);
 440          if (isset($this->headers[$key])) {
 441              return $this->headers[$key][1];
 442          } else {
 443              return null;
 444          }
 445      }
 446  
 447      /**
 448       * Set a GET parameter for the request. Wrapper around _setParameter
 449       *
 450       * @param string|array $name
 451       * @param string $value
 452       * @return Zend_Http_Client
 453       */
 454      public function setParameterGet($name, $value = null)
 455      {
 456          if (is_array($name)) {
 457              foreach ($name as $k => $v)
 458                  $this->_setParameter('GET', $k, $v);
 459          } else {
 460              $this->_setParameter('GET', $name, $value);
 461          }
 462  
 463          return $this;
 464      }
 465  
 466      /**
 467       * Set a POST parameter for the request. Wrapper around _setParameter
 468       *
 469       * @param string|array $name
 470       * @param string $value
 471       * @return Zend_Http_Client
 472       */
 473      public function setParameterPost($name, $value = null)
 474      {
 475          if (is_array($name)) {
 476              foreach ($name as $k => $v)
 477                  $this->_setParameter('POST', $k, $v);
 478          } else {
 479              $this->_setParameter('POST', $name, $value);
 480          }
 481  
 482          return $this;
 483      }
 484  
 485      /**
 486       * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost
 487       *
 488       * @param string $type GET or POST
 489       * @param string $name
 490       * @param string $value
 491       * @return null
 492       */
 493      protected function _setParameter($type, $name, $value)
 494      {
 495          $parray = array();
 496          $type = strtolower($type);
 497          switch ($type) {
 498              case 'get':
 499                  $parray = &$this->paramsGet;
 500                  break;
 501              case 'post':
 502                  $parray = &$this->paramsPost;
 503                  break;
 504          }
 505  
 506          if ($value === null) {
 507              if (isset($parray[$name])) unset($parray[$name]);
 508          } else {
 509              $parray[$name] = $value;
 510          }
 511      }
 512  
 513      /**
 514       * Get the number of redirections done on the last request
 515       *
 516       * @return int
 517       */
 518      public function getRedirectionsCount()
 519      {
 520          return $this->redirectCounter;
 521      }
 522  
 523      /**
 524       * Set HTTP authentication parameters
 525       *
 526       * $type should be one of the supported types - see the self::AUTH_*
 527       * constants.
 528       *
 529       * To enable authentication:
 530       * <code>
 531       * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC);
 532       * </code>
 533       *
 534       * To disable authentication:
 535       * <code>
 536       * $this->setAuth(false);
 537       * </code>
 538       *
 539       * @see http://www.faqs.org/rfcs/rfc2617.html
 540       * @param string|false $user User name or false disable authentication
 541       * @param string $password Password
 542       * @param string $type Authentication type
 543       * @return Zend_Http_Client
 544       * @throws Zend_Http_Client_Exception
 545       */
 546      public function setAuth($user, $password = '', $type = self::AUTH_BASIC)
 547      {
 548          // If we got false or null, disable authentication
 549          if ($user === false || $user === null) {
 550              $this->auth = null;
 551  
 552              // Clear the auth information in the uri instance as well
 553              if ($this->uri instanceof Zend_Uri_Http) {
 554                  $this->getUri()->setUsername('');
 555                  $this->getUri()->setPassword('');
 556              }
 557          // Else, set up authentication
 558          } else {
 559              // Check we got a proper authentication type
 560              if (! defined('self::AUTH_' . strtoupper($type))) {
 561                  /** @see Zend_Http_Client_Exception */
 562                  require_once 'Zend/Http/Client/Exception.php';
 563                  throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'");
 564              }
 565  
 566              $this->auth = array(
 567                  'user' => (string) $user,
 568                  'password' => (string) $password,
 569                  'type' => $type
 570              );
 571          }
 572  
 573          return $this;
 574      }
 575  
 576      /**
 577       * Set the HTTP client's cookie jar.
 578       *
 579       * A cookie jar is an object that holds and maintains cookies across HTTP requests
 580       * and responses.
 581       *
 582       * @param Zend_Http_CookieJar|boolean $cookiejar Existing cookiejar object, true to create a new one, false to disable
 583       * @return Zend_Http_Client
 584       * @throws Zend_Http_Client_Exception
 585       */
 586      public function setCookieJar($cookiejar = true)
 587      {
 588          if (! class_exists('Zend_Http_CookieJar')) {
 589              require_once 'Zend/Http/CookieJar.php';
 590          }
 591  
 592          if ($cookiejar instanceof Zend_Http_CookieJar) {
 593              $this->cookiejar = $cookiejar;
 594          } elseif ($cookiejar === true) {
 595              $this->cookiejar = new Zend_Http_CookieJar();
 596          } elseif (! $cookiejar) {
 597              $this->cookiejar = null;
 598          } else {
 599              /** @see Zend_Http_Client_Exception */
 600              require_once 'Zend/Http/Client/Exception.php';
 601              throw new Zend_Http_Client_Exception('Invalid parameter type passed as CookieJar');
 602          }
 603  
 604          return $this;
 605      }
 606  
 607      /**
 608       * Return the current cookie jar or null if none.
 609       *
 610       * @return Zend_Http_CookieJar|null
 611       */
 612      public function getCookieJar()
 613      {
 614          return $this->cookiejar;
 615      }
 616  
 617      /**
 618       * Add a cookie to the request. If the client has no Cookie Jar, the cookies
 619       * will be added directly to the headers array as "Cookie" headers.
 620       *
 621       * @param Zend_Http_Cookie|string $cookie
 622       * @param string|null $value If "cookie" is a string, this is the cookie value.
 623       * @return Zend_Http_Client
 624       * @throws Zend_Http_Client_Exception
 625       */
 626      public function setCookie($cookie, $value = null)
 627      {
 628          if (! class_exists('Zend_Http_Cookie')) {
 629              require_once 'Zend/Http/Cookie.php';
 630          }
 631  
 632          if (is_array($cookie)) {
 633              foreach ($cookie as $c => $v) {
 634                  if (is_string($c)) {
 635                      $this->setCookie($c, $v);
 636                  } else {
 637                      $this->setCookie($v);
 638                  }
 639              }
 640  
 641              return $this;
 642          }
 643  
 644          if ($value !== null && $this->config['encodecookies']) {
 645              $value = urlencode($value);
 646          }
 647  
 648          if (isset($this->cookiejar)) {
 649              if ($cookie instanceof Zend_Http_Cookie) {
 650                  $this->cookiejar->addCookie($cookie);
 651              } elseif (is_string($cookie) && $value !== null) {
 652                  $cookie = Zend_Http_Cookie::fromString("{$cookie}={$value}",
 653                                                         $this->uri,
 654                                                         $this->config['encodecookies']);
 655                  $this->cookiejar->addCookie($cookie);
 656              }
 657          } else {
 658              if ($cookie instanceof Zend_Http_Cookie) {
 659                  $name = $cookie->getName();
 660                  $value = $cookie->getValue();
 661                  $cookie = $name;
 662              }
 663  
 664              if (preg_match("/[=,; \t\r\n\013\014]/", $cookie)) {
 665                  /** @see Zend_Http_Client_Exception */
 666                  require_once 'Zend/Http/Client/Exception.php';
 667                  throw new Zend_Http_Client_Exception("Cookie name cannot contain these characters: =,; \t\r\n\013\014 ({$cookie})");
 668              }
 669  
 670              $value = addslashes($value);
 671  
 672              if (! isset($this->headers['cookie'])) {
 673                  $this->headers['cookie'] = array('Cookie', '');
 674              }
 675              $this->headers['cookie'][1] .= $cookie . '=' . $value . '; ';
 676          }
 677  
 678          return $this;
 679      }
 680  
 681      /**
 682       * Set a file to upload (using a POST request)
 683       *
 684       * Can be used in two ways:
 685       *
 686       * 1. $data is null (default): $filename is treated as the name if a local file which
 687       *    will be read and sent. Will try to guess the content type using mime_content_type().
 688       * 2. $data is set - $filename is sent as the file name, but $data is sent as the file
 689       *    contents and no file is read from the file system. In this case, you need to
 690       *    manually set the Content-Type ($ctype) or it will default to
 691       *    application/octet-stream.
 692       *
 693       * @param string $filename Name of file to upload, or name to save as
 694       * @param string $formname Name of form element to send as
 695       * @param string $data Data to send (if null, $filename is read and sent)
 696       * @param string $ctype Content type to use (if $data is set and $ctype is
 697       *     null, will be application/octet-stream)
 698       * @return Zend_Http_Client
 699       * @throws Zend_Http_Client_Exception
 700       */
 701      public function setFileUpload($filename, $formname, $data = null, $ctype = null)
 702      {
 703          if ($data === null) {
 704              if (($data = @file_get_contents($filename)) === false) {
 705                  /** @see Zend_Http_Client_Exception */
 706                  require_once 'Zend/Http/Client/Exception.php';
 707                  throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload");
 708              }
 709  
 710              if (! $ctype) {
 711                  $ctype = $this->_detectFileMimeType($filename);
 712              }
 713          }
 714  
 715          // Force enctype to multipart/form-data
 716          $this->setEncType(self::ENC_FORMDATA);
 717  
 718          $this->files[] = array(
 719              'formname' => $formname,
 720              'filename' => basename($filename),
 721              'ctype'    => $ctype,
 722              'data'     => $data
 723          );
 724  
 725          return $this;
 726      }
 727  
 728      /**
 729       * Set the encoding type for POST data
 730       *
 731       * @param string $enctype
 732       * @return Zend_Http_Client
 733       */
 734      public function setEncType($enctype = self::ENC_URLENCODED)
 735      {
 736          $this->enctype = $enctype;
 737  
 738          return $this;
 739      }
 740  
 741      /**
 742       * Set the raw (already encoded) POST data.
 743       *
 744       * This function is here for two reasons:
 745       * 1. For advanced user who would like to set their own data, already encoded
 746       * 2. For backwards compatibilty: If someone uses the old post($data) method.
 747       *    this method will be used to set the encoded data.
 748       *
 749       * $data can also be stream (such as file) from which the data will be read.
 750       *
 751       * @param string|resource $data
 752       * @param string $enctype
 753       * @return Zend_Http_Client
 754       */
 755      public function setRawData($data, $enctype = null)
 756      {
 757          $this->raw_post_data = $data;
 758          $this->setEncType($enctype);
 759          if (is_resource($data)) {
 760              // We've got stream data
 761              $stat = @fstat($data);
 762              if($stat) {
 763                  $this->setHeaders(self::CONTENT_LENGTH, $stat['size']);
 764              }
 765          }
 766          return $this;
 767      }
 768  
 769      /**
 770       * Clear all GET and POST parameters
 771       *
 772       * Should be used to reset the request parameters if the client is
 773       * used for several concurrent requests.
 774       *
 775       * clearAll parameter controls if we clean just parameters or also
 776       * headers and last_*
 777       *
 778       * @param bool $clearAll Should all data be cleared?
 779       * @return Zend_Http_Client
 780       */
 781      public function resetParameters($clearAll = false)
 782      {
 783          // Reset parameter data
 784          $this->paramsGet     = array();
 785          $this->paramsPost    = array();
 786          $this->files         = array();
 787          $this->raw_post_data = null;
 788  
 789          if($clearAll) {
 790              $this->headers = array();
 791              $this->last_request = null;
 792              $this->last_response = null;
 793          } else {
 794              // Clear outdated headers
 795              if (isset($this->headers[strtolower(self::CONTENT_TYPE)])) {
 796                  unset($this->headers[strtolower(self::CONTENT_TYPE)]);
 797              }
 798              if (isset($this->headers[strtolower(self::CONTENT_LENGTH)])) {
 799                  unset($this->headers[strtolower(self::CONTENT_LENGTH)]);
 800              }
 801          }
 802  
 803          return $this;
 804      }
 805  
 806      /**
 807       * Get the last HTTP request as string
 808       *
 809       * @return string
 810       */
 811      public function getLastRequest()
 812      {
 813          return $this->last_request;
 814      }
 815  
 816      /**
 817       * Get the last HTTP response received by this client
 818       *
 819       * If $config['storeresponse'] is set to false, or no response was
 820       * stored yet, will return null
 821       *
 822       * @return Zend_Http_Response or null if none
 823       */
 824      public function getLastResponse()
 825      {
 826          return $this->last_response;
 827      }
 828  
 829      /**
 830       * Load the connection adapter
 831       *
 832       * While this method is not called more than one for a client, it is
 833       * seperated from ->request() to preserve logic and readability
 834       *
 835       * @param Zend_Http_Client_Adapter_Interface|string $adapter
 836       * @return null
 837       * @throws Zend_Http_Client_Exception
 838       */
 839      public function setAdapter($adapter)
 840      {
 841          if (is_string($adapter)) {
 842              if (!class_exists($adapter)) {
 843                  try {
 844                      require_once 'Zend/Loader.php';
 845                      Zend_Loader::loadClass($adapter);
 846                  } catch (Zend_Exception $e) {
 847                      /** @see Zend_Http_Client_Exception */
 848                      require_once 'Zend/Http/Client/Exception.php';
 849                      throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}", 0, $e);
 850                  }
 851              }
 852  
 853              $adapter = new $adapter;
 854          }
 855  
 856          if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) {
 857              /** @see Zend_Http_Client_Exception */
 858              require_once 'Zend/Http/Client/Exception.php';
 859              throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter');
 860          }
 861  
 862          $this->adapter = $adapter;
 863          $config = $this->config;
 864          unset($config['adapter']);
 865          $this->adapter->setConfig($config);
 866      }
 867  
 868      /**
 869       * Load the connection adapter
 870       *
 871       * @return Zend_Http_Client_Adapter_Interface $adapter
 872       */
 873      public function getAdapter()
 874      {
 875          return $this->adapter;
 876      }
 877  
 878      /**
 879       * Set streaming for received data
 880       *
 881       * @param string|boolean $streamfile Stream file, true for temp file, false/null for no streaming
 882       * @return Zend_Http_Client
 883       */
 884      public function setStream($streamfile = true)
 885      {
 886          $this->setConfig(array("output_stream" => $streamfile));
 887          return $this;
 888      }
 889  
 890      /**
 891       * Get status of streaming for received data
 892       * @return boolean|string
 893       */
 894      public function getStream()
 895      {
 896          return $this->config["output_stream"];
 897      }
 898  
 899      /**
 900       * Create temporary stream
 901       *
 902       * @return resource
 903       */
 904      protected function _openTempStream()
 905      {
 906          $this->_stream_name = $this->config['output_stream'];
 907          if(!is_string($this->_stream_name)) {
 908              // If name is not given, create temp name
 909              $this->_stream_name = tempnam(isset($this->config['stream_tmp_dir'])?$this->config['stream_tmp_dir']:sys_get_temp_dir(),
 910                   'Zend_Http_Client');
 911          }
 912  
 913          if (false === ($fp = @fopen($this->_stream_name, "w+b"))) {
 914                  if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
 915                      $this->adapter->close();
 916                  }
 917                  require_once 'Zend/Http/Client/Exception.php';
 918                  throw new Zend_Http_Client_Exception("Could not open temp file {$this->_stream_name}");
 919          }
 920          
 921          return $fp;
 922      }
 923      
 924      /**
 925       * Send the HTTP request and return an HTTP response object
 926       *
 927       * @param string $method
 928       * @return Zend_Http_Response
 929       * @throws Zend_Http_Client_Exception
 930       */
 931      public function request($method = null)
 932      {
 933          if (! $this->uri instanceof Zend_Uri_Http) {
 934              /** @see Zend_Http_Client_Exception */
 935              require_once 'Zend/Http/Client/Exception.php';
 936              throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');
 937          }
 938  
 939          if ($method) {
 940              $this->setMethod($method);
 941          }
 942          $this->redirectCounter = 0;
 943          $response = null;
 944  
 945          // Make sure the adapter is loaded
 946          if ($this->adapter == null) {
 947              $this->setAdapter($this->config['adapter']);
 948          }
 949  
 950          // Send the first request. If redirected, continue.
 951          do {
 952              // Clone the URI and add the additional GET parameters to it
 953              $uri = clone $this->uri;
 954              if (! empty($this->paramsGet)) {
 955                  $query = $uri->getQuery();
 956                     if (! empty($query)) {
 957                         $query .= '&';
 958                     }
 959                  $query .= http_build_query($this->paramsGet, null, '&');
 960  
 961                  $uri->setQuery($query);
 962              }
 963  
 964              $body = $this->_prepareBody();
 965              $headers = $this->_prepareHeaders();
 966  
 967              // check that adapter supports streaming before using it
 968              if(is_resource($body) && !($this->adapter instanceof Zend_Http_Client_Adapter_Stream)) {
 969                  /** @see Zend_Http_Client_Exception */
 970                  require_once 'Zend/Http/Client/Exception.php';
 971                  throw new Zend_Http_Client_Exception('Adapter does not support streaming');
 972              }
 973  
 974              // Open the connection, send the request and read the response
 975              $this->adapter->connect($uri->getHost(), $uri->getPort(),
 976                  ($uri->getScheme() == 'https' ? true : false));
 977  
 978              if($this->config['output_stream']) {
 979                  if($this->adapter instanceof Zend_Http_Client_Adapter_Stream) {
 980                      $stream = $this->_openTempStream();
 981                      $this->adapter->setOutputStream($stream);
 982                  } else {
 983                      /** @see Zend_Http_Client_Exception */
 984                      require_once 'Zend/Http/Client/Exception.php';
 985                      throw new Zend_Http_Client_Exception('Adapter does not support streaming');
 986                  }
 987              }
 988  
 989              $this->last_request = $this->adapter->write($this->method,
 990                  $uri, $this->config['httpversion'], $headers, $body);
 991  
 992              $response = $this->adapter->read();
 993              if (! $response) {
 994                  /** @see Zend_Http_Client_Exception */
 995                  require_once 'Zend/Http/Client/Exception.php';
 996                  throw new Zend_Http_Client_Exception('Unable to read response, or response is empty');
 997              }
 998  
 999              if($this->config['output_stream']) {
1000                  rewind($stream);
1001                  // cleanup the adapter
1002                  $this->adapter->setOutputStream(null);
1003                  $response = Zend_Http_Response_Stream::fromStream($response, $stream);
1004                  $response->setStreamName($this->_stream_name);
1005                  if(!is_string($this->config['output_stream'])) {
1006                      // we used temp name, will need to clean up
1007                      $response->setCleanup(true);
1008                  }
1009              } else {
1010                  $response = Zend_Http_Response::fromString($response);
1011              }
1012  
1013              if ($this->config['storeresponse']) {
1014                  $this->last_response = $response;
1015              }
1016  
1017              // Load cookies into cookie jar
1018              if (isset($this->cookiejar)) {
1019                  $this->cookiejar->addCookiesFromResponse($response, $uri);
1020              }
1021  
1022              // If we got redirected, look for the Location header
1023              if ($response->isRedirect() && ($location = $response->getHeader('location'))) {
1024  
1025                  // Check whether we send the exact same request again, or drop the parameters
1026                  // and send a GET request
1027                  if ($response->getStatus() == 303 ||
1028                     ((! $this->config['strictredirects']) && ($response->getStatus() == 302 ||
1029                         $response->getStatus() == 301))) {
1030  
1031                      $this->resetParameters();
1032                      $this->setMethod(self::GET);
1033                  }
1034  
1035                  // If we got a well formed absolute URI
1036                  if (Zend_Uri_Http::check($location)) {
1037                      $this->setHeaders('host', null);
1038                      $this->setUri($location);
1039  
1040                  } else {
1041  
1042                      // Split into path and query and set the query
1043                      if (strpos($location, '?') !== false) {
1044                          list($location, $query) = explode('?', $location, 2);
1045                      } else {
1046                          $query = '';
1047                      }
1048                      $this->uri->setQuery($query);
1049  
1050                      // Else, if we got just an absolute path, set it
1051                      if(strpos($location, '/') === 0) {
1052                          $this->uri->setPath($location);
1053  
1054                          // Else, assume we have a relative path
1055                      } else {
1056                          // Get the current path directory, removing any trailing slashes
1057                          $path = $this->uri->getPath();
1058                          $path = rtrim(substr($path, 0, strrpos($path, '/')), "/");
1059                          $this->uri->setPath($path . '/' . $location);
1060                      }
1061                  }
1062                  ++$this->redirectCounter;
1063  
1064              } else {
1065                  // If we didn't get any location, stop redirecting
1066                  break;
1067              }
1068  
1069          } while ($this->redirectCounter < $this->config['maxredirects']);
1070  
1071          return $response;
1072      }
1073  
1074      /**
1075       * Prepare the request headers
1076       *
1077       * @return array
1078       */
1079      protected function _prepareHeaders()
1080      {
1081          $headers = array();
1082  
1083          // Set the host header
1084          if (! isset($this->headers['host'])) {
1085              $host = $this->uri->getHost();
1086  
1087              // If the port is not default, add it
1088              if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) ||
1089                    ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) {
1090                  $host .= ':' . $this->uri->getPort();
1091              }
1092  
1093              $headers[] = "Host: {$host}";
1094          }
1095  
1096          // Set the connection header
1097          if (! isset($this->headers['connection'])) {
1098              if (! $this->config['keepalive']) {
1099                  $headers[] = "Connection: close";
1100              }
1101          }
1102  
1103          // Set the Accept-encoding header if not set - depending on whether
1104          // zlib is available or not.
1105          if (! isset($this->headers['accept-encoding'])) {
1106              if (function_exists('gzinflate')) {
1107                  $headers[] = 'Accept-encoding: gzip, deflate';
1108              } else {
1109                  $headers[] = 'Accept-encoding: identity';
1110              }
1111          }
1112  
1113          // Set the Content-Type header
1114          if ($this->method == self::POST &&
1115             (! isset($this->headers[strtolower(self::CONTENT_TYPE)]) && isset($this->enctype))) {
1116  
1117              $headers[] = self::CONTENT_TYPE . ': ' . $this->enctype;
1118          }
1119  
1120          // Set the user agent header
1121          if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) {
1122              $headers[] = "User-Agent: {$this->config['useragent']}";
1123          }
1124  
1125          // Set HTTP authentication if needed
1126          if (is_array($this->auth)) {
1127              $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']);
1128              $headers[] = "Authorization: {$auth}";
1129          }
1130  
1131          // Load cookies from cookie jar
1132          if (isset($this->cookiejar)) {
1133              $cookstr = $this->cookiejar->getMatchingCookies($this->uri,
1134                  true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT);
1135  
1136              if ($cookstr) {
1137                  $headers[] = "Cookie: {$cookstr}";
1138              }
1139          }
1140  
1141          // Add all other user defined headers
1142          foreach ($this->headers as $header) {
1143              list($name, $value) = $header;
1144              if (is_array($value)) {
1145                  $value = implode(', ', $value);
1146              }
1147  
1148              $headers[] = "$name: $value";
1149          }
1150  
1151          return $headers;
1152      }
1153  
1154      /**
1155       * Prepare the request body (for POST and PUT requests)
1156       *
1157       * @return string
1158       * @throws Zend_Http_Client_Exception
1159       */
1160      protected function _prepareBody()
1161      {
1162          // According to RFC2616, a TRACE request should not have a body.
1163          if ($this->method == self::TRACE) {
1164              return '';
1165          }
1166  
1167          if (isset($this->raw_post_data) && is_resource($this->raw_post_data)) {
1168              return $this->raw_post_data;
1169          }
1170          // If mbstring overloads substr and strlen functions, we have to
1171          // override it's internal encoding
1172          if (function_exists('mb_internal_encoding') &&
1173             ((int) ini_get('mbstring.func_overload')) & 2) {
1174  
1175              $mbIntEnc = mb_internal_encoding();
1176              mb_internal_encoding('ASCII');
1177          }
1178  
1179          // If we have raw_post_data set, just use it as the body.
1180          if (isset($this->raw_post_data)) {
1181              $this->setHeaders(self::CONTENT_LENGTH, strlen($this->raw_post_data));
1182              if (isset($mbIntEnc)) {
1183                  mb_internal_encoding($mbIntEnc);
1184              }
1185  
1186              return $this->raw_post_data;
1187          }
1188  
1189          $body = '';
1190  
1191          // If we have files to upload, force enctype to multipart/form-data
1192          if (count ($this->files) > 0) {
1193              $this->setEncType(self::ENC_FORMDATA);
1194          }
1195  
1196          // If we have POST parameters or files, encode and add them to the body
1197          if (count($this->paramsPost) > 0 || count($this->files) > 0) {
1198              switch($this->enctype) {
1199                  case self::ENC_FORMDATA:
1200                      // Encode body as multipart/form-data
1201                      $boundary = '---ZENDHTTPCLIENT-' . md5(microtime());
1202                      $this->setHeaders(self::CONTENT_TYPE, self::ENC_FORMDATA . "; boundary={$boundary}");
1203  
1204                      // Get POST parameters and encode them
1205                      $params = self::_flattenParametersArray($this->paramsPost);
1206                      foreach ($params as $pp) {
1207                          $body .= self::encodeFormData($boundary, $pp[0], $pp[1]);
1208                      }
1209  
1210                      // Encode files
1211                      foreach ($this->files as $file) {
1212                          $fhead = array(self::CONTENT_TYPE => $file['ctype']);
1213                          $body .= self::encodeFormData($boundary, $file['formname'], $file['data'], $file['filename'], $fhead);
1214                      }
1215  
1216                      $body .= "--{$boundary}--\r\n";
1217                      break;
1218  
1219                  case self::ENC_URLENCODED:
1220                      // Encode body as application/x-www-form-urlencoded
1221                      $this->setHeaders(self::CONTENT_TYPE, self::ENC_URLENCODED);
1222                      $body = http_build_query($this->paramsPost, '', '&');
1223                      break;
1224  
1225                  default:
1226                      if (isset($mbIntEnc)) {
1227                          mb_internal_encoding($mbIntEnc);
1228                      }
1229  
1230                      /** @see Zend_Http_Client_Exception */
1231                      require_once 'Zend/Http/Client/Exception.php';
1232                      throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}' automatically." .
1233                          " Please use Zend_Http_Client::setRawData to send this kind of content.");
1234                      break;
1235              }
1236          }
1237  
1238          // Set the Content-Length if we have a body or if request is POST/PUT
1239          if ($body || $this->method == self::POST || $this->method == self::PUT) {
1240              $this->setHeaders(self::CONTENT_LENGTH, strlen($body));
1241          }
1242  
1243          if (isset($mbIntEnc)) {
1244              mb_internal_encoding($mbIntEnc);
1245          }
1246  
1247          return $body;
1248      }
1249  
1250      /**
1251       * Helper method that gets a possibly multi-level parameters array (get or
1252       * post) and flattens it.
1253       *
1254       * The method returns an array of (key, value) pairs (because keys are not
1255       * necessarily unique. If one of the parameters in as array, it will also
1256       * add a [] suffix to the key.
1257       *
1258       * This method is deprecated since Zend Framework 1.9 in favour of
1259       * self::_flattenParametersArray() and will be dropped in 2.0
1260       *
1261       * @deprecated since 1.9
1262       *
1263       * @param  array $parray    The parameters array
1264       * @param  bool  $urlencode Whether to urlencode the name and value
1265       * @return array
1266       */
1267      protected function _getParametersRecursive($parray, $urlencode = false)
1268      {
1269          // Issue a deprecated notice
1270          trigger_error("The " .  __METHOD__ . " method is deprecated and will be dropped in 2.0.",
1271              E_USER_NOTICE);
1272  
1273          if (! is_array($parray)) {
1274              return $parray;
1275          }
1276          $parameters = array();
1277  
1278          foreach ($parray as $name => $value) {
1279              if ($urlencode) {
1280                  $name = urlencode($name);
1281              }
1282  
1283              // If $value is an array, iterate over it
1284              if (is_array($value)) {
1285                  $name .= ($urlencode ? '%5B%5D' : '[]');
1286                  foreach ($value as $subval) {
1287                      if ($urlencode) {
1288                          $subval = urlencode($subval);
1289                      }
1290                      $parameters[] = array($name, $subval);
1291                  }
1292              } else {
1293                  if ($urlencode) {
1294                      $value = urlencode($value);
1295                  }
1296                  $parameters[] = array($name, $value);
1297              }
1298          }
1299  
1300          return $parameters;
1301      }
1302  
1303      /**
1304       * Attempt to detect the MIME type of a file using available extensions
1305       *
1306       * This method will try to detect the MIME type of a file. If the fileinfo
1307       * extension is available, it will be used. If not, the mime_magic
1308       * extension which is deprected but is still available in many PHP setups
1309       * will be tried.
1310       *
1311       * If neither extension is available, the default application/octet-stream
1312       * MIME type will be returned
1313       *
1314       * @param  string $file File path
1315       * @return string       MIME type
1316       */
1317      protected function _detectFileMimeType($file)
1318      {
1319          $type = null;
1320  
1321          // First try with fileinfo functions
1322          if (function_exists('finfo_open')) {
1323              if (self::$_fileInfoDb === null) {
1324                  self::$_fileInfoDb = @finfo_open(FILEINFO_MIME);
1325              }
1326  
1327              if (self::$_fileInfoDb) {
1328                  $type = finfo_file(self::$_fileInfoDb, $file);
1329              }
1330  
1331          } elseif (function_exists('mime_content_type')) {
1332              $type = mime_content_type($file);
1333          }
1334  
1335          // Fallback to the default application/octet-stream
1336          if (! $type) {
1337              $type = 'application/octet-stream';
1338          }
1339  
1340          return $type;
1341      }
1342  
1343      /**
1344       * Encode data to a multipart/form-data part suitable for a POST request.
1345       *
1346       * @param string $boundary
1347       * @param string $name
1348       * @param mixed $value
1349       * @param string $filename
1350       * @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" => "binary")
1351       * @return string
1352       */
1353      public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array()) {
1354          $ret = "--{$boundary}\r\n" .
1355              'Content-Disposition: form-data; name="' . $name .'"';
1356  
1357          if ($filename) {
1358              $ret .= '; filename="' . $filename . '"';
1359          }
1360          $ret .= "\r\n";
1361  
1362          foreach ($headers as $hname => $hvalue) {
1363              $ret .= "{$hname}: {$hvalue}\r\n";
1364          }
1365          $ret .= "\r\n";
1366  
1367          $ret .= "{$value}\r\n";
1368  
1369          return $ret;
1370      }
1371  
1372      /**
1373       * Create a HTTP authentication "Authorization:" header according to the
1374       * specified user, password and authentication method.
1375       *
1376       * @see http://www.faqs.org/rfcs/rfc2617.html
1377       * @param string $user
1378       * @param string $password
1379       * @param string $type
1380       * @return string
1381       * @throws Zend_Http_Client_Exception
1382       */
1383      public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC)
1384      {
1385          $authHeader = null;
1386  
1387          switch ($type) {
1388              case self::AUTH_BASIC:
1389                  // In basic authentication, the user name cannot contain ":"
1390                  if (strpos($user, ':') !== false) {
1391                      /** @see Zend_Http_Client_Exception */
1392                      require_once 'Zend/Http/Client/Exception.php';
1393                      throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP authentication");
1394                  }
1395  
1396                  $authHeader = 'Basic ' . base64_encode($user . ':' . $password);
1397                  break;
1398  
1399              //case self::AUTH_DIGEST:
1400                  /**
1401                   * @todo Implement digest authentication
1402                   */
1403              //    break;
1404  
1405              default:
1406                  /** @see Zend_Http_Client_Exception */
1407                  require_once 'Zend/Http/Client/Exception.php';
1408                  throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'");
1409          }
1410  
1411          return $authHeader;
1412      }
1413  
1414      /**
1415       * Convert an array of parameters into a flat array of (key, value) pairs
1416       *
1417       * Will flatten a potentially multi-dimentional array of parameters (such
1418       * as POST parameters) into a flat array of (key, value) paris. In case
1419       * of multi-dimentional arrays, square brackets ([]) will be added to the
1420       * key to indicate an array.
1421       *
1422       * @since  1.9
1423       *
1424       * @param  array  $parray
1425       * @param  string $prefix
1426       * @return array
1427       */
1428      static protected function _flattenParametersArray($parray, $prefix = null)
1429      {
1430          if (! is_array($parray)) {
1431              return $parray;
1432          }
1433  
1434          $parameters = array();
1435  
1436          foreach($parray as $name => $value) {
1437  
1438              // Calculate array key
1439              if ($prefix) {
1440                  if (is_int($name)) {
1441                      $key = $prefix . '[]';
1442                  } else {
1443                      $key = $prefix . "[$name]";
1444                  }
1445              } else {
1446                  $key = $name;
1447              }
1448  
1449              if (is_array($value)) {
1450                  $parameters = array_merge($parameters, self::_flattenParametersArray($value, $key));
1451  
1452              } else {
1453                  $parameters[] = array($key, $value);
1454              }
1455          }
1456  
1457          return $parameters;
1458      }
1459  
1460  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1