7 * This source file is subject to version 1.0 of the Zend Framework
8 * license, that is bundled with this package in the file LICENSE.txt,
9 * and is available through the world-wide-web at the following URL:
10 * http://framework.zend.com/license/new-bsd. If you did not
11 * receive a copy of the Zend Framework license and are unable to
12 * obtain it through the world-wide-web, please send a note to
13 * license@zend.com so we can mail you a copy immediately.
17 * @subpackage CookieJar
18 * @version $Id: CookieJar.php 8064 2008-02-16 10:58:39Z thomas $
19 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com/)
20 * @license http://framework.zend.com/license/new-bsd New BSD License
23 require_once "Zend/Uri.php";
24 require_once "Zend/Http/Cookie.php";
25 require_once "Zend/Http/Response.php";
28 * A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should
29 * be used along with Zend_Http_Client in order to manage cookies across HTTP requests and
32 * The class contains an array of Zend_Http_Cookie objects. Cookies can be added to the jar
33 * automatically from a request or manually. Then, the jar can find and return the cookies
34 * needed for a specific HTTP request.
36 * A special parameter can be passed to all methods of this class that return cookies: Cookies
37 * can be returned either in their native form (as Zend_Http_Cookie objects) or as strings -
38 * the later is suitable for sending as the value of the "Cookie" header in an HTTP request.
39 * You can also choose, when returning more than one cookie, whether to get an array of strings
40 * (by passing Zend_Http_CookieJar::COOKIE_STRING_ARRAY) or one unified string for all cookies
41 * (by passing Zend_Http_CookieJar::COOKIE_STRING_CONCAT).
43 * @link http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
47 * @subpackage CookieJar
48 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com/)
49 * @license http://framework.zend.com/license/new-bsd New BSD License
51 class Zend_Http_CookieJar
{
53 * Return cookie(s) as a Zend_Http_Cookie object
56 const COOKIE_OBJECT
= 0;
59 * Return cookie(s) as a string (suitable for sending in an HTTP request)
62 const COOKIE_STRING_ARRAY
= 1;
65 * Return all cookies as one long string (suitable for sending in an HTTP request)
68 const COOKIE_STRING_CONCAT
= 2;
71 * Array storing cookies
73 * Cookies are stored according to domain and path:
81 * + www.otherdomain.net
87 protected $cookies = array();
90 * Construct a new CookieJar object
93 public function __construct() {}
96 * Add a cookie to the jar. Cookie should be passed either as a Zend_Http_Cookie object
97 * or as a string - in which case an object is created from the string.
99 * @param Zend_Http_Cookie|string $cookie
100 * @param Zend_Uri_Http|string $ref_uri Optional reference URI (for domain, path, secure)
102 public function addCookie($cookie, $ref_uri = null) {
103 if (is_string($cookie)) {
104 $cookie = Zend_Http_Cookie
::fromString($cookie, $ref_uri);
107 if ($cookie instanceof Zend_Http_Cookie
) {
108 $domain = $cookie->getDomain();
109 $path = $cookie->getPath();
110 if (! isset($this->cookies
[$domain])) $this->cookies
[$domain] = array();
111 if (! isset($this->cookies
[$domain][$path])) $this->cookies
[$domain][$path] = array();
112 $this->cookies
[$domain][$path][$cookie->getName()] = $cookie;
114 require_once 'external/Zend/Http/Exception.php';
115 throw new Zend_Http_Exception('Supplient argument is not a valid cookie string or object');
120 * Parse an HTTP response, adding all the cookies set in that response
123 * @param Zend_Http_Response $response
124 * @param Zend_Uri_Http|string $ref_uri Requested URI
126 public function addCookiesFromResponse($response, $ref_uri) {
127 if (! $response instanceof Zend_Http_Response
) {
128 require_once 'external/Zend/Http/Exception.php';
129 throw new Zend_Http_Exception('$response is expected to be a Response object, ' . gettype($response) . ' was passed');
132 $cookie_hdrs = $response->getHeader('Set-Cookie');
134 if (is_array($cookie_hdrs)) {
135 foreach ($cookie_hdrs as $cookie) {
136 $this->addCookie($cookie, $ref_uri);
138 } elseif (is_string($cookie_hdrs)) {
139 $this->addCookie($cookie_hdrs, $ref_uri);
144 * Get all cookies in the cookie jar as an array
146 * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
147 * @return array|string
149 public function getAllCookies($ret_as = self
::COOKIE_OBJECT
) {
150 $cookies = $this->_flattenCookiesArray($this->cookies
, $ret_as);
155 * Return an array of all cookies matching a specific request according to the request URI,
156 * whether session cookies should be sent or not, and the time to consider as "now" when
157 * checking cookie expiry time.
159 * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path)
160 * @param boolean $matchSessionCookies Whether to send session cookies
161 * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
162 * @param int $now Override the current time when checking for expiry time
163 * @return array|string
165 public function getMatchingCookies($uri, $matchSessionCookies = true, $ret_as = self
::COOKIE_OBJECT
, $now = null) {
166 if (is_string($uri)) $uri = Zend_Uri
::factory($uri);
167 if (! $uri instanceof Zend_Uri_Http
) {
168 require_once 'external/Zend/Http/Exception.php';
169 throw new Zend_Http_Exception("Invalid URI string or object passed");
173 $path = $uri->getPath();
174 $path = substr($path, 0, strrpos($path, '/'));
175 if (! $path) $path = '/';
177 // First, reduce the array of cookies to only those matching domain and path
178 $cookies = $this->_matchDomain($uri->getHost());
179 $cookies = $this->_matchPath($cookies, $path);
180 $cookies = $this->_flattenCookiesArray($cookies, self
::COOKIE_OBJECT
);
182 // Next, run Cookie->match on all cookies to check secure, time and session mathcing
184 foreach ($cookies as $cookie)
185 if ($cookie->match($uri, $matchSessionCookies, $now)) $ret[] = $cookie;
187 // Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
188 $ret = $this->_flattenCookiesArray($ret, $ret_as);
194 * Get a specific cookie according to a URI and name
196 * @param Zend_Uri_Http|string $uri The uri (domain and path) to match
197 * @param string $cookie_name The cookie's name
198 * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
199 * @return Zend_Http_Cookie|string
201 public function getCookie($uri, $cookie_name, $ret_as = self
::COOKIE_OBJECT
) {
202 if (is_string($uri)) {
203 $uri = Zend_Uri
::factory($uri);
206 if (! $uri instanceof Zend_Uri_Http
) {
207 require_once 'external/Zend/Http/Exception.php';
208 throw new Zend_Http_Exception('Invalid URI specified');
211 // Get correct cookie path
212 $path = $uri->getPath();
213 $path = substr($path, 0, strrpos($path, '/'));
214 if (! $path) $path = '/';
216 if (isset($this->cookies
[$uri->getHost()][$path][$cookie_name])) {
217 $cookie = $this->cookies
[$uri->getHost()][$path][$cookie_name];
220 case self
::COOKIE_OBJECT
:
224 case self
::COOKIE_STRING_ARRAY
:
225 case self
::COOKIE_STRING_CONCAT
:
226 return $cookie->__toString();
230 require_once 'external/Zend/Http/Exception.php';
231 throw new Zend_Http_Exception("Invalid value passed for \$ret_as: {$ret_as}");
240 * Helper function to recursivly flatten an array. Shoud be used when exporting the
241 * cookies array (or parts of it)
243 * @param Zend_Http_Cookie|array $ptr
244 * @param int $ret_as What value to return
245 * @return array|string
247 protected function _flattenCookiesArray($ptr, $ret_as = self
::COOKIE_OBJECT
) {
248 if (is_array($ptr)) {
249 $ret = ($ret_as == self
::COOKIE_STRING_CONCAT ?
'' : array());
250 foreach ($ptr as $item) {
251 if ($ret_as == self
::COOKIE_STRING_CONCAT
) {
252 $ret .= $this->_flattenCookiesArray($item, $ret_as);
254 $ret = array_merge($ret, $this->_flattenCookiesArray($item, $ret_as));
258 } elseif ($ptr instanceof Zend_Http_Cookie
) {
260 case self
::COOKIE_STRING_ARRAY
:
261 return array($ptr->__toString());
264 case self
::COOKIE_STRING_CONCAT
:
265 return $ptr->__toString();
268 case self
::COOKIE_OBJECT
:
279 * Return a subset of the cookies array matching a specific domain
281 * Returned array is actually an array of pointers to items in the $this->cookies array.
283 * @param string $domain
286 protected function _matchDomain($domain) {
289 foreach (array_keys($this->cookies
) as $cdom) {
290 $regex = "/" . preg_quote($cdom, "/") . "$/i";
291 if (preg_match($regex, $domain)) $ret[$cdom] = &$this->cookies
[$cdom];
298 * Return a subset of a domain-matching cookies that also match a specified path
300 * Returned array is actually an array of pointers to items in the $passed array.
302 * @param array $dom_array
303 * @param string $path
306 protected function _matchPath($domains, $path) {
308 if (substr($path, - 1) != '/') $path .= '/';
310 foreach ($domains as $dom => $paths_array) {
311 foreach (array_keys($paths_array) as $cpath) {
312 $regex = "|^" . preg_quote($cpath, "|") . "|i";
313 if (preg_match($regex, $path)) {
314 if (! isset($ret[$dom])) $ret[$dom] = array();
315 $ret[$dom][$cpath] = &$paths_array[$cpath];
324 * Create a new CookieJar object and automatically load into it all the
325 * cookies set in an Http_Response object. If $uri is set, it will be
326 * considered as the requested URI for setting default domain and path
329 * @param Zend_Http_Response $response HTTP Response object
330 * @param Zend_Uri_Http|string $uri The requested URI
331 * @return Zend_Http_CookieJar
332 * @todo Add the $uri functionality.
334 public static function fromResponse(Zend_Http_Response
$response, $ref_uri) {
336 $jar->addCookiesFromResponse($response, $ref_uri);