File indexing completed on 2025-01-19 05:21:14

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Http
0018  * @subpackage Client
0019  * @version    $Id$
0020  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0021  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0022  */
0023 
0024 /**
0025  * @see Zend_Loader
0026  */
0027 // require_once 'Zend/Loader.php';
0028 
0029 
0030 /**
0031  * @see Zend_Uri
0032  */
0033 // require_once 'Zend/Uri.php';
0034 
0035 
0036 /**
0037  * @see Zend_Http_Client_Adapter_Interface
0038  */
0039 // require_once 'Zend/Http/Client/Adapter/Interface.php';
0040 
0041 
0042 /**
0043  * @see Zend_Http_Header_HeaderValue
0044  */
0045 // require_once 'Zend/Http/Header/HeaderValue.php';
0046 
0047 
0048 /**
0049  * @see Zend_Http_Response
0050  */
0051 // require_once 'Zend/Http/Response.php';
0052 
0053 /**
0054  * @see Zend_Http_Response_Stream
0055  */
0056 // require_once 'Zend/Http/Response/Stream.php';
0057 
0058 /**
0059  * Zend_Http_Client is an implementation of an HTTP client in PHP. The client
0060  * supports basic features like sending different HTTP requests and handling
0061  * redirections, as well as more advanced features like proxy settings, HTTP
0062  * authentication and cookie persistence (using a Zend_Http_CookieJar object)
0063  *
0064  * @todo Implement proxy settings
0065  * @category   Zend
0066  * @package    Zend_Http
0067  * @subpackage Client
0068  * @throws     Zend_Http_Client_Exception
0069  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0070  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0071  */
0072 class Zend_Http_Client
0073 {
0074     /**
0075      * HTTP request methods
0076      */
0077     const GET     = 'GET';
0078     const POST    = 'POST';
0079     const PUT     = 'PUT';
0080     const HEAD    = 'HEAD';
0081     const DELETE  = 'DELETE';
0082     const TRACE   = 'TRACE';
0083     const OPTIONS = 'OPTIONS';
0084     const CONNECT = 'CONNECT';
0085     const MERGE   = 'MERGE';
0086     const PATCH   = 'PATCH';
0087 
0088     /**
0089      * Supported HTTP Authentication methods
0090      */
0091     const AUTH_BASIC = 'basic';
0092     //const AUTH_DIGEST = 'digest'; <-- not implemented yet
0093 
0094     /**
0095      * HTTP protocol versions
0096      */
0097     const HTTP_1 = '1.1';
0098     const HTTP_0 = '1.0';
0099 
0100     /**
0101      * Content attributes
0102      */
0103     const CONTENT_TYPE   = 'Content-Type';
0104     const CONTENT_LENGTH = 'Content-Length';
0105 
0106     /**
0107      * POST data encoding methods
0108      */
0109     const ENC_URLENCODED = 'application/x-www-form-urlencoded';
0110     const ENC_FORMDATA   = 'multipart/form-data';
0111 
0112     /**
0113      * Value types for Body key/value pairs
0114      */
0115     const VTYPE_SCALAR  = 'SCALAR';
0116     const VTYPE_FILE    = 'FILE';
0117 
0118     /**
0119      * Configuration array, set using the constructor or using ::setConfig()
0120      *
0121      * @var array
0122      */
0123     protected $config = array(
0124         'maxredirects'    => 5,
0125         'strictredirects' => false,
0126         'useragent'       => 'Zend_Http_Client',
0127         'timeout'         => 10,
0128         'adapter'         => 'Zend_Http_Client_Adapter_Socket',
0129         'httpversion'     => self::HTTP_1,
0130         'keepalive'       => false,
0131         'storeresponse'   => true,
0132         'strict'          => true,
0133         'output_stream'   => false,
0134         'encodecookies'   => true,
0135         'rfc3986_strict'  => false
0136     );
0137 
0138     /**
0139      * The adapter used to perform the actual connection to the server
0140      *
0141      * @var Zend_Http_Client_Adapter_Interface
0142      */
0143     protected $adapter = null;
0144 
0145     /**
0146      * Request URI
0147      *
0148      * @var Zend_Uri_Http
0149      */
0150     protected $uri = null;
0151 
0152     /**
0153      * Associative array of request headers
0154      *
0155      * @var array
0156      */
0157     protected $headers = array();
0158 
0159     /**
0160      * HTTP request method
0161      *
0162      * @var string
0163      */
0164     protected $method = self::GET;
0165 
0166     /**
0167      * Associative array of GET parameters
0168      *
0169      * @var array
0170      */
0171     protected $paramsGet = array();
0172 
0173     /**
0174      * Associative array of POST parameters
0175      *
0176      * @var array
0177      */
0178     protected $paramsPost = array();
0179 
0180     /**
0181      * Request body content type (for POST requests)
0182      *
0183      * @var string
0184      */
0185     protected $enctype = null;
0186 
0187     /**
0188      * The raw post data to send. Could be set by setRawData($data, $enctype).
0189      *
0190      * @var string
0191      */
0192     protected $raw_post_data = null;
0193 
0194     /**
0195      * HTTP Authentication settings
0196      *
0197      * Expected to be an associative array with this structure:
0198      * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic')
0199      * Where 'type' should be one of the supported authentication types (see the AUTH_*
0200      * constants), for example 'basic' or 'digest'.
0201      *
0202      * If null, no authentication will be used.
0203      *
0204      * @var array|null
0205      */
0206     protected $auth;
0207 
0208     /**
0209      * File upload arrays (used in POST requests)
0210      *
0211      * An associative array, where each element is of the format:
0212      *   'name' => array('filename.txt', 'text/plain', 'This is the actual file contents')
0213      *
0214      * @var array
0215      */
0216     protected $files = array();
0217 
0218     /**
0219      * Ordered list of keys from key/value pair data to include in body
0220      *
0221      * An associative array, where each element is of the format:
0222      *   '<field name>' => VTYPE_SCALAR | VTYPE_FILE
0223      *
0224      * @var array
0225      */
0226     protected $body_field_order = array();
0227 
0228     /**
0229      * The client's cookie jar
0230      *
0231      * @var Zend_Http_CookieJar
0232      */
0233     protected $cookiejar = null;
0234 
0235     /**
0236      * The last HTTP request sent by the client, as string
0237      *
0238      * @var string
0239      */
0240     protected $last_request = null;
0241 
0242     /**
0243      * The last HTTP response received by the client
0244      *
0245      * @var Zend_Http_Response
0246      */
0247     protected $last_response = null;
0248 
0249     /**
0250      * Redirection counter
0251      *
0252      * @var int
0253      */
0254     protected $redirectCounter = 0;
0255 
0256     /**
0257      * Status for unmasking GET array params
0258      *
0259      * @var boolean
0260      */
0261     protected $_unmaskStatus = false;
0262 
0263     /**
0264      * Status if the http_build_query function escapes brackets
0265      *
0266      * @var boolean
0267      */
0268     protected $_queryBracketsEscaped = true;
0269 
0270     /**
0271      * Fileinfo magic database resource
0272      *
0273      * This variable is populated the first time _detectFileMimeType is called
0274      * and is then reused on every call to this method
0275      *
0276      * @var resource
0277      */
0278     protected static $_fileInfoDb = null;
0279 
0280     /**
0281      * Constructor method. Will create a new HTTP client. Accepts the target
0282      * URL and optionally configuration array.
0283      *
0284      * @param Zend_Uri_Http|string $uri
0285      * @param array $config Configuration key-value pairs.
0286      */
0287     public function __construct($uri = null, $config = null)
0288     {
0289         if ($uri !== null) {
0290             $this->setUri($uri);
0291         }
0292         if ($config !== null) {
0293             $this->setConfig($config);
0294         }
0295 
0296         $this->_queryBracketsEscaped = version_compare(phpversion(), '5.1.3', '>=');
0297     }
0298 
0299     /**
0300      * Set the URI for the next request
0301      *
0302      * @param  Zend_Uri_Http|string $uri
0303      * @return Zend_Http_Client
0304      * @throws Zend_Http_Client_Exception
0305      */
0306     public function setUri($uri)
0307     {
0308         if ($uri instanceof Zend_Uri_Http) {
0309             // clone the URI in order to keep the passed parameter constant
0310             $uri = clone $uri;
0311         } elseif (is_string($uri)) {
0312             $uri = Zend_Uri::factory($uri);
0313         }
0314 
0315         if (!$uri instanceof Zend_Uri_Http) {
0316             /** @see Zend_Http_Client_Exception */
0317             // require_once 'Zend/Http/Client/Exception.php';
0318             throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.');
0319         }
0320 
0321         // Set auth if username and password has been specified in the uri
0322         if ($uri->getUsername() && $uri->getPassword()) {
0323             $this->setAuth($uri->getUsername(), $uri->getPassword());
0324         }
0325 
0326         // We have no ports, set the defaults
0327         if (! $uri->getPort()) {
0328             $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80));
0329         }
0330 
0331         $this->uri = $uri;
0332 
0333         return $this;
0334     }
0335 
0336     /**
0337      * Get the URI for the next request
0338      *
0339      * @param boolean $as_string If true, will return the URI as a string
0340      * @return Zend_Uri_Http|string
0341      */
0342     public function getUri($as_string = false)
0343     {
0344         if ($as_string && $this->uri instanceof Zend_Uri_Http) {
0345             return $this->uri->__toString();
0346         } else {
0347             return $this->uri;
0348         }
0349     }
0350 
0351     /**
0352      * Set configuration parameters for this HTTP client
0353      *
0354      * @param  Zend_Config | array $config
0355      * @return Zend_Http_Client
0356      * @throws Zend_Http_Client_Exception
0357      */
0358     public function setConfig($config = array())
0359     {
0360         if ($config instanceof Zend_Config) {
0361             $config = $config->toArray();
0362 
0363         } elseif (! is_array($config)) {
0364             /** @see Zend_Http_Client_Exception */
0365             // require_once 'Zend/Http/Client/Exception.php';
0366             throw new Zend_Http_Client_Exception('Array or Zend_Config object expected, got ' . gettype($config));
0367         }
0368 
0369         foreach ($config as $k => $v) {
0370             $this->config[strtolower($k)] = $v;
0371         }
0372 
0373         // Pass configuration options to the adapter if it exists
0374         if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
0375             $this->adapter->setConfig($config);
0376         }
0377 
0378         return $this;
0379     }
0380 
0381     /**
0382      * Set the next request's method
0383      *
0384      * Validated the passed method and sets it. If we have files set for
0385      * POST requests, and the new method is not POST, the files are silently
0386      * dropped.
0387      *
0388      * @param string $method
0389      * @return Zend_Http_Client
0390      * @throws Zend_Http_Client_Exception
0391      */
0392     public function setMethod($method = self::GET)
0393     {
0394         if (! preg_match('/^[^\x00-\x1f\x7f-\xff\(\)<>@,;:\\\\"\/\[\]\?={}\s]+$/', $method)) {
0395             // require_once 'Zend/Http/Client/Exception.php';
0396             throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method.");
0397         }
0398 
0399         if (($method == self::POST
0400                 || $method == self::PUT
0401                 || $method == self::DELETE
0402                 || $method == self::PATCH
0403                 || $method == self::OPTIONS)
0404             && $this->enctype === null
0405         ) {
0406             $this->setEncType(self::ENC_URLENCODED);
0407         }
0408 
0409         $this->method = $method;
0410 
0411         return $this;
0412     }
0413 
0414     /**
0415      * Set one or more request headers
0416      *
0417      * This function can be used in several ways to set the client's request
0418      * headers:
0419      * 1. By providing two parameters: $name as the header to set (e.g. 'Host')
0420      *    and $value as it's value (e.g. 'www.example.com').
0421      * 2. By providing a single header string as the only parameter
0422      *    e.g. 'Host: www.example.com'
0423      * 3. By providing an array of headers as the first parameter
0424      *    e.g. array('host' => 'www.example.com', 'x-foo: bar'). In This case
0425      *    the function will call itself recursively for each array item.
0426      *
0427      * @param string|array $name Header name, full header string ('Header: value')
0428      *     or an array of headers
0429      * @param mixed $value Header value or null
0430      * @return Zend_Http_Client
0431      * @throws Zend_Http_Client_Exception
0432      */
0433     public function setHeaders($name, $value = null)
0434     {
0435         // If we got an array, go recursive!
0436         if (is_array($name)) {
0437             foreach ($name as $k => $v) {
0438                 if (is_string($k)) {
0439                     $this->setHeaders($k, $v);
0440                     continue;
0441                 }
0442                 $this->setHeaders($v, null);
0443             }
0444             return $this;
0445         }
0446 
0447         // Check if $name needs to be split
0448         if ($value === null && (strpos($name, ':') > 0)) {
0449             list($name, $value) = explode(':', $name, 2);
0450         }
0451 
0452         // Make sure the name is valid if we are in strict mode
0453         if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) {
0454             // require_once 'Zend/Http/Client/Exception.php';
0455             throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name");
0456         }
0457 
0458         $normalized_name = strtolower($name);
0459 
0460         // If $value is null or false, unset the header
0461         if ($value === null || $value === false) {
0462             unset($this->headers[$normalized_name]);
0463             return $this;
0464         }
0465 
0466         // Validate value
0467         $this->_validateHeaderValue($value);
0468 
0469         // Header names are stored lowercase internally.
0470         if (is_string($value)) {
0471             $value = trim($value);
0472         }
0473         $this->headers[$normalized_name] = array($name, $value);
0474 
0475         return $this;
0476     }
0477 
0478     /**
0479      * Get the value of a specific header
0480      *
0481      * Note that if the header has more than one value, an array
0482      * will be returned.
0483      *
0484      * @param string $key
0485      * @return string|array|null The header value or null if it is not set
0486      */
0487     public function getHeader($key)
0488     {
0489         $key = strtolower($key);
0490         if (isset($this->headers[$key])) {
0491             return $this->headers[$key][1];
0492         } else {
0493             return null;
0494         }
0495     }
0496 
0497     /**
0498      * Set a GET parameter for the request. Wrapper around _setParameter
0499      *
0500      * @param string|array $name
0501      * @param string $value
0502      * @return Zend_Http_Client
0503      */
0504     public function setParameterGet($name, $value = null)
0505     {
0506         if (is_array($name)) {
0507             foreach ($name as $k => $v)
0508                 $this->_setParameter('GET', $k, $v);
0509         } else {
0510             $this->_setParameter('GET', $name, $value);
0511         }
0512 
0513         return $this;
0514     }
0515 
0516     /**
0517      * Set a POST parameter for the request. Wrapper around _setParameter
0518      *
0519      * @param string|array $name
0520      * @param string $value
0521      * @return Zend_Http_Client
0522      */
0523     public function setParameterPost($name, $value = null)
0524     {
0525         if (is_array($name)) {
0526             foreach ($name as $k => $v)
0527                 $this->_setParameter('POST', $k, $v);
0528         } else {
0529             $this->_setParameter('POST', $name, $value);
0530         }
0531 
0532         return $this;
0533     }
0534 
0535     /**
0536      * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost
0537      *
0538      * @param string $type GET or POST
0539      * @param string $name
0540      * @param string $value
0541      * @return null
0542      */
0543     protected function _setParameter($type, $name, $value)
0544     {
0545         $parray = array();
0546         $type = strtolower($type);
0547         switch ($type) {
0548             case 'get':
0549                 $parray = &$this->paramsGet;
0550                 break;
0551             case 'post':
0552                 $parray = &$this->paramsPost;
0553                 if ( $value === null ) {
0554                     if (isset($this->body_field_order[$name]))
0555                         unset($this->body_field_order[$name]);
0556                 } else {
0557                     $this->body_field_order[$name] = self::VTYPE_SCALAR;
0558                 }
0559                 break;
0560         }
0561 
0562         if ($value === null) {
0563             if (isset($parray[$name])) unset($parray[$name]);
0564         } else {
0565             $parray[$name] = $value;
0566         }
0567     }
0568 
0569     /**
0570      * Get the number of redirections done on the last request
0571      *
0572      * @return int
0573      */
0574     public function getRedirectionsCount()
0575     {
0576         return $this->redirectCounter;
0577     }
0578 
0579     /**
0580      * Set HTTP authentication parameters
0581      *
0582      * $type should be one of the supported types - see the self::AUTH_*
0583      * constants.
0584      *
0585      * To enable authentication:
0586      * <code>
0587      * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC);
0588      * </code>
0589      *
0590      * To disable authentication:
0591      * <code>
0592      * $this->setAuth(false);
0593      * </code>
0594      *
0595      * @see http://www.faqs.org/rfcs/rfc2617.html
0596      * @param string|false $user User name or false disable authentication
0597      * @param string $password Password
0598      * @param string $type Authentication type
0599      * @return Zend_Http_Client
0600      * @throws Zend_Http_Client_Exception
0601      */
0602     public function setAuth($user, $password = '', $type = self::AUTH_BASIC)
0603     {
0604         // If we got false or null, disable authentication
0605         if ($user === false || $user === null) {
0606             $this->auth = null;
0607 
0608             // Clear the auth information in the uri instance as well
0609             if ($this->uri instanceof Zend_Uri_Http) {
0610                 $this->getUri()->setUsername('');
0611                 $this->getUri()->setPassword('');
0612             }
0613         // Else, set up authentication
0614         } else {
0615             // Check we got a proper authentication type
0616             if (! defined('self::AUTH_' . strtoupper($type))) {
0617                 /** @see Zend_Http_Client_Exception */
0618                 // require_once 'Zend/Http/Client/Exception.php';
0619                 throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'");
0620             }
0621 
0622             $this->auth = array(
0623                 'user' => (string) $user,
0624                 'password' => (string) $password,
0625                 'type' => $type
0626             );
0627         }
0628 
0629         return $this;
0630     }
0631 
0632     /**
0633      * Set the HTTP client's cookie jar.
0634      *
0635      * A cookie jar is an object that holds and maintains cookies across HTTP requests
0636      * and responses.
0637      *
0638      * @param Zend_Http_CookieJar|boolean $cookiejar Existing cookiejar object, true to create a new one, false to disable
0639      * @return Zend_Http_Client
0640      * @throws Zend_Http_Client_Exception
0641      */
0642     public function setCookieJar($cookiejar = true)
0643     {
0644         Zend_Loader::loadClass('Zend_Http_CookieJar');
0645 
0646         if ($cookiejar instanceof Zend_Http_CookieJar) {
0647             $this->cookiejar = $cookiejar;
0648         } elseif ($cookiejar === true) {
0649             $this->cookiejar = new Zend_Http_CookieJar();
0650         } elseif (! $cookiejar) {
0651             $this->cookiejar = null;
0652         } else {
0653             /** @see Zend_Http_Client_Exception */
0654             // require_once 'Zend/Http/Client/Exception.php';
0655             throw new Zend_Http_Client_Exception('Invalid parameter type passed as CookieJar');
0656         }
0657 
0658         return $this;
0659     }
0660 
0661     /**
0662      * Return the current cookie jar or null if none.
0663      *
0664      * @return Zend_Http_CookieJar|null
0665      */
0666     public function getCookieJar()
0667     {
0668         return $this->cookiejar;
0669     }
0670 
0671     /**
0672      * Add a cookie to the request. If the client has no Cookie Jar, the cookies
0673      * will be added directly to the headers array as "Cookie" headers.
0674      *
0675      * @param Zend_Http_Cookie|string $cookie
0676      * @param string|null $value If "cookie" is a string, this is the cookie value.
0677      * @return Zend_Http_Client
0678      * @throws Zend_Http_Client_Exception
0679      */
0680     public function setCookie($cookie, $value = null)
0681     {
0682         Zend_Loader::loadClass('Zend_Http_Cookie');
0683 
0684         if (is_array($cookie)) {
0685             foreach ($cookie as $c => $v) {
0686                 if (is_string($c)) {
0687                     $this->setCookie($c, $v);
0688                 } else {
0689                     $this->setCookie($v);
0690                 }
0691             }
0692 
0693             return $this;
0694         }
0695 
0696         if ($value !== null && $this->config['encodecookies']) {
0697             $value = urlencode($value);
0698         }
0699 
0700         if (isset($this->cookiejar)) {
0701             if ($cookie instanceof Zend_Http_Cookie) {
0702                 $this->cookiejar->addCookie($cookie);
0703             } elseif (is_string($cookie) && $value !== null) {
0704                 $cookie = Zend_Http_Cookie::fromString("{$cookie}={$value}",
0705                                                        $this->uri,
0706                                                        $this->config['encodecookies']);
0707                 $this->cookiejar->addCookie($cookie);
0708             }
0709         } else {
0710             if ($cookie instanceof Zend_Http_Cookie) {
0711                 $name = $cookie->getName();
0712                 $value = $cookie->getValue();
0713                 $cookie = $name;
0714             }
0715 
0716             if (preg_match("/[=,; \t\r\n\013\014]/", $cookie)) {
0717                 /** @see Zend_Http_Client_Exception */
0718                 // require_once 'Zend/Http/Client/Exception.php';
0719                 throw new Zend_Http_Client_Exception("Cookie name cannot contain these characters: =,; \t\r\n\013\014 ({$cookie})");
0720             }
0721 
0722             $value = addslashes($value);
0723 
0724             if (! isset($this->headers['cookie'])) {
0725                 $this->headers['cookie'] = array('Cookie', '');
0726             }
0727             $this->headers['cookie'][1] .= $cookie . '=' . $value . '; ';
0728         }
0729 
0730         return $this;
0731     }
0732 
0733     /**
0734      * Set a file to upload (using a POST request)
0735      *
0736      * Can be used in two ways:
0737      *
0738      * 1. $data is null (default): $filename is treated as the name if a local file which
0739      *    will be read and sent. Will try to guess the content type using mime_content_type().
0740      * 2. $data is set - $filename is sent as the file name, but $data is sent as the file
0741      *    contents and no file is read from the file system. In this case, you need to
0742      *    manually set the Content-Type ($ctype) or it will default to
0743      *    application/octet-stream.
0744      *
0745      * @param string $filename Name of file to upload, or name to save as
0746      * @param string $formname Name of form element to send as
0747      * @param string $data Data to send (if null, $filename is read and sent)
0748      * @param string $ctype Content type to use (if $data is set and $ctype is
0749      *     null, will be application/octet-stream)
0750      * @return Zend_Http_Client
0751      * @throws Zend_Http_Client_Exception
0752      */
0753     public function setFileUpload($filename, $formname, $data = null, $ctype = null)
0754     {
0755         if ($data === null) {
0756             if (($data = @file_get_contents($filename)) === false) {
0757                 /** @see Zend_Http_Client_Exception */
0758                 // require_once 'Zend/Http/Client/Exception.php';
0759                 throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload");
0760             }
0761 
0762             if (! $ctype) {
0763                 $ctype = $this->_detectFileMimeType($filename);
0764             }
0765         }
0766 
0767         // Force enctype to multipart/form-data
0768         $this->setEncType(self::ENC_FORMDATA);
0769 
0770         $this->files[] = array(
0771             'formname' => $formname,
0772             'filename' => basename($filename),
0773             'ctype'    => $ctype,
0774             'data'     => $data
0775         );
0776 
0777         $this->body_field_order[$formname] = self::VTYPE_FILE;
0778 
0779         return $this;
0780     }
0781 
0782     /**
0783      * Set the encoding type for POST data
0784      *
0785      * @param string $enctype
0786      * @return Zend_Http_Client
0787      */
0788     public function setEncType($enctype = self::ENC_URLENCODED)
0789     {
0790         $this->enctype = $enctype;
0791 
0792         return $this;
0793     }
0794 
0795     /**
0796      * Set the raw (already encoded) POST data.
0797      *
0798      * This function is here for two reasons:
0799      * 1. For advanced user who would like to set their own data, already encoded
0800      * 2. For backwards compatibilty: If someone uses the old post($data) method.
0801      *    this method will be used to set the encoded data.
0802      *
0803      * $data can also be stream (such as file) from which the data will be read.
0804      *
0805      * @param string|resource $data
0806      * @param string $enctype
0807      * @return Zend_Http_Client
0808      */
0809     public function setRawData($data, $enctype = null)
0810     {
0811         $this->raw_post_data = $data;
0812         $this->setEncType($enctype);
0813         if (is_resource($data)) {
0814             // We've got stream data
0815             $stat = @fstat($data);
0816             if($stat) {
0817                 $this->setHeaders(self::CONTENT_LENGTH, $stat['size']);
0818             }
0819         }
0820         return $this;
0821     }
0822 
0823     /**
0824      * Set the unmask feature for GET parameters as array
0825      *
0826      * Example:
0827      * foo%5B0%5D=a&foo%5B1%5D=b
0828      * becomes
0829      * foo=a&foo=b
0830      *
0831      * This is usefull for some services
0832      *
0833      * @param boolean $status
0834      * @return Zend_Http_Client
0835      */
0836     public function setUnmaskStatus($status = true)
0837     {
0838         $this->_unmaskStatus = (BOOL)$status;
0839         return $this;
0840     }
0841 
0842     /**
0843      * Returns the currently configured unmask status
0844      *
0845      * @return boolean
0846      */
0847     public function getUnmaskStatus()
0848     {
0849         return $this->_unmaskStatus;
0850     }
0851 
0852     /**
0853      * Clear all GET and POST parameters
0854      *
0855      * Should be used to reset the request parameters if the client is
0856      * used for several concurrent requests.
0857      *
0858      * clearAll parameter controls if we clean just parameters or also
0859      * headers and last_*
0860      *
0861      * @param bool $clearAll Should all data be cleared?
0862      * @return Zend_Http_Client
0863      */
0864     public function resetParameters($clearAll = false)
0865     {
0866         // Reset parameter data
0867         $this->paramsGet     = array();
0868         $this->paramsPost    = array();
0869         $this->files         = array();
0870         $this->raw_post_data = null;
0871         $this->enctype       = null;
0872 
0873         if($clearAll) {
0874             $this->headers = array();
0875             $this->last_request = null;
0876             $this->last_response = null;
0877         } else {
0878             // Clear outdated headers
0879             if (isset($this->headers[strtolower(self::CONTENT_TYPE)])) {
0880                 unset($this->headers[strtolower(self::CONTENT_TYPE)]);
0881             }
0882             if (isset($this->headers[strtolower(self::CONTENT_LENGTH)])) {
0883                 unset($this->headers[strtolower(self::CONTENT_LENGTH)]);
0884             }
0885         }
0886 
0887         return $this;
0888     }
0889 
0890     /**
0891      * Get the last HTTP request as string
0892      *
0893      * @return string
0894      */
0895     public function getLastRequest()
0896     {
0897         return $this->last_request;
0898     }
0899 
0900     /**
0901      * Get the last HTTP response received by this client
0902      *
0903      * If $config['storeresponse'] is set to false, or no response was
0904      * stored yet, will return null
0905      *
0906      * @return Zend_Http_Response or null if none
0907      */
0908     public function getLastResponse()
0909     {
0910         return $this->last_response;
0911     }
0912 
0913     /**
0914      * Load the connection adapter
0915      *
0916      * While this method is not called more than one for a client, it is
0917      * seperated from ->request() to preserve logic and readability
0918      *
0919      * @param Zend_Http_Client_Adapter_Interface|string $adapter
0920      * @return null
0921      * @throws Zend_Http_Client_Exception
0922      */
0923     public function setAdapter($adapter)
0924     {
0925         if (is_string($adapter)) {
0926             try {
0927                 Zend_Loader::loadClass($adapter);
0928             } catch (Zend_Exception $e) {
0929                 /** @see Zend_Http_Client_Exception */
0930                 // require_once 'Zend/Http/Client/Exception.php';
0931                 throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}", 0, $e);
0932             }
0933 
0934             $adapter = new $adapter;
0935         }
0936 
0937         if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) {
0938             /** @see Zend_Http_Client_Exception */
0939             // require_once 'Zend/Http/Client/Exception.php';
0940             throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter');
0941         }
0942 
0943         $this->adapter = $adapter;
0944         $config = $this->config;
0945         unset($config['adapter']);
0946         $this->adapter->setConfig($config);
0947     }
0948 
0949     /**
0950      * Load the connection adapter
0951      *
0952      * @return Zend_Http_Client_Adapter_Interface $adapter
0953      */
0954     public function getAdapter()
0955     {
0956         if (null === $this->adapter) {
0957             $this->setAdapter($this->config['adapter']);
0958         }
0959 
0960         return $this->adapter;
0961     }
0962 
0963     /**
0964      * Set streaming for received data
0965      *
0966      * @param string|boolean $streamfile Stream file, true for temp file, false/null for no streaming
0967      * @return Zend_Http_Client
0968      */
0969     public function setStream($streamfile = true)
0970     {
0971         $this->setConfig(array("output_stream" => $streamfile));
0972         return $this;
0973     }
0974 
0975     /**
0976      * Get status of streaming for received data
0977      * @return boolean|string
0978      */
0979     public function getStream()
0980     {
0981         return $this->config["output_stream"];
0982     }
0983 
0984     /**
0985      * Create temporary stream
0986      *
0987      * @return resource
0988      */
0989     protected function _openTempStream()
0990     {
0991         $this->_stream_name = $this->config['output_stream'];
0992         if(!is_string($this->_stream_name)) {
0993             // If name is not given, create temp name
0994             $this->_stream_name = tempnam(isset($this->config['stream_tmp_dir'])?$this->config['stream_tmp_dir']:sys_get_temp_dir(),
0995                  'Zend_Http_Client');
0996         }
0997 
0998         if (false === ($fp = @fopen($this->_stream_name, "w+b"))) {
0999                 if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) {
1000                     $this->adapter->close();
1001                 }
1002                 // require_once 'Zend/Http/Client/Exception.php';
1003                 throw new Zend_Http_Client_Exception("Could not open temp file {$this->_stream_name}");
1004         }
1005 
1006         return $fp;
1007     }
1008 
1009     /**
1010      * Send the HTTP request and return an HTTP response object
1011      *
1012      * @param string $method
1013      * @return Zend_Http_Response
1014      * @throws Zend_Http_Client_Exception
1015      */
1016     public function request($method = null)
1017     {
1018         if (! $this->uri instanceof Zend_Uri_Http) {
1019             /** @see Zend_Http_Client_Exception */
1020             // require_once 'Zend/Http/Client/Exception.php';
1021             throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');
1022         }
1023 
1024         if ($method) {
1025             $this->setMethod($method);
1026         }
1027         $this->redirectCounter = 0;
1028         $response = null;
1029 
1030         // Make sure the adapter is loaded
1031         if ($this->adapter == null) {
1032             $this->setAdapter($this->config['adapter']);
1033         }
1034 
1035         // Send the first request. If redirected, continue.
1036         do {
1037             // Clone the URI and add the additional GET parameters to it
1038             $uri = clone $this->uri;
1039             if (! empty($this->paramsGet)) {
1040                 $query = $uri->getQuery();
1041                    if (! empty($query)) {
1042                        $query .= '&';
1043                    }
1044                 $query .= http_build_query($this->paramsGet, null, '&');
1045                 if ($this->config['rfc3986_strict']) {
1046                     $query = str_replace('+', '%20', $query);
1047                 }
1048 
1049                 // @see ZF-11671 to unmask for some services to foo=val1&foo=val2
1050                 if ($this->getUnmaskStatus()) {
1051                     if ($this->_queryBracketsEscaped) {
1052                         $query = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $query);
1053                     } else {
1054                         $query = preg_replace('/\\[(?:[0-9]|[1-9][0-9]+)\\]=/', '=', $query);
1055                     }
1056                 }
1057 
1058                 $uri->setQuery($query);
1059             }
1060 
1061             $body = $this->_prepareBody();
1062             $headers = $this->_prepareHeaders();
1063 
1064             // check that adapter supports streaming before using it
1065             if(is_resource($body) && !($this->adapter instanceof Zend_Http_Client_Adapter_Stream)) {
1066                 /** @see Zend_Http_Client_Exception */
1067                 // require_once 'Zend/Http/Client/Exception.php';
1068                 throw new Zend_Http_Client_Exception('Adapter does not support streaming');
1069             }
1070 
1071             // Open the connection, send the request and read the response
1072             $this->adapter->connect($uri->getHost(), $uri->getPort(),
1073                 ($uri->getScheme() == 'https' ? true : false));
1074 
1075             if($this->config['output_stream']) {
1076                 if($this->adapter instanceof Zend_Http_Client_Adapter_Stream) {
1077                     $stream = $this->_openTempStream();
1078                     $this->adapter->setOutputStream($stream);
1079                 } else {
1080                     /** @see Zend_Http_Client_Exception */
1081                     // require_once 'Zend/Http/Client/Exception.php';
1082                     throw new Zend_Http_Client_Exception('Adapter does not support streaming');
1083                 }
1084             }
1085 
1086             $this->last_request = $this->adapter->write($this->method,
1087                 $uri, $this->config['httpversion'], $headers, $body);
1088 
1089             $response = $this->adapter->read();
1090             if (! $response) {
1091                 /** @see Zend_Http_Client_Exception */
1092                 // require_once 'Zend/Http/Client/Exception.php';
1093                 throw new Zend_Http_Client_Exception('Unable to read response, or response is empty');
1094             }
1095 
1096             if($this->config['output_stream']) {
1097                 $streamMetaData = stream_get_meta_data($stream);
1098                 if ($streamMetaData['seekable']) {
1099                     rewind($stream);
1100                 }
1101                 // cleanup the adapter
1102                 $this->adapter->setOutputStream(null);
1103                 $response = Zend_Http_Response_Stream::fromStream($response, $stream);
1104                 $response->setStreamName($this->_stream_name);
1105                 if(!is_string($this->config['output_stream'])) {
1106                     // we used temp name, will need to clean up
1107                     $response->setCleanup(true);
1108                 }
1109             } else {
1110                 $response = Zend_Http_Response::fromString($response);
1111             }
1112 
1113             if ($this->config['storeresponse']) {
1114                 $this->last_response = $response;
1115             }
1116 
1117             // Load cookies into cookie jar
1118             if (isset($this->cookiejar)) {
1119                 $this->cookiejar->addCookiesFromResponse($response, $uri, $this->config['encodecookies']);
1120             }
1121 
1122             // If we got redirected, look for the Location header
1123             if ($response->isRedirect() && ($location = $response->getHeader('location'))) {
1124 
1125                 // Avoid problems with buggy servers that add whitespace at the
1126                 // end of some headers (See ZF-11283)
1127                 $location = trim($location);
1128 
1129                 // Check whether we send the exact same request again, or drop the parameters
1130                 // and send a GET request
1131                 if ($response->getStatus() == 303 ||
1132                    ((! $this->config['strictredirects']) && ($response->getStatus() == 302 ||
1133                        $response->getStatus() == 301))) {
1134 
1135                     $this->resetParameters();
1136                     $this->setMethod(self::GET);
1137                 }
1138 
1139                 // If we got a well formed absolute URI
1140                 if (($scheme = substr($location, 0, 6)) && ($scheme == 'http:/' || $scheme == 'https:')) {
1141                     $this->setHeaders('host', null);
1142                     $this->setUri($location);
1143 
1144                 } else {
1145 
1146                     // Split into path and query and set the query
1147                     if (strpos($location, '?') !== false) {
1148                         list($location, $query) = explode('?', $location, 2);
1149                     } else {
1150                         $query = '';
1151                     }
1152                     $this->uri->setQuery($query);
1153 
1154                     // Else, if we got just an absolute path, set it
1155                     if(strpos($location, '/') === 0) {
1156                         $this->uri->setPath($location);
1157 
1158                         // Else, assume we have a relative path
1159                     } else {
1160                         // Get the current path directory, removing any trailing slashes
1161                         $path = $this->uri->getPath();
1162                         $path = rtrim(substr($path, 0, strrpos($path, '/')), "/");
1163                         $this->uri->setPath($path . '/' . $location);
1164                     }
1165                 }
1166                 ++$this->redirectCounter;
1167 
1168             } else {
1169                 // If we didn't get any location, stop redirecting
1170                 break;
1171             }
1172 
1173         } while ($this->redirectCounter < $this->config['maxredirects']);
1174 
1175         return $response;
1176     }
1177 
1178     /**
1179      * Prepare the request headers
1180      *
1181      * @return array
1182      */
1183     protected function _prepareHeaders()
1184     {
1185         $headers = array();
1186 
1187         // Set the host header
1188         if (! isset($this->headers['host'])) {
1189             $host = $this->uri->getHost();
1190 
1191             // If the port is not default, add it
1192             if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) ||
1193                   ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) {
1194                 $host .= ':' . $this->uri->getPort();
1195             }
1196 
1197             $headers[] = "Host: {$host}";
1198         }
1199 
1200         // Set the connection header
1201         if (! isset($this->headers['connection'])) {
1202             if (! $this->config['keepalive']) {
1203                 $headers[] = "Connection: close";
1204             }
1205         }
1206 
1207         // Set the Accept-encoding header if not set - depending on whether
1208         // zlib is available or not.
1209         if (! isset($this->headers['accept-encoding'])) {
1210             if (function_exists('gzinflate')) {
1211                 $headers[] = 'Accept-encoding: gzip, deflate';
1212             } else {
1213                 $headers[] = 'Accept-encoding: identity';
1214             }
1215         }
1216 
1217         // Set the Content-Type header
1218         if (($this->method == self::POST || $this->method == self::PUT) &&
1219            (! isset($this->headers[strtolower(self::CONTENT_TYPE)]) && isset($this->enctype))) {
1220 
1221             $headers[] = self::CONTENT_TYPE . ': ' . $this->enctype;
1222         }
1223 
1224         // Set the user agent header
1225         if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) {
1226             $headers[] = "User-Agent: {$this->config['useragent']}";
1227         }
1228 
1229         // Set HTTP authentication if needed
1230         if (is_array($this->auth)) {
1231             $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']);
1232             $headers[] = "Authorization: {$auth}";
1233         }
1234 
1235         // Load cookies from cookie jar
1236         if (isset($this->cookiejar)) {
1237             $cookstr = $this->cookiejar->getMatchingCookies($this->uri,
1238                 true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT);
1239 
1240             if ($cookstr) {
1241                 $headers[] = "Cookie: {$cookstr}";
1242             }
1243         }
1244 
1245         // Add all other user defined headers
1246         foreach ($this->headers as $header) {
1247             list($name, $value) = $header;
1248             if (is_array($value)) {
1249                 $value = implode(', ', $value);
1250             }
1251 
1252             $headers[] = "$name: $value";
1253         }
1254 
1255         return $headers;
1256     }
1257 
1258     /**
1259      * Prepare the request body (for POST and PUT requests)
1260      *
1261      * @return string
1262      * @throws Zend_Http_Client_Exception
1263      */
1264     protected function _prepareBody()
1265     {
1266         // According to RFC2616, a TRACE request should not have a body.
1267         if ($this->method == self::TRACE) {
1268             return '';
1269         }
1270 
1271         if (isset($this->raw_post_data) && is_resource($this->raw_post_data)) {
1272             return $this->raw_post_data;
1273         }
1274         // If mbstring overloads substr and strlen functions, we have to
1275         // override it's internal encoding
1276         if (function_exists('mb_internal_encoding') &&
1277            ((int) ini_get('mbstring.func_overload')) & 2) {
1278 
1279             $mbIntEnc = mb_internal_encoding();
1280             mb_internal_encoding('ASCII');
1281         }
1282 
1283         // If we have raw_post_data set, just use it as the body.
1284         if (isset($this->raw_post_data)) {
1285             $this->setHeaders(self::CONTENT_LENGTH, strlen($this->raw_post_data));
1286             if (isset($mbIntEnc)) {
1287                 mb_internal_encoding($mbIntEnc);
1288             }
1289 
1290             return $this->raw_post_data;
1291         }
1292 
1293         $body = '';
1294 
1295         // If we have files to upload, force enctype to multipart/form-data
1296         if (count ($this->files) > 0) {
1297             $this->setEncType(self::ENC_FORMDATA);
1298         }
1299 
1300         // If we have POST parameters or files, encode and add them to the body
1301         if (count($this->paramsPost) > 0 || count($this->files) > 0) {
1302             switch($this->enctype) {
1303                 case self::ENC_FORMDATA:
1304                     // Encode body as multipart/form-data
1305                     $boundary = '---ZENDHTTPCLIENT-' . md5(microtime());
1306                     $this->setHeaders(self::CONTENT_TYPE, self::ENC_FORMDATA . "; boundary={$boundary}");
1307 
1308                     // Encode all files and POST vars in the order they were given
1309                     foreach ($this->body_field_order as $fieldName=>$fieldType) {
1310                         switch ($fieldType) {
1311                             case self::VTYPE_FILE:
1312                                 foreach ($this->files as $file) {
1313                                     if ($file['formname']===$fieldName) {
1314                                         $fhead = array(self::CONTENT_TYPE => $file['ctype']);
1315                                         $body .= self::encodeFormData($boundary, $file['formname'], $file['data'], $file['filename'], $fhead);
1316                                     }
1317                                 }
1318                                 break;
1319                             case self::VTYPE_SCALAR:
1320                                 if (isset($this->paramsPost[$fieldName])) {
1321                                     if (is_array($this->paramsPost[$fieldName])) {
1322                                         $flattened = self::_flattenParametersArray($this->paramsPost[$fieldName], $fieldName);
1323                                         foreach ($flattened as $pp) {
1324                                             $body .= self::encodeFormData($boundary, $pp[0], $pp[1]);
1325                                         }
1326                                     } else {
1327                                         $body .= self::encodeFormData($boundary, $fieldName, $this->paramsPost[$fieldName]);
1328                                     }
1329                                 }
1330                                 break;
1331                         }
1332                     }
1333 
1334                     $body .= "--{$boundary}--\r\n";
1335                     break;
1336 
1337                 case self::ENC_URLENCODED:
1338                     // Encode body as application/x-www-form-urlencoded
1339                     $this->setHeaders(self::CONTENT_TYPE, self::ENC_URLENCODED);
1340                     $body = http_build_query($this->paramsPost, '', '&');
1341                     break;
1342 
1343                 default:
1344                     if (isset($mbIntEnc)) {
1345                         mb_internal_encoding($mbIntEnc);
1346                     }
1347 
1348                     /** @see Zend_Http_Client_Exception */
1349                     // require_once 'Zend/Http/Client/Exception.php';
1350                     throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}' automatically." .
1351                         " Please use Zend_Http_Client::setRawData to send this kind of content.");
1352                     break;
1353             }
1354         }
1355 
1356         // Set the Content-Length if we have a body or if request is POST/PUT
1357         if ($body || $this->method == self::POST || $this->method == self::PUT) {
1358             $this->setHeaders(self::CONTENT_LENGTH, strlen($body));
1359         }
1360 
1361         if (isset($mbIntEnc)) {
1362             mb_internal_encoding($mbIntEnc);
1363         }
1364 
1365         return $body;
1366     }
1367 
1368     /**
1369      * Helper method that gets a possibly multi-level parameters array (get or
1370      * post) and flattens it.
1371      *
1372      * The method returns an array of (key, value) pairs (because keys are not
1373      * necessarily unique. If one of the parameters in as array, it will also
1374      * add a [] suffix to the key.
1375      *
1376      * This method is deprecated since Zend Framework 1.9 in favour of
1377      * self::_flattenParametersArray() and will be dropped in 2.0
1378      *
1379      * @deprecated since 1.9
1380      *
1381      * @param  array $parray    The parameters array
1382      * @param  bool  $urlencode Whether to urlencode the name and value
1383      * @return array
1384      */
1385     protected function _getParametersRecursive($parray, $urlencode = false)
1386     {
1387         // Issue a deprecated notice
1388         trigger_error("The " .  __METHOD__ . " method is deprecated and will be dropped in 2.0.",
1389             E_USER_NOTICE);
1390 
1391         if (! is_array($parray)) {
1392             return $parray;
1393         }
1394         $parameters = array();
1395 
1396         foreach ($parray as $name => $value) {
1397             if ($urlencode) {
1398                 $name = urlencode($name);
1399             }
1400 
1401             // If $value is an array, iterate over it
1402             if (is_array($value)) {
1403                 $name .= ($urlencode ? '%5B%5D' : '[]');
1404                 foreach ($value as $subval) {
1405                     if ($urlencode) {
1406                         $subval = urlencode($subval);
1407                     }
1408                     $parameters[] = array($name, $subval);
1409                 }
1410             } else {
1411                 if ($urlencode) {
1412                     $value = urlencode($value);
1413                 }
1414                 $parameters[] = array($name, $value);
1415             }
1416         }
1417 
1418         return $parameters;
1419     }
1420 
1421     /**
1422      * Attempt to detect the MIME type of a file using available extensions
1423      *
1424      * This method will try to detect the MIME type of a file. If the fileinfo
1425      * extension is available, it will be used. If not, the mime_magic
1426      * extension which is deprected but is still available in many PHP setups
1427      * will be tried.
1428      *
1429      * If neither extension is available, the default application/octet-stream
1430      * MIME type will be returned
1431      *
1432      * @param  string $file File path
1433      * @return string       MIME type
1434      */
1435     protected function _detectFileMimeType($file)
1436     {
1437         $type = null;
1438 
1439         // First try with fileinfo functions
1440         if (function_exists('finfo_open')) {
1441             if (self::$_fileInfoDb === null) {
1442                 self::$_fileInfoDb = @finfo_open(FILEINFO_MIME);
1443             }
1444 
1445             if (self::$_fileInfoDb) {
1446                 $type = finfo_file(self::$_fileInfoDb, $file);
1447             }
1448 
1449         } elseif (function_exists('mime_content_type')) {
1450             $type = mime_content_type($file);
1451         }
1452 
1453         // Fallback to the default application/octet-stream
1454         if (! $type) {
1455             $type = 'application/octet-stream';
1456         }
1457 
1458         return $type;
1459     }
1460 
1461     /**
1462      * Encode data to a multipart/form-data part suitable for a POST request.
1463      *
1464      * @param string $boundary
1465      * @param string $name
1466      * @param mixed $value
1467      * @param string $filename
1468      * @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" => "binary")
1469      * @return string
1470      */
1471     public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array())
1472     {
1473         $ret = "--{$boundary}\r\n" .
1474             'Content-Disposition: form-data; name="' . $name .'"';
1475 
1476         if ($filename) {
1477             $ret .= '; filename="' . $filename . '"';
1478         }
1479         $ret .= "\r\n";
1480 
1481         foreach ($headers as $hname => $hvalue) {
1482             $ret .= "{$hname}: {$hvalue}\r\n";
1483         }
1484         $ret .= "\r\n";
1485 
1486         $ret .= "{$value}\r\n";
1487 
1488         return $ret;
1489     }
1490 
1491     /**
1492      * Create a HTTP authentication "Authorization:" header according to the
1493      * specified user, password and authentication method.
1494      *
1495      * @see http://www.faqs.org/rfcs/rfc2617.html
1496      * @param string $user
1497      * @param string $password
1498      * @param string $type
1499      * @return string
1500      * @throws Zend_Http_Client_Exception
1501      */
1502     public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC)
1503     {
1504         $authHeader = null;
1505 
1506         switch ($type) {
1507             case self::AUTH_BASIC:
1508                 // In basic authentication, the user name cannot contain ":"
1509                 if (strpos($user, ':') !== false) {
1510                     /** @see Zend_Http_Client_Exception */
1511                     // require_once 'Zend/Http/Client/Exception.php';
1512                     throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP authentication");
1513                 }
1514 
1515                 $authHeader = 'Basic ' . base64_encode($user . ':' . $password);
1516                 break;
1517 
1518             //case self::AUTH_DIGEST:
1519                 /**
1520                  * @todo Implement digest authentication
1521                  */
1522             //    break;
1523 
1524             default:
1525                 /** @see Zend_Http_Client_Exception */
1526                 // require_once 'Zend/Http/Client/Exception.php';
1527                 throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'");
1528         }
1529 
1530         return $authHeader;
1531     }
1532 
1533     /**
1534      * Convert an array of parameters into a flat array of (key, value) pairs
1535      *
1536      * Will flatten a potentially multi-dimentional array of parameters (such
1537      * as POST parameters) into a flat array of (key, value) paris. In case
1538      * of multi-dimentional arrays, square brackets ([]) will be added to the
1539      * key to indicate an array.
1540      *
1541      * @since  1.9
1542      *
1543      * @param  array  $parray
1544      * @param  string $prefix
1545      * @return array
1546      */
1547     protected static function _flattenParametersArray($parray, $prefix = null)
1548     {
1549         if (! is_array($parray)) {
1550             return $parray;
1551         }
1552 
1553         $parameters = array();
1554 
1555         foreach($parray as $name => $value) {
1556 
1557             // Calculate array key
1558             if ($prefix) {
1559                 if (is_int($name)) {
1560                     $key = $prefix . '[]';
1561                 } else {
1562                     $key = $prefix . "[$name]";
1563                 }
1564             } else {
1565                 $key = $name;
1566             }
1567 
1568             if (is_array($value)) {
1569                 $parameters = array_merge($parameters, self::_flattenParametersArray($value, $key));
1570 
1571             } else {
1572                 $parameters[] = array($key, $value);
1573             }
1574         }
1575 
1576         return $parameters;
1577     }
1578 
1579     /**
1580      * Ensure a header value is valid per RFC 7230.
1581      *
1582      * @see http://tools.ietf.org/html/rfc7230#section-3.2
1583      * @param string|object|array $value
1584      * @param bool $recurse
1585      */
1586     protected function _validateHeaderValue($value, $recurse = true)
1587     {
1588         if (is_array($value) && $recurse) {
1589             foreach ($value as $v) {
1590                 $this->_validateHeaderValue($v, false);
1591             }
1592             return;
1593         }
1594 
1595         // Cast integers and floats to strings for purposes of header representation.
1596         if (is_int($value) || is_float($value)) {
1597             $value = (string) $value;
1598         }
1599 
1600         if (! is_string($value) && (! is_object($value) || ! method_exists($value, '__toString'))) {
1601             // require_once 'Zend/Http/Exception.php';
1602             throw new Zend_Http_Exception('Invalid header value detected');
1603         }
1604 
1605         Zend_Http_Header_HeaderValue::assertValid($value);
1606     }
1607 }