You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ch...@apache.org on 2008/05/08 01:48:22 UTC
svn commit: r654331 [3/6] - in /incubator/shindig/trunk/php: ./
src/common/Zend/ src/common/Zend/Feed/ src/common/Zend/Feed/Builder/
src/common/Zend/Feed/Builder/Header/ src/common/Zend/Feed/Entry/
src/common/Zend/Http/ src/common/Zend/Http/Client/ src...
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client.php Wed May 7 16:48:15 2008
@@ -0,0 +1,1086 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client
+ * @version $Id: Client.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Loader.php';
+require_once 'Zend/Uri.php';
+require_once 'Zend/Http/Client/Adapter/Interface.php';
+require_once 'Zend/Http/Response.php';
+
+/**
+ * Zend_Http_Client is an implemetation of an HTTP client in PHP. The client
+ * supports basic features like sending different HTTP requests and handling
+ * redirections, as well as more advanced features like proxy settings, HTTP
+ * authentication and cookie persistance (using a Zend_Http_CookieJar object)
+ *
+ * @todo Implement proxy settings
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client
+ * @throws Zend_Http_Client_Exception
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client
+{
+ /**
+ * HTTP request methods
+ */
+ const GET = 'GET';
+ const POST = 'POST';
+ const PUT = 'PUT';
+ const HEAD = 'HEAD';
+ const DELETE = 'DELETE';
+ const TRACE = 'TRACE';
+ const OPTIONS = 'OPTIONS';
+ const CONNECT = 'CONNECT';
+
+ /**
+ * Supported HTTP Authentication methods
+ */
+ const AUTH_BASIC = 'basic';
+ //const AUTH_DIGEST = 'digest'; <-- not implemented yet
+
+ /**
+ * HTTP protocol versions
+ */
+ const HTTP_1 = '1.1';
+ const HTTP_0 = '1.0';
+
+ /**
+ * POST data encoding methods
+ */
+ const ENC_URLENCODED = 'application/x-www-form-urlencoded';
+ const ENC_FORMDATA = 'multipart/form-data';
+
+ /**
+ * Configuration array, set using the constructor or using ::setConfig()
+ *
+ * @var unknown_type
+ */
+ protected $config = array(
+ 'maxredirects' => 5,
+ 'strictredirects' => false,
+ 'useragent' => 'Zend_Http_Client',
+ 'timeout' => 10,
+ 'adapter' => 'Zend_Http_Client_Adapter_Socket',
+ 'httpversion' => self::HTTP_1,
+ 'keepalive' => false,
+ 'storeresponse' => true,
+ 'strict' => true
+ );
+
+ /**
+ * The adapter used to preform the actual connection to the server
+ *
+ * @var Zend_Http_Client_Adapter_Interface
+ */
+ protected $adapter = null;
+
+ /**
+ * Request URI
+ *
+ * @var Zend_Uri_Http
+ */
+ protected $uri;
+
+ /**
+ * Associative array of request headers
+ *
+ * @var array
+ */
+ protected $headers = array();
+
+ /**
+ * HTTP request method
+ *
+ * @var string
+ */
+ protected $method = self::GET;
+
+ /**
+ * Associative array of GET parameters
+ *
+ * @var array
+ */
+ protected $paramsGet = array();
+
+ /**
+ * Assiciative array of POST parameters
+ *
+ * @var array
+ */
+ protected $paramsPost = array();
+
+ /**
+ * Request body content type (for POST requests)
+ *
+ * @var string
+ */
+ protected $enctype = null;
+
+ /**
+ * The raw post data to send. Could be set by setRawData($data, $enctype).
+ *
+ * @var string
+ */
+ protected $raw_post_data = null;
+
+ /**
+ * HTTP Authentication settings
+ *
+ * Expected to be an associative array with this structure:
+ * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic')
+ * Where 'type' should be one of the supported authentication types (see the AUTH_*
+ * constants), for example 'basic' or 'digest'.
+ *
+ * If null, no authentication will be used.
+ *
+ * @var array|null
+ */
+ protected $auth;
+
+ /**
+ * File upload arrays (used in POST requests)
+ *
+ * An associative array, where each element is of the format:
+ * 'name' => array('filename.txt', 'text/plain', 'This is the actual file contents')
+ *
+ * @var array
+ */
+ protected $files = array();
+
+ /**
+ * The client's cookie jar
+ *
+ * @var Zend_Http_CookieJar
+ */
+ protected $cookiejar = null;
+
+ /**
+ * The last HTTP request sent by the client, as string
+ *
+ * @var string
+ */
+ protected $last_request = null;
+
+ /**
+ * The last HTTP response received by the client
+ *
+ * @var Zend_Http_Response
+ */
+ protected $last_response = null;
+
+ /**
+ * Redirection counter
+ *
+ * @var int
+ */
+ protected $redirectCounter = 0;
+
+ /**
+ * Contructor method. Will create a new HTTP client. Accepts the target
+ * URL and optionally and array of headers.
+ *
+ * @param Zend_Uri_Http|string $uri
+ * @param array $headers Optional request headers to set
+ */
+ public function __construct($uri = null, $config = null)
+ {
+ if ($uri !== null) $this->setUri($uri);
+ if ($config !== null) $this->setConfig($config);
+ }
+
+ /**
+ * Set the URI for the next request
+ *
+ * @param Zend_Uri_Http|string $uri
+ * @return Zend_Http_Client
+ * @throws Zend_Http_Client_Exception
+ */
+ public function setUri($uri)
+ {
+ if (is_string($uri)) {
+ $uri = Zend_Uri::factory($uri);
+ }
+
+ if (!$uri instanceof Zend_Uri_Http) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.');
+ }
+
+ // We have no ports, set the defaults
+ if (! $uri->getPort()) {
+ $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80));
+ }
+
+ $this->uri = $uri;
+
+ return $this;
+ }
+
+ /**
+ * Get the URI for the next request
+ *
+ * @param boolean $as_string If true, will return the URI as a string
+ * @return Zend_Uri_Http|string
+ */
+ public function getUri($as_string = false)
+ {
+ if ($as_string && $this->uri instanceof Zend_Uri_Http) {
+ return $this->uri->__toString();
+ } else {
+ return $this->uri;
+ }
+ }
+
+ /**
+ * Set configuration parameters for this HTTP client
+ *
+ * @param array $config
+ * @return Zend_Http_Client
+ */
+ public function setConfig($config = array())
+ {
+ if (! is_array($config)) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception('Expected array parameter, given ' . gettype($config));
+ }
+
+ foreach ($config as $k => $v)
+ $this->config[strtolower($k)] = $v;
+
+ return $this;
+ }
+
+ /**
+ * Set the next request's method
+ *
+ * Validated the passed method and sets it. If we have files set for
+ * POST requests, and the new method is not POST, the files are silently
+ * dropped.
+ *
+ * @param string $method
+ * @return Zend_Http_Client
+ */
+ public function setMethod($method = self::GET)
+ {
+ if (! preg_match('/^[A-Za-z_]+$/', $method)) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method.");
+ }
+
+ if ($method == self::POST && $this->enctype === null)
+ $this->setEncType(self::ENC_URLENCODED);
+
+ $this->method = $method;
+
+ return $this;
+ }
+
+ /**
+ * Set one or more request headers
+ *
+ * This function can be used in several ways to set the client's request
+ * headers:
+ * 1. By providing two parameters: $name as the header to set (eg. 'Host')
+ * and $value as it's value (eg. 'www.example.com').
+ * 2. By providing a single header string as the only parameter
+ * eg. 'Host: www.example.com'
+ * 3. By providing an array of headers as the first parameter
+ * eg. array('host' => 'www.example.com', 'x-foo: bar'). In This case
+ * the function will call itself recursively for each array item.
+ *
+ * @param string|array $name Header name, full header string ('Header: value')
+ * or an array of headers
+ * @param mixed $value Header value or null
+ * @return Zend_Http_Client
+ */
+ public function setHeaders($name, $value = null)
+ {
+ // If we got an array, go recusive!
+ if (is_array($name)) {
+ foreach ($name as $k => $v) {
+ if (is_string($k)) {
+ $this->setHeaders($k, $v);
+ } else {
+ $this->setHeaders($v, null);
+ }
+ }
+ } else {
+ // Check if $name needs to be split
+ if ($value === null && (strpos($name, ':') > 0))
+ list($name, $value) = explode(':', $name, 2);
+
+ // Make sure the name is valid if we are in strict mode
+ if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name");
+ }
+
+ $normalized_name = strtolower($name);
+
+ // If $value is null or false, unset the header
+ if ($value === null || $value === false) {
+ unset($this->headers[$normalized_name]);
+
+ // Else, set the header
+ } else {
+ // Header names are storred lowercase internally.
+ if (is_string($value)) $value = trim($value);
+ $this->headers[$normalized_name] = array($name, $value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the value of a specific header
+ *
+ * Note that if the header has more than one value, an array
+ * will be returned.
+ *
+ * @param unknown_type $key
+ * @return string|array|null The header value or null if it is not set
+ */
+ public function getHeader($key)
+ {
+ $key = strtolower($key);
+ if (isset($this->headers[$key])) {
+ return $this->headers[$key][1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set a GET parameter for the request. Wrapper around _setParameter
+ *
+ * @param string|array $name
+ * @param string $value
+ * @return Zend_Http_Client
+ */
+ public function setParameterGet($name, $value = null)
+ {
+ if (is_array($name)) {
+ foreach ($name as $k => $v)
+ $this->_setParameter('GET', $k, $v);
+ } else {
+ $this->_setParameter('GET', $name, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set a POST parameter for the request. Wrapper around _setParameter
+ *
+ * @param string|array $name
+ * @param string $value
+ * @return Zend_Http_Client
+ */
+ public function setParameterPost($name, $value = null)
+ {
+ if (is_array($name)) {
+ foreach ($name as $k => $v)
+ $this->_setParameter('POST', $k, $v);
+ } else {
+ $this->_setParameter('POST', $name, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost
+ *
+ * @param string $type GET or POST
+ * @param string $name
+ * @param string $value
+ */
+ protected function _setParameter($type, $name, $value)
+ {
+ $parray = array();
+ $type = strtolower($type);
+ switch ($type) {
+ case 'get':
+ $parray = &$this->paramsGet;
+ break;
+ case 'post':
+ $parray = &$this->paramsPost;
+ break;
+ }
+
+ if ($value === null) {
+ if (isset($parray[$name])) unset($parray[$name]);
+ } else {
+ $parray[$name] = $value;
+ }
+ }
+
+ /**
+ * Get the number of redirections done on the last request
+ *
+ * @return int
+ */
+ public function getRedirectionsCount()
+ {
+ return $this->redirectCounter;
+ }
+
+ /**
+ * Set HTTP authentication parameters
+ *
+ * $type should be one of the supported types - see the self::AUTH_*
+ * constants.
+ *
+ * To enable authentication:
+ * <code>
+ * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC);
+ * </code>
+ *
+ * To disable authentication:
+ * <code>
+ * $this->setAuth(false);
+ * </code>
+ *
+ * @see http://www.faqs.org/rfcs/rfc2617.html
+ * @param string|false $user User name or false disable authentication
+ * @param string $password Password
+ * @param string $type Authentication type
+ * @return Zend_Http_Client
+ */
+ public function setAuth($user, $password = '', $type = self::AUTH_BASIC)
+ {
+ // If we got false or null, disable authentication
+ if ($user === false || $user === null) {
+ $this->auth = null;
+
+ // Else, set up authentication
+ } else {
+ // Check we got a proper authentication type
+ if (! defined('self::AUTH_' . strtoupper($type))) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'");
+ }
+
+ $this->auth = array(
+ 'user' => (string) $user,
+ 'password' => (string) $password,
+ 'type' => $type
+ );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the HTTP client's cookie jar.
+ *
+ * A cookie jar is an object that holds and maintains cookies across HTTP requests
+ * and responses.
+ *
+ * @param Zend_Http_CookieJar|boolean $cookiejar Existing cookiejar object, true to create a new one, false to disable
+ * @return Zend_Http_Client
+ */
+ public function setCookieJar($cookiejar = true)
+ {
+ if (! class_exists('Zend_Http_CookieJar'))
+ require_once 'Zend/Http/CookieJar.php';
+
+ if ($cookiejar instanceof Zend_Http_CookieJar) {
+ $this->cookiejar = $cookiejar;
+ } elseif ($cookiejar === true) {
+ $this->cookiejar = new Zend_Http_CookieJar();
+ } elseif (! $cookiejar) {
+ $this->cookiejar = null;
+ } else {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception('Invalid parameter type passed as CookieJar');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return the current cookie jar or null if none.
+ *
+ * @return Zend_Http_CookieJar|null
+ */
+ public function getCookieJar()
+ {
+ return $this->cookiejar;
+ }
+
+ /**
+ * Add a cookie to the request. If the client has no Cookie Jar, the cookies
+ * will be added directly to the headers array as "Cookie" headers.
+ *
+ * @param Zend_Http_Cookie|string $cookie
+ * @param string|null $value If "cookie" is a string, this is the cookie value.
+ * @return Zend_Http_Client
+ */
+ public function setCookie($cookie, $value = null)
+ {
+ if (! class_exists('Zend_Http_Cookie'))
+ require_once 'Zend/Http/Cookie.php';
+
+ if (is_array($cookie)) {
+ foreach ($cookie as $c => $v) {
+ if (is_string($c)) {
+ $this->setCookie($c, $v);
+ } else {
+ $this->setCookie($v);
+ }
+ }
+
+ return $this;
+ }
+
+ if ($value !== null) $value = urlencode($value);
+
+ if (isset($this->cookiejar)) {
+ if ($cookie instanceof Zend_Http_Cookie) {
+ $this->cookiejar->addCookie($cookie);
+ } elseif (is_string($cookie) && $value !== null) {
+ $cookie = Zend_Http_Cookie::fromString("{$cookie}={$value}", $this->uri);
+ $this->cookiejar->addCookie($cookie);
+ }
+ } else {
+ if ($cookie instanceof Zend_Http_Cookie) {
+ $name = $cookie->getName();
+ $value = $cookie->getValue();
+ $cookie = $name;
+ }
+
+ if (preg_match("/[=,; \t\r\n\013\014]/", $cookie)) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Cookie name cannot contain these characters: =,; \t\r\n\013\014 ({$cookie})");
+ }
+
+ $value = addslashes($value);
+
+ if (! isset($this->headers['cookie'])) $this->headers['cookie'] = array('Cookie', '');
+ $this->headers['cookie'][1] .= $cookie . '=' . $value . '; ';
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set a file to upload (using a POST request)
+ *
+ * Can be used in two ways:
+ *
+ * 1. $data is null (default): $filename is treated as the name if a local file which
+ * will be read and sent. Will try to guess the content type using mime_content_type().
+ * 2. $data is set - $filename is sent as the file name, but $data is sent as the file
+ * contents and no file is read from the file system. In this case, you need to
+ * manually set the content-type ($ctype) or it will default to
+ * application/octet-stream.
+ *
+ * @param string $filename Name of file to upload, or name to save as
+ * @param string $formname Name of form element to send as
+ * @param string $data Data to send (if null, $filename is read and sent)
+ * @param string $ctype Content type to use (if $data is set and $ctype is
+ * null, will be application/octet-stream)
+ * @return Zend_Http_Client
+ */
+ public function setFileUpload($filename, $formname, $data = null, $ctype = null)
+ {
+ if ($data === null) {
+ if (($data = @file_get_contents($filename)) === false) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload");
+ }
+
+ if (! $ctype && function_exists('mime_content_type')) $ctype = mime_content_type($filename);
+ }
+
+ // Force enctype to multipart/form-data
+ $this->setEncType(self::ENC_FORMDATA);
+
+ if ($ctype === null) $ctype = 'application/octet-stream';
+ $this->files[$formname] = array(basename($filename), $ctype, $data);
+
+ return $this;
+ }
+
+ /**
+ * Set the encoding type for POST data
+ *
+ * @param string $enctype
+ * @return Zend_Http_Client
+ */
+ public function setEncType($enctype = self::ENC_URLENCODED)
+ {
+ $this->enctype = $enctype;
+
+ return $this;
+ }
+
+ /**
+ * Set the raw (already encoded) POST data.
+ *
+ * This function is here for two reasons:
+ * 1. For advanced user who would like to set their own data, already encoded
+ * 2. For backwards compatibilty: If someone uses the old post($data) method.
+ * this method will be used to set the encoded data.
+ *
+ * @param string $data
+ * @param string $enctype
+ * @return Zend_Http_Client
+ */
+ public function setRawData($data, $enctype = null)
+ {
+ $this->raw_post_data = $data;
+ $this->setEncType($enctype);
+
+ return $this;
+ }
+
+ /**
+ * Clear all GET and POST parameters
+ *
+ * Should be used to reset the request parameters if the client is
+ * used for several concurrent requests.
+ *
+ * @return Zend_Http_Client
+ */
+ public function resetParameters()
+ {
+ // Reset parameter data
+ $this->paramsGet = array();
+ $this->paramsPost = array();
+ $this->files = array();
+ $this->raw_post_data = null;
+
+ // Clear outdated headers
+ if (isset($this->headers['content-type'])) unset($this->headers['content-type']);
+ if (isset($this->headers['content-length'])) unset($this->headers['content-length']);
+
+ return $this;
+ }
+
+ /**
+ * Get the last HTTP request as string
+ *
+ * @return string
+ */
+ public function getLastRequest()
+ {
+ return $this->last_request;
+ }
+
+ /**
+ * Get the last HTTP response received by this client
+ *
+ * If $config['storeresponse'] is set to false, or no response was
+ * stored yet, will return null
+ *
+ * @return Zend_Http_Response or null if none
+ */
+ public function getLastResponse()
+ {
+ return $this->last_response;
+ }
+
+ /**
+ * Load the connection adapter
+ *
+ * While this method is not called more than one for a client, it is
+ * seperated from ->request() to preserve logic and readability
+ *
+ * @param Zend_Http_Client_Adapter_Interface|string $adapter
+ */
+ public function setAdapter($adapter)
+ {
+ if (is_string($adapter)) {
+ try {
+ Zend_Loader::loadClass($adapter);
+ } catch (Zend_Exception $e) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}");
+ }
+
+ $adapter = new $adapter;
+ }
+
+ if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter');
+ }
+
+ $this->adapter = $adapter;
+ $config = $this->config;
+ unset($config['adapter']);
+ $this->adapter->setConfig($config);
+ }
+
+ /**
+ * Send the HTTP request and return an HTTP response object
+ *
+ * @param string $method
+ * @return Zend_Http_Response
+ */
+ public function request($method = null)
+ {
+ if (! $this->uri instanceof Zend_Uri_Http) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');
+ }
+
+ if ($method) $this->setMethod($method);
+ $this->redirectCounter = 0;
+ $response = null;
+
+ // Make sure the adapter is loaded
+ if ($this->adapter == null) $this->setAdapter($this->config['adapter']);
+
+ // Send the first request. If redirected, continue.
+ do {
+ // Clone the URI and add the additional GET parameters to it
+ $uri = clone $this->uri;
+ if (! empty($this->paramsGet)) {
+ $query = $uri->getQuery();
+ if (! empty($query)) $query .= '&';
+ $query .= http_build_query($this->paramsGet, null, '&');
+
+ $uri->setQuery($query);
+ }
+
+ $body = $this->prepare_body();
+ $headers = $this->prepare_headers();
+
+ // Open the connection, send the request and read the response
+ $this->adapter->connect($uri->getHost(), $uri->getPort(),
+ ($uri->getScheme() == 'https' ? true : false));
+
+ $this->last_request = $this->adapter->write($this->method,
+ $uri, $this->config['httpversion'], $headers, $body);
+
+ $response = $this->adapter->read();
+ if (! $response) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception('Unable to read response, or response is empty');
+ }
+
+ $response = Zend_Http_Response::fromString($response);
+ if ($this->config['storeresponse']) $this->last_response = $response;
+
+ // Load cookies into cookie jar
+ if (isset($this->cookiejar)) $this->cookiejar->addCookiesFromResponse($response, $uri);
+
+ // If we got redirected, look for the Location header
+ if ($response->isRedirect() && ($location = $response->getHeader('location'))) {
+
+ // Check whether we send the exact same request again, or drop the parameters
+ // and send a GET request
+ if ($response->getStatus() == 303 ||
+ ((! $this->config['strictredirects']) && ($response->getStatus() == 302 ||
+ $response->getStatus() == 301))) {
+
+ $this->resetParameters();
+ $this->setMethod(self::GET);
+ }
+
+ // If we got a well formed absolute URI
+ if (Zend_Uri_Http::check($location)) {
+ $this->setHeaders('host', null);
+ $this->setUri($location);
+
+ } else {
+
+ // Split into path and query and set the query
+ if (strpos($location, '?') !== false) {
+ list($location, $query) = explode('?', $location, 2);
+ } else {
+ $query = '';
+ }
+ $this->uri->setQuery($query);
+
+ // Else, if we got just an absolute path, set it
+ if(strpos($location, '/') === 0) {
+ $this->uri->setPath($location);
+
+ // Else, assume we have a relative path
+ } else {
+ // Get the current path directory, removing any trailing slashes
+ $path = $this->uri->getPath();
+ $path = rtrim(substr($path, 0, strrpos($path, '/')), "/");
+ $this->uri->setPath($path . '/' . $location);
+ }
+ }
+ ++$this->redirectCounter;
+
+ } else {
+ // If we didn't get any location, stop redirecting
+ break;
+ }
+
+ } while ($this->redirectCounter < $this->config['maxredirects']);
+
+ return $response;
+ }
+
+ /**
+ * Prepare the request headers
+ *
+ * @return array
+ */
+ protected function prepare_headers()
+ {
+ $headers = array();
+
+ // Set the host header
+ if (! isset($this->headers['host'])) {
+ $host = $this->uri->getHost();
+
+ // If the port is not default, add it
+ if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) ||
+ ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) {
+ $host .= ':' . $this->uri->getPort();
+ }
+
+ $headers[] = "Host: {$host}";
+ }
+
+ // Set the connection header
+ if (! isset($this->headers['connection'])) {
+ if (! $this->config['keepalive']) $headers[] = "Connection: close";
+ }
+
+ // Set the Accept-encoding header if not set - depending on whether
+ // zlib is available or not.
+ if (! isset($this->headers['accept-encoding'])) {
+ if (function_exists('gzinflate')) {
+ $headers[] = 'Accept-encoding: gzip, deflate';
+ } else {
+ $headers[] = 'Accept-encoding: identity';
+ }
+ }
+
+ // Set the content-type header
+ if ($this->method == self::POST &&
+ (! isset($this->headers['content-type']) && isset($this->enctype))) {
+
+ $headers[] = "Content-type: {$this->enctype}";
+ }
+
+ // Set the user agent header
+ if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) {
+ $headers[] = "User-agent: {$this->config['useragent']}";
+ }
+
+ // Set HTTP authentication if needed
+ if (is_array($this->auth)) {
+ $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']);
+ $headers[] = "Authorization: {$auth}";
+ }
+
+ // Load cookies from cookie jar
+ if (isset($this->cookiejar)) {
+ $cookstr = $this->cookiejar->getMatchingCookies($this->uri,
+ true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT);
+
+ if ($cookstr) $headers[] = "Cookie: {$cookstr}";
+ }
+
+ // Add all other user defined headers
+ foreach ($this->headers as $header) {
+ list($name, $value) = $header;
+ if (is_array($value))
+ $value = implode(', ', $value);
+
+ $headers[] = "$name: $value";
+ }
+
+ return $headers;
+ }
+
+ /**
+ * Prepare the request body (for POST and PUT requests)
+ *
+ * @return string
+ */
+ protected function prepare_body()
+ {
+ // According to RFC2616, a TRACE request should not have a body.
+ if ($this->method == self::TRACE) {
+ return '';
+ }
+
+ // If we have raw_post_data set, just use it as the body.
+ if (isset($this->raw_post_data)) {
+ $this->setHeaders('Content-length', strlen($this->raw_post_data));
+ return $this->raw_post_data;
+ }
+
+ $body = '';
+
+ // If we have files to upload, force enctype to multipart/form-data
+ if (count ($this->files) > 0) $this->setEncType(self::ENC_FORMDATA);
+
+ // If we have POST parameters or files, encode and add them to the body
+ if (count($this->paramsPost) > 0 || count($this->files) > 0) {
+ switch($this->enctype) {
+ case self::ENC_FORMDATA:
+ // Encode body as multipart/form-data
+ $boundary = '---ZENDHTTPCLIENT-' . md5(microtime());
+ $this->setHeaders('Content-type', self::ENC_FORMDATA . "; boundary={$boundary}");
+
+ // Get POST parameters and encode them
+ $params = $this->_getParametersRecursive($this->paramsPost);
+ foreach ($params as $pp) {
+ $body .= self::encodeFormData($boundary, $pp[0], $pp[1]);
+ }
+
+ // Encode files
+ foreach ($this->files as $name => $file) {
+ $fhead = array('Content-type' => $file[1]);
+ $body .= self::encodeFormData($boundary, $name, $file[2], $file[0], $fhead);
+ }
+
+ $body .= "--{$boundary}--\r\n";
+ break;
+
+ case self::ENC_URLENCODED:
+ // Encode body as application/x-www-form-urlencoded
+ $this->setHeaders('Content-type', self::ENC_URLENCODED);
+ $body = http_build_query($this->paramsPost, '', '&');
+ break;
+
+ default:
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}' automatically." .
+ " Please use Zend_Http_Client::setRawData to send this kind of content.");
+ break;
+ }
+ }
+
+ if ($body) $this->setHeaders('Content-length', strlen($body));
+ return $body;
+ }
+
+ /**
+ * Helper method that gets a possibly multi-level parameters array (get or
+ * post) and flattens it.
+ *
+ * The method returns an array of (key, value) pairs (because keys are not
+ * necessarily unique. If one of the parameters in as array, it will also
+ * add a [] suffix to the key.
+ *
+ * @param array $parray The parameters array
+ * @param bool $urlencode Whether to urlencode the name and value
+ * @return array
+ */
+ protected function _getParametersRecursive($parray, $urlencode = false)
+ {
+ if (! is_array($parray)) return $parray;
+ $parameters = array();
+
+ foreach ($parray as $name => $value) {
+ if ($urlencode) $name = urlencode($name);
+
+ // If $value is an array, iterate over it
+ if (is_array($value)) {
+ $name .= ($urlencode ? '%5B%5D' : '[]');
+ foreach ($value as $subval) {
+ if ($urlencode) $subval = urlencode($subval);
+ $parameters[] = array($name, $subval);
+ }
+ } else {
+ if ($urlencode) $value = urlencode($value);
+ $parameters[] = array($name, $value);
+ }
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Encode data to a multipart/form-data part suitable for a POST request.
+ *
+ * @param string $boundary
+ * @param string $name
+ * @param mixed $value
+ * @param string $filename
+ * @param array $headers Associative array of optional headers @example ("Content-transfer-encoding" => "binary")
+ * @return string
+ */
+ public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array()) {
+ $ret = "--{$boundary}\r\n" .
+ 'Content-disposition: form-data; name="' . $name .'"';
+
+ if ($filename) $ret .= '; filename="' . $filename . '"';
+ $ret .= "\r\n";
+
+ foreach ($headers as $hname => $hvalue) {
+ $ret .= "{$hname}: {$hvalue}\r\n";
+ }
+ $ret .= "\r\n";
+
+ $ret .= "{$value}\r\n";
+
+ return $ret;
+ }
+
+ /**
+ * Create a HTTP authentication "Authorization:" header according to the
+ * specified user, password and authentication method.
+ *
+ * @see http://www.faqs.org/rfcs/rfc2617.html
+ * @param string $user
+ * @param string $password
+ * @param string $type
+ * @return string
+ */
+ public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC)
+ {
+ $authHeader = null;
+
+ switch ($type) {
+ case self::AUTH_BASIC:
+ // In basic authentication, the user name cannot contain ":"
+ if (strpos($user, ':') !== false) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP authentication");
+ }
+
+ $authHeader = 'Basic ' . base64_encode($user . ':' . $password);
+ break;
+
+ //case self::AUTH_DIGEST:
+ /**
+ * @todo Implement digest authentication
+ */
+ // break;
+
+ default:
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'");
+ }
+
+ return $authHeader;
+ }
+}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Exception.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Exception.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Exception.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Exception.php Wed May 7 16:48:15 2008
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter_Exception
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Http/Client/Exception.php';
+
+/**
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Exception extends Zend_Http_Client_Exception
+{}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Interface.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Interface.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Interface.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Interface.php Wed May 7 16:48:15 2008
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * An interface description for Zend_Http_Client_Adapter classes.
+ *
+ * These classes are used as connectors for Zend_Http_Client, performing the
+ * tasks of connecting, writing, reading and closing connection to the server.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Http_Client_Adapter_Interface
+{
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @param array $config
+ */
+ public function setConfig($config = array());
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ */
+ public function connect($host, $port = 80, $secure = false);
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $url
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as text
+ */
+ public function write($method, $url, $http_ver = '1.1', $headers = array(), $body = '');
+
+ /**
+ * Read response from server
+ *
+ * @return string
+ */
+ public function read();
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close();
+}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Proxy.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Proxy.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Proxy.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Proxy.php Wed May 7 16:48:15 2008
@@ -0,0 +1,267 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Proxy.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Uri/Http.php';
+require_once 'Zend/Http/Client.php';
+require_once 'Zend/Http/Client/Adapter/Socket.php';
+
+/**
+ * HTTP Proxy-supporting Zend_Http_Client adapter class, based on the default
+ * socket based adapter.
+ *
+ * Should be used if proxy HTTP access is required. If no proxy is set, will
+ * fall back to Zend_Http_Client_Adapter_Socket behavior. Just like the
+ * default Socket adapter, this adapter does not require any special extensions
+ * installed.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Proxy extends Zend_Http_Client_Adapter_Socket
+{
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $config = array(
+ 'ssltransport' => 'ssl',
+ 'proxy_host' => '',
+ 'proxy_port' => 8080,
+ 'proxy_user' => '',
+ 'proxy_pass' => '',
+ 'proxy_auth' => Zend_Http_Client::AUTH_BASIC
+ );
+
+ /**
+ * Whether HTTPS CONNECT was already negotiated with the proxy or not
+ *
+ * @var boolean
+ */
+ protected $negotiated = false;
+
+ /**
+ * Connect to the remote server
+ *
+ * Will try to connect to the proxy server. If no proxy was set, will
+ * fall back to the target server (behave like regular Socket adapter)
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ * @param int $timeout
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ // If no proxy is set, fall back to Socket adapter
+ if (! $this->config['proxy_host']) return parent::connect($host, $port, $secure);
+
+ // Go through a proxy - the connection is actually to the proxy server
+ $host = $this->config['proxy_host'];
+ $port = $this->config['proxy_port'];
+
+ // If we are connected to the wrong proxy, disconnect first
+ if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
+ if (is_resource($this->socket)) $this->close();
+ }
+
+ // Now, if we are not connected, connect
+ if (! is_resource($this->socket) || ! $this->config['keepalive']) {
+ $this->socket = @fsockopen($host, $port, $errno, $errstr, (int) $this->config['timeout']);
+ if (! $this->socket) {
+ $this->close();
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Unable to Connect to proxy server ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr);
+ }
+
+ // Set the stream timeout
+ if (!stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
+ }
+
+ // Update connected_to
+ $this->connected_to = array($host, $port);
+ }
+ }
+
+ /**
+ * Send request to the proxy server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as string
+ */
+ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
+ {
+ // If no proxy is set, fall back to default Socket adapter
+ if (! $this->config['proxy_host']) return parent::write($method, $uri, $http_ver, $headers, $body);
+
+ // Make sure we're properly connected
+ if (! $this->socket) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
+ }
+
+ $host = $this->config['proxy_host'];
+ $port = $this->config['proxy_port'];
+
+ if ($this->connected_to[0] != $host || $this->connected_to[1] != $port) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy server");
+ }
+
+ // Add Proxy-Authorization header
+ if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization']))
+ $headers['proxy-authorization'] = Zend_Http_Client::encodeAuthHeader(
+ $this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth']
+ );
+
+ // if we are proxying HTTPS, preform CONNECT handshake with the proxy
+ if ($uri->getScheme() == 'https' && (! $this->negotiated)) {
+ $this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver, $headers);
+ $this->negotiated = true;
+ }
+
+ // Save request method for later
+ $this->method = $method;
+
+ // Build request headers
+ $request = "{$method} {$uri->__toString()} HTTP/{$http_ver}\r\n";
+
+ // Add all headers to the request string
+ foreach ($headers as $k => $v) {
+ if (is_string($k)) $v = "$k: $v";
+ $request .= "$v\r\n";
+ }
+
+ // Add the request body
+ $request .= "\r\n" . $body;
+
+ // Send the request
+ if (! @fwrite($this->socket, $request)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server");
+ }
+
+ return $request;
+ }
+
+ /**
+ * Preform handshaking with HTTPS proxy using CONNECT method
+ *
+ * @param string $host
+ * @param integer $port
+ * @param string $http_ver
+ * @param array $headers
+ */
+ protected function connectHandshake($host, $port = 443, $http_ver = '1.1', array &$headers = array())
+ {
+ $request = "CONNECT $host:$port HTTP/$http_ver\r\n" .
+ "Host: " . $this->config['proxy_host'] . "\r\n";
+
+ // Add the user-agent header
+ if (isset($this->config['useragent'])) {
+ $request .= "User-agent: " . $this->config['useragent'] . "\r\n";
+ }
+
+ // If the proxy-authorization header is set, send it to proxy but remove
+ // it from headers sent to target host
+ if (isset($headers['proxy-authorization'])) {
+ $request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n";
+ unset($headers['proxy-authorization']);
+ }
+
+ $request .= "\r\n";
+
+ // Send the request
+ if (! @fwrite($this->socket, $request)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server");
+ }
+
+ // Read response headers only
+ $response = '';
+ $gotStatus = false;
+ while ($line = @fgets($this->socket)) {
+ $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
+ if ($gotStatus) {
+ $response .= $line;
+ if (!chop($line)) break;
+ }
+ }
+
+ // Check that the response from the proxy is 200
+ if (Zend_Http_Response::extractCode($response) != 200) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Unable to connect to HTTPS proxy. Server response: " . $response);
+ }
+
+ // If all is good, switch socket to secure mode. We have to fall back
+ // through the different modes
+ $modes = array(
+ STREAM_CRYPTO_METHOD_TLS_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv2_CLIENT
+ );
+
+ $success = false;
+ foreach($modes as $mode) {
+ $success = stream_socket_enable_crypto($this->socket, true, $mode);
+ if ($success) break;
+ }
+
+ if (! $success) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Unable to connect to" .
+ " HTTPS server through proxy: could not negotiate secure connection.");
+ }
+ }
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close()
+ {
+ parent::close();
+ $this->negotiated = false;
+ }
+
+ /**
+ * Destructor: make sure the socket is disconnected
+ *
+ */
+ public function __destruct()
+ {
+ if ($this->socket) $this->close();
+ }
+}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Socket.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Socket.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Socket.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Socket.php Wed May 7 16:48:15 2008
@@ -0,0 +1,319 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Socket.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Uri/Http.php';
+require_once 'Zend/Http/Client/Adapter/Interface.php';
+
+/**
+ * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used
+ * on almost every PHP environment, and does not require any special extensions.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface
+{
+ /**
+ * The socket for server connection
+ *
+ * @var resource|null
+ */
+ protected $socket = null;
+
+ /**
+ * What host/port are we connected to?
+ *
+ * @var array
+ */
+ protected $connected_to = array(null, null);
+
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $config = array(
+ 'ssltransport' => 'ssl',
+ 'sslcert' => null,
+ 'sslpassphrase' => null
+ );
+
+ /**
+ * Request method - will be set by write() and might be used by read()
+ *
+ * @var string
+ */
+ protected $method = null;
+
+ /**
+ * Adapter constructor, currently empty. Config is set using setConfig()
+ *
+ */
+ public function __construct()
+ {
+ }
+
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @param array $config
+ */
+ public function setConfig($config = array())
+ {
+ if (! is_array($config)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ '$config expects an array, ' . gettype($config) . ' recieved.');
+ }
+
+ foreach ($config as $k => $v) {
+ $this->config[strtolower($k)] = $v;
+ }
+ }
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ * @param int $timeout
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ // If the URI should be accessed via SSL, prepend the Hostname with ssl://
+ $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
+
+ // If we are connected to the wrong host, disconnect first
+ if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
+ if (is_resource($this->socket)) $this->close();
+ }
+
+ // Now, if we are not connected, connect
+ if (! is_resource($this->socket) || ! $this->config['keepalive']) {
+ $context = stream_context_create();
+ if ($secure) {
+ if ($this->config['sslcert'] !== null) {
+ if (! stream_context_set_option($context, 'ssl', 'local_cert',
+ $this->config['sslcert'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option');
+ }
+ }
+ if ($this->config['sslpassphrase'] !== null) {
+ if (! stream_context_set_option($context, 'ssl', 'passphrase',
+ $this->config['sslpassphrase'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option');
+ }
+ }
+ }
+
+ $this->socket = @stream_socket_client($host . ':' . $port,
+ $errno,
+ $errstr,
+ (int) $this->config['timeout'],
+ STREAM_CLIENT_CONNECT,
+ $context);
+ if (! $this->socket) {
+ $this->close();
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr);
+ }
+
+ // Set the stream timeout
+ if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
+ }
+
+ // Update connected_to
+ $this->connected_to = array($host, $port);
+ }
+ }
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as string
+ */
+ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
+ {
+ // Make sure we're properly connected
+ if (! $this->socket) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
+ }
+
+ $host = $uri->getHost();
+ $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
+ if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host');
+ }
+
+ // Save request method for later
+ $this->method = $method;
+
+ // Build request headers
+ $path = $uri->getPath();
+ if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
+ $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
+ foreach ($headers as $k => $v) {
+ if (is_string($k)) $v = ucfirst($k) . ": $v";
+ $request .= "$v\r\n";
+ }
+
+ // Add the request body
+ $request .= "\r\n" . $body;
+
+ // Send the request
+ if (! @fwrite($this->socket, $request)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
+ }
+
+ return $request;
+ }
+
+ /**
+ * Read response from server
+ *
+ * @return string
+ */
+ public function read()
+ {
+ // First, read headers only
+ $response = '';
+ $gotStatus = false;
+ while ($line = @fgets($this->socket)) {
+ $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
+ if ($gotStatus) {
+ $response .= $line;
+ if (!chop($line)) break;
+ }
+ }
+
+ // Handle 100 and 101 responses internally by restarting the read again
+ if (Zend_Http_Response::extractCode($response) == 100 ||
+ Zend_Http_Response::extractCode($response) == 101) return $this->read();
+
+ // If this was a HEAD request, return after reading the header (no need to read body)
+ if ($this->method == Zend_Http_Client::HEAD) return $response;
+
+ // Check headers to see what kind of connection / transfer encoding we have
+ $headers = Zend_Http_Response::extractHeaders($response);
+
+ // if the connection is set to close, just read until socket closes
+ if (isset($headers['connection']) && $headers['connection'] == 'close') {
+ while ($buff = @fread($this->socket, 8192)) {
+ $response .= $buff;
+ }
+
+ $this->close();
+
+ // Else, if we got a transfer-encoding header (chunked body)
+ } elseif (isset($headers['transfer-encoding'])) {
+ if ($headers['transfer-encoding'] == 'chunked') {
+ do {
+ $chunk = '';
+ $line = @fgets($this->socket);
+ $chunk .= $line;
+
+ $hexchunksize = ltrim(chop($line), '0');
+ $hexchunksize = strlen($hexchunksize) ? strtolower($hexchunksize) : 0;
+
+ $chunksize = hexdec(chop($line));
+ if (dechex($chunksize) != $hexchunksize) {
+ @fclose($this->socket);
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' .
+ $hexchunksize . '" unable to read chunked body');
+ }
+
+ $left_to_read = $chunksize;
+ while ($left_to_read > 0) {
+ $line = @fread($this->socket, $left_to_read);
+ $chunk .= $line;
+ $left_to_read -= strlen($line);
+ }
+
+ $chunk .= @fgets($this->socket);
+ $response .= $chunk;
+ } while ($chunksize > 0);
+ } else {
+ throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' .
+ $headers['transfer-encoding'] . '" transfer encoding');
+ }
+
+ // Else, if we got the content-length header, read this number of bytes
+ } elseif (isset($headers['content-length'])) {
+ $left_to_read = $headers['content-length'];
+ $chunk = '';
+ while ($left_to_read > 0) {
+ $chunk = @fread($this->socket, $left_to_read);
+ $left_to_read -= strlen($chunk);
+ $response .= $chunk;
+ }
+
+ // Fallback: just read the response (should not happen)
+ } else {
+ while ($buff = @fread($this->socket, 8192)) {
+ $response .= $buff;
+ }
+
+ $this->close();
+ }
+
+ return $response;
+ }
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close()
+ {
+ if (is_resource($this->socket)) @fclose($this->socket);
+ $this->socket = null;
+ $this->connected_to = array(null, null);
+ }
+
+ /**
+ * Destructor: make sure the socket is disconnected
+ *
+ */
+ public function __destruct()
+ {
+ if ($this->socket) $this->close();
+ }
+}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Test.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Test.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Test.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Test.php Wed May 7 16:48:15 2008
@@ -0,0 +1,193 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Test.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Uri/Http.php';
+require_once 'Zend/Http/Response.php';
+require_once 'Zend/Http/Client/Adapter/Interface.php';
+
+/**
+ * A testing-purposes adapter.
+ *
+ * Should be used to test all components that rely on Zend_Http_Client,
+ * without actually performing an HTTP request. You should instantiate this
+ * object manually, and then set it as the client's adapter. Then, you can
+ * set the expected response using the setResponse() method.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Test implements Zend_Http_Client_Adapter_Interface
+{
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * Buffer of responses to be returned by the read() method. Can be
+ * set using setResponse() and addResponse().
+ *
+ * @var array
+ */
+ protected $responses = array("HTTP/1.1 400 Bad Request\r\n\r\n");
+
+ /**
+ * Current position in the response buffer
+ *
+ * @var integer
+ */
+ protected $responseIndex = 0;
+
+ /**
+ * Adapter constructor, currently empty. Config is set using setConfig()
+ *
+ */
+ public function __construct()
+ { }
+
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @param array $config
+ */
+ public function setConfig($config = array())
+ {
+ if (! is_array($config)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ '$config expects an array, ' . gettype($config) . ' recieved.');
+ }
+
+ foreach ($config as $k => $v) {
+ $this->config[strtolower($k)] = $v;
+ }
+ }
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ * @param int $timeout
+ */
+ public function connect($host, $port = 80, $secure = false)
+ { }
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as string
+ */
+ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
+ {
+ $host = $uri->getHost();
+ $host = (strtolower($uri->getScheme()) == 'https' ? 'sslv2://' . $host : $host);
+
+ // Build request headers
+ $path = $uri->getPath();
+ if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
+ $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
+ foreach ($headers as $k => $v) {
+ if (is_string($k)) $v = ucfirst($k) . ": $v";
+ $request .= "$v\r\n";
+ }
+
+ // Add the request body
+ $request .= "\r\n" . $body;
+
+ // Do nothing - just return the request as string
+
+ return $request;
+ }
+
+ /**
+ * Return the response set in $this->setResponse()
+ *
+ * @return string
+ */
+ public function read()
+ {
+ if ($this->responseIndex >= count($this->responses)) {
+ $this->responseIndex = 0;
+ }
+ return $this->responses[$this->responseIndex++];
+ }
+
+ /**
+ * Close the connection (dummy)
+ *
+ */
+ public function close()
+ { }
+
+ /**
+ * Set the HTTP response(s) to be returned by this adapter
+ *
+ * @param Zend_Http_Response|array|string $response
+ */
+ public function setResponse($response)
+ {
+ if ($response instanceof Zend_Http_Response) {
+ $response = $response->asString();
+ }
+
+ $this->responses = (array)$response;
+ $this->responseIndex = 0;
+ }
+
+ /**
+ * Add another response to the response buffer.
+ *
+ * @param string $response
+ */
+ public function addResponse($response)
+ {
+ $this->responses[] = $response;
+ }
+
+ /**
+ * Sets the position of the response buffer. Selects which
+ * response will be returned on the next call to read().
+ *
+ * @param integer $index
+ */
+ public function setResponseIndex($index)
+ {
+ if ($index < 0 || $index >= count($this->responses)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Index out of range of response buffer size');
+ }
+ $this->responseIndex = $index;
+ }
+}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Client/Exception.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Client/Exception.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Client/Exception.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Client/Exception.php Wed May 7 16:48:15 2008
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Exception
+ * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Http/Exception.php';
+
+/**
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Exception extends Zend_Http_Exception
+{}
Added: incubator/shindig/trunk/php/src/common/Zend/Http/Cookie.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Http/Cookie.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Http/Cookie.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Http/Cookie.php Wed May 7 16:48:15 2008
@@ -0,0 +1,327 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to version 1.0 of the Zend Framework
+ * license, that is bundled with this package in the file LICENSE.txt,
+ * and is available through the world-wide-web at the following URL:
+ * http://framework.zend.com/license/new-bsd. If you did not
+ * receive a copy of the Zend Framework license and are unable to
+ * obtain it through the world-wide-web, please send a note to
+ * license@zend.com so we can mail you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Cookie
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com/)
+ * @version $Id: Cookie.php 8064 2008-02-16 10:58:39Z thomas $
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+require_once 'Zend/Uri/Http.php';
+
+/**
+ * Zend_Http_Cookie is a class describing an HTTP cookie and all it's parameters.
+ *
+ * Zend_Http_Cookie is a class describing an HTTP cookie and all it's parameters. The
+ * class also enables validating whether the cookie should be sent to the server in
+ * a specified scenario according to the request URI, the expiry time and whether
+ * session cookies should be used or not. Generally speaking cookies should be
+ * contained in a Cookiejar object, or instantiated manually and added to an HTTP
+ * request.
+ *
+ * See http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com/)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Cookie
+{
+ /**
+ * Cookie name
+ *
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * Cookie value
+ *
+ * @var string
+ */
+ protected $value;
+
+ /**
+ * Cookie expiry date
+ *
+ * @var int
+ */
+ protected $expires;
+
+ /**
+ * Cookie domain
+ *
+ * @var string
+ */
+ protected $domain;
+
+ /**
+ * Cookie path
+ *
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * Whether the cookie is secure or not
+ *
+ * @var boolean
+ */
+ protected $secure;
+
+ /**
+ * Cookie object constructor
+ *
+ * @todo Add validation of each one of the parameters (legal domain, etc.)
+ *
+ * @param string $name
+ * @param string $value
+ * @param int $expires
+ * @param string $domain
+ * @param string $path
+ * @param bool $secure
+ */
+ public function __construct($name, $value, $domain, $expires = null, $path = null, $secure = false)
+ {
+ if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
+ require_once 'Zend/Http/Exception.php';
+ throw new Zend_Http_Exception("Cookie name cannot contain these characters: =,; \\t\\r\\n\\013\\014 ({$name})");
+ }
+
+ if (! $this->name = (string) $name) {
+ require_once 'Zend/Http/Exception.php';
+ throw new Zend_Http_Exception('Cookies must have a name');
+ }
+
+ if (! $this->domain = (string) $domain) {
+ require_once 'Zend/Http/Exception.php';
+ throw new Zend_Http_Exception('Cookies must have a domain');
+ }
+
+ $this->value = (string) $value;
+ $this->expires = ($expires === null ? null : (int) $expires);
+ $this->path = ($path ? $path : '/');
+ $this->secure = $secure;
+ }
+
+ /**
+ * Get Cookie name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get cookie value
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Get cookie domain
+ *
+ * @return string
+ */
+ public function getDomain()
+ {
+ return $this->domain;
+ }
+
+ /**
+ * Get the cookie path
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Get the expiry time of the cookie, or null if no expiry time is set
+ *
+ * @return int|null
+ */
+ public function getExpiryTime()
+ {
+ return $this->expires;
+ }
+
+ /**
+ * Check whether the cookie should only be sent over secure connections
+ *
+ * @return boolean
+ */
+ public function isSecure()
+ {
+ return $this->secure;
+ }
+
+ /**
+ * Check whether the cookie has expired
+ *
+ * Always returns false if the cookie is a session cookie (has no expiry time)
+ *
+ * @param int $now Timestamp to consider as "now"
+ * @return boolean
+ */
+ public function isExpired($now = null)
+ {
+ if ($now === null) $now = time();
+ if (is_int($this->expires) && $this->expires < $now) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check whether the cookie is a session cookie (has no expiry time set)
+ *
+ * @return boolean
+ */
+ public function isSessionCookie()
+ {
+ return ($this->expires === null);
+ }
+
+ /**
+ * Checks whether the cookie should be sent or not in a specific scenario
+ *
+ * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path)
+ * @param boolean $matchSessionCookies Whether to send session cookies
+ * @param int $now Override the current time when checking for expiry time
+ * @return boolean
+ */
+ public function match($uri, $matchSessionCookies = true, $now = null)
+ {
+ if (is_string ($uri)) {
+ $uri = Zend_Uri_Http::factory($uri);
+ }
+
+ // Make sure we have a valid Zend_Uri_Http object
+ if (! ($uri->valid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) {
+ require_once 'Zend/Http/Exception.php';
+ throw new Zend_Http_Exception('Passed URI is not a valid HTTP or HTTPS URI');
+ }
+
+ // Check that the cookie is secure (if required) and not expired
+ if ($this->secure && $uri->getScheme() != 'https') return false;
+ if ($this->isExpired($now)) return false;
+ if ($this->isSessionCookie() && ! $matchSessionCookies) return false;
+
+ // Validate domain and path
+ // Domain is validated using tail match, while path is validated using head match
+ $domain_preg = preg_quote($this->getDomain(), "/");
+ if (! preg_match("/{$domain_preg}$/", $uri->getHost())) return false;
+ $path_preg = preg_quote($this->getPath(), "/");
+ if (! preg_match("/^{$path_preg}/", $uri->getPath())) return false;
+
+ // If we didn't die until now, return true.
+ return true;
+ }
+
+ /**
+ * Get the cookie as a string, suitable for sending as a "Cookie" header in an
+ * HTTP request
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->name . '=' . urlencode($this->value) . ';';
+ }
+
+ /**
+ * Generate a new Cookie object from a cookie string
+ * (for example the value of the Set-Cookie HTTP header)
+ *
+ * @param string $cookieStr
+ * @param Zend_Uri_Http|string $ref_uri Reference URI for default values (domain, path)
+ * @return Zend_Http_Cookie A new Zend_Http_Cookie object or false on failure.
+ */
+ public static function fromString($cookieStr, $ref_uri = null)
+ {
+ // Set default values
+ if (is_string($ref_uri)) {
+ $ref_uri = Zend_Uri_Http::factory($ref_uri);
+ }
+
+ $name = '';
+ $value = '';
+ $domain = '';
+ $path = '';
+ $expires = null;
+ $secure = false;
+ $parts = explode(';', $cookieStr);
+
+ // If first part does not include '=', fail
+ if (strpos($parts[0], '=') === false) return false;
+
+ // Get the name and value of the cookie
+ list($name, $value) = explode('=', trim(array_shift($parts)), 2);
+ $name = trim($name);
+ $value = urldecode(trim($value));
+
+ // Set default domain and path
+ if ($ref_uri instanceof Zend_Uri_Http) {
+ $domain = $ref_uri->getHost();
+ $path = $ref_uri->getPath();
+ $path = substr($path, 0, strrpos($path, '/'));
+ }
+
+ // Set other cookie parameters
+ foreach ($parts as $part) {
+ $part = trim($part);
+ if (strtolower($part) == 'secure') {
+ $secure = true;
+ continue;
+ }
+
+ $keyValue = explode('=', $part, 2);
+ if (count($keyValue) == 2) {
+ list($k, $v) = $keyValue;
+ switch (strtolower($k)) {
+ case 'expires':
+ $expires = strtotime($v);
+ break;
+ case 'path':
+ $path = $v;
+ break;
+ case 'domain':
+ $domain = $v;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if ($name !== '') {
+ return new Zend_Http_Cookie($name, $value, $domain, $expires, $path, $secure);
+ } else {
+ return false;
+ }
+ }
+}