File indexing completed on 2024-05-12 06:02:41

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Http
0017  * @subpackage CookieJar
0018  * @version    $Id$
0019  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  */
0022 
0023 /**
0024  * @see Zend_Uri
0025  */
0026 // require_once "Zend/Uri.php";
0027 /**
0028  * @see Zend_Http_Cookie
0029  */
0030 // require_once "Zend/Http/Cookie.php";
0031 /**
0032  * @see Zend_Http_Response
0033  */
0034 // require_once "Zend/Http/Response.php";
0035 
0036 /**
0037  * A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should
0038  * be used along with Zend_Http_Client in order to manage cookies across HTTP requests and
0039  * responses.
0040  *
0041  * The class contains an array of Zend_Http_Cookie objects. Cookies can be added to the jar
0042  * automatically from a request or manually. Then, the jar can find and return the cookies
0043  * needed for a specific HTTP request.
0044  *
0045  * A special parameter can be passed to all methods of this class that return cookies: Cookies
0046  * can be returned either in their native form (as Zend_Http_Cookie objects) or as strings -
0047  * the later is suitable for sending as the value of the "Cookie" header in an HTTP request.
0048  * You can also choose, when returning more than one cookie, whether to get an array of strings
0049  * (by passing Zend_Http_CookieJar::COOKIE_STRING_ARRAY) or one unified string for all cookies
0050  * (by passing Zend_Http_CookieJar::COOKIE_STRING_CONCAT).
0051  *
0052  * @link       http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
0053  *
0054  * @category   Zend
0055  * @package    Zend_Http
0056  * @subpackage CookieJar
0057  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0058  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0059  */
0060 class Zend_Http_CookieJar implements Countable, IteratorAggregate
0061 {
0062     /**
0063      * Return cookie(s) as a Zend_Http_Cookie object
0064      *
0065      */
0066     const COOKIE_OBJECT = 0;
0067 
0068     /**
0069      * Return cookie(s) as a string (suitable for sending in an HTTP request)
0070      *
0071      */
0072     const COOKIE_STRING_ARRAY = 1;
0073 
0074     /**
0075      * Return all cookies as one long string (suitable for sending in an HTTP request)
0076      *
0077      */
0078     const COOKIE_STRING_CONCAT = 2;
0079 
0080     /**
0081      * Return all cookies as one long string (strict mode)
0082      *  - Single space after the semi-colon separating each cookie
0083      *  - Remove trailing semi-colon, if any
0084      */
0085     const COOKIE_STRING_CONCAT_STRICT = 3;
0086 
0087     /**
0088      * Array storing cookies
0089      *
0090      * Cookies are stored according to domain and path:
0091      * $cookies
0092      *  + www.mydomain.com
0093      *    + /
0094      *      - cookie1
0095      *      - cookie2
0096      *    + /somepath
0097      *      - othercookie
0098      *  + www.otherdomain.net
0099      *    + /
0100      *      - alsocookie
0101      *
0102      * @var array
0103      */
0104     protected $cookies = array();
0105 
0106     /**
0107      * The Zend_Http_Cookie array
0108      *
0109      * @var array
0110      */
0111     protected $_rawCookies = array();
0112 
0113     /**
0114      * Construct a new CookieJar object
0115      *
0116      */
0117     public function __construct()
0118     { }
0119 
0120     /**
0121      * Add a cookie to the jar. Cookie should be passed either as a Zend_Http_Cookie object
0122      * or as a string - in which case an object is created from the string.
0123      *
0124      * @param Zend_Http_Cookie|string $cookie
0125      * @param Zend_Uri_Http|string    $ref_uri Optional reference URI (for domain, path, secure)
0126      * @param boolean $encodeValue
0127      */
0128     public function addCookie($cookie, $ref_uri = null, $encodeValue = true)
0129     {
0130         if (is_string($cookie)) {
0131             $cookie = Zend_Http_Cookie::fromString($cookie, $ref_uri, $encodeValue);
0132         }
0133 
0134         if ($cookie instanceof Zend_Http_Cookie) {
0135             $domain = $cookie->getDomain();
0136             $path = $cookie->getPath();
0137             if (! isset($this->cookies[$domain])) $this->cookies[$domain] = array();
0138             if (! isset($this->cookies[$domain][$path])) $this->cookies[$domain][$path] = array();
0139             $this->cookies[$domain][$path][$cookie->getName()] = $cookie;
0140             $this->_rawCookies[] = $cookie;
0141         } else {
0142             // require_once 'Zend/Http/Exception.php';
0143             throw new Zend_Http_Exception('Supplient argument is not a valid cookie string or object');
0144         }
0145     }
0146 
0147     /**
0148      * Parse an HTTP response, adding all the cookies set in that response
0149      * to the cookie jar.
0150      *
0151      * @param Zend_Http_Response $response
0152      * @param Zend_Uri_Http|string $ref_uri Requested URI
0153      * @param boolean $encodeValue
0154      */
0155     public function addCookiesFromResponse($response, $ref_uri, $encodeValue = true)
0156     {
0157         if (! $response instanceof Zend_Http_Response) {
0158             // require_once 'Zend/Http/Exception.php';
0159             throw new Zend_Http_Exception('$response is expected to be a Response object, ' .
0160                 gettype($response) . ' was passed');
0161         }
0162 
0163         $cookie_hdrs = $response->getHeader('Set-Cookie');
0164 
0165         if (is_array($cookie_hdrs)) {
0166             foreach ($cookie_hdrs as $cookie) {
0167                 $this->addCookie($cookie, $ref_uri, $encodeValue);
0168             }
0169         } elseif (is_string($cookie_hdrs)) {
0170             $this->addCookie($cookie_hdrs, $ref_uri, $encodeValue);
0171         }
0172     }
0173 
0174     /**
0175      * Get all cookies in the cookie jar as an array
0176      *
0177      * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
0178      * @return array|string
0179      */
0180     public function getAllCookies($ret_as = self::COOKIE_OBJECT)
0181     {
0182         $cookies = $this->_flattenCookiesArray($this->cookies, $ret_as);
0183         if($ret_as == self::COOKIE_STRING_CONCAT_STRICT) {
0184             $cookies = rtrim(trim($cookies), ';');
0185         }
0186         return $cookies;
0187     }
0188 
0189     /**
0190      * Return an array of all cookies matching a specific request according to the request URI,
0191      * whether session cookies should be sent or not, and the time to consider as "now" when
0192      * checking cookie expiry time.
0193      *
0194      * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path)
0195      * @param boolean $matchSessionCookies Whether to send session cookies
0196      * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
0197      * @param int $now Override the current time when checking for expiry time
0198      * @return array|string
0199      */
0200     public function getMatchingCookies($uri, $matchSessionCookies = true,
0201         $ret_as = self::COOKIE_OBJECT, $now = null)
0202     {
0203         if (is_string($uri)) $uri = Zend_Uri::factory($uri);
0204         if (! $uri instanceof Zend_Uri_Http) {
0205             // require_once 'Zend/Http/Exception.php';
0206             throw new Zend_Http_Exception("Invalid URI string or object passed");
0207         }
0208 
0209         // First, reduce the array of cookies to only those matching domain and path
0210         $cookies = $this->_matchDomain($uri->getHost());
0211         $cookies = $this->_matchPath($cookies, $uri->getPath());
0212         $cookies = $this->_flattenCookiesArray($cookies, self::COOKIE_OBJECT);
0213 
0214         // Next, run Cookie->match on all cookies to check secure, time and session mathcing
0215         $ret = array();
0216         foreach ($cookies as $cookie)
0217             if ($cookie->match($uri, $matchSessionCookies, $now))
0218                 $ret[] = $cookie;
0219 
0220         // Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
0221         $ret = $this->_flattenCookiesArray($ret, $ret_as);
0222         if($ret_as == self::COOKIE_STRING_CONCAT_STRICT) {
0223             $ret = rtrim(trim($ret), ';');
0224         }
0225 
0226         return $ret;
0227     }
0228 
0229     /**
0230      * Get a specific cookie according to a URI and name
0231      *
0232      * @param Zend_Uri_Http|string $uri The uri (domain and path) to match
0233      * @param string $cookie_name The cookie's name
0234      * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings
0235      * @return Zend_Http_Cookie|string
0236      */
0237     public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT)
0238     {
0239         if (is_string($uri)) {
0240             $uri = Zend_Uri::factory($uri);
0241         }
0242 
0243         if (! $uri instanceof Zend_Uri_Http) {
0244             // require_once 'Zend/Http/Exception.php';
0245             throw new Zend_Http_Exception('Invalid URI specified');
0246         }
0247 
0248         // Get correct cookie path
0249         $path = $uri->getPath();
0250         $path = substr($path, 0, strrpos($path, '/'));
0251         if (! $path) $path = '/';
0252 
0253         if (isset($this->cookies[$uri->getHost()][$path][$cookie_name])) {
0254             $cookie = $this->cookies[$uri->getHost()][$path][$cookie_name];
0255 
0256             switch ($ret_as) {
0257                 case self::COOKIE_OBJECT:
0258                     return $cookie;
0259                     break;
0260 
0261                 case self::COOKIE_STRING_CONCAT_STRICT:
0262                     return rtrim(trim($cookie->__toString()), ';');
0263                     break;
0264 
0265                 case self::COOKIE_STRING_ARRAY:
0266                 case self::COOKIE_STRING_CONCAT:
0267                     return $cookie->__toString();
0268                     break;
0269 
0270                 default:
0271                     // require_once 'Zend/Http/Exception.php';
0272                     throw new Zend_Http_Exception("Invalid value passed for \$ret_as: {$ret_as}");
0273                     break;
0274             }
0275         } else {
0276             return false;
0277         }
0278     }
0279 
0280     /**
0281      * Helper function to recursivly flatten an array. Shoud be used when exporting the
0282      * cookies array (or parts of it)
0283      *
0284      * @param Zend_Http_Cookie|array $ptr
0285      * @param int $ret_as What value to return
0286      * @return array|string
0287      */
0288     protected function _flattenCookiesArray($ptr, $ret_as = self::COOKIE_OBJECT) {
0289         if (is_array($ptr)) {
0290             $ret = ($ret_as == self::COOKIE_STRING_CONCAT || $ret_as == self::COOKIE_STRING_CONCAT_STRICT) ? '' : array();
0291             foreach ($ptr as $item) {
0292                 if ($ret_as == self::COOKIE_STRING_CONCAT_STRICT) {
0293                     $postfix_combine = (!is_array($item) ? ' ' : '');
0294                     $ret .= $this->_flattenCookiesArray($item, $ret_as) . $postfix_combine;
0295                 } elseif ($ret_as == self::COOKIE_STRING_CONCAT) {
0296                     $ret .= $this->_flattenCookiesArray($item, $ret_as);
0297                 } else {
0298                     $ret = array_merge($ret, $this->_flattenCookiesArray($item, $ret_as));
0299                 }
0300             }
0301             return $ret;
0302         } elseif ($ptr instanceof Zend_Http_Cookie) {
0303             switch ($ret_as) {
0304                 case self::COOKIE_STRING_ARRAY:
0305                     return array($ptr->__toString());
0306                     break;
0307 
0308                 case self::COOKIE_STRING_CONCAT_STRICT:
0309                     // break intentionally omitted
0310 
0311                 case self::COOKIE_STRING_CONCAT:
0312                     return $ptr->__toString();
0313                     break;
0314 
0315                 case self::COOKIE_OBJECT:
0316                 default:
0317                     return array($ptr);
0318                     break;
0319             }
0320         }
0321 
0322         return null;
0323     }
0324 
0325     /**
0326      * Return a subset of the cookies array matching a specific domain
0327      *
0328      * @param string $domain
0329      * @return array
0330      */
0331     protected function _matchDomain($domain)
0332     {
0333         $ret = array();
0334 
0335         foreach (array_keys($this->cookies) as $cdom) {
0336             if (Zend_Http_Cookie::matchCookieDomain($cdom, $domain)) {
0337                 $ret[$cdom] = $this->cookies[$cdom];
0338             }
0339         }
0340 
0341         return $ret;
0342     }
0343 
0344     /**
0345      * Return a subset of a domain-matching cookies that also match a specified path
0346      *
0347      * @param array $dom_array
0348      * @param string $path
0349      * @return array
0350      */
0351     protected function _matchPath($domains, $path)
0352     {
0353         $ret = array();
0354 
0355         foreach ($domains as $dom => $paths_array) {
0356             foreach (array_keys($paths_array) as $cpath) {
0357                 if (Zend_Http_Cookie::matchCookiePath($cpath, $path)) {
0358                     if (! isset($ret[$dom])) {
0359                         $ret[$dom] = array();
0360                     }
0361 
0362                     $ret[$dom][$cpath] = $paths_array[$cpath];
0363                 }
0364             }
0365         }
0366 
0367         return $ret;
0368     }
0369 
0370     /**
0371      * Create a new CookieJar object and automatically load into it all the
0372      * cookies set in an Http_Response object. If $uri is set, it will be
0373      * considered as the requested URI for setting default domain and path
0374      * of the cookie.
0375      *
0376      * @param Zend_Http_Response $response HTTP Response object
0377      * @param Zend_Uri_Http|string $uri The requested URI
0378      * @return Zend_Http_CookieJar
0379      * @todo Add the $uri functionality.
0380      */
0381     public static function fromResponse(Zend_Http_Response $response, $ref_uri)
0382     {
0383         $jar = new self();
0384         $jar->addCookiesFromResponse($response, $ref_uri);
0385         return $jar;
0386     }
0387 
0388     /**
0389      * Required by Countable interface
0390      *
0391      * @return int
0392      */
0393     public function count()
0394     {
0395         return count($this->_rawCookies);
0396     }
0397 
0398     /**
0399      * Required by IteratorAggregate interface
0400      *
0401      * @return ArrayIterator
0402      */
0403     public function getIterator()
0404     {
0405         return new ArrayIterator($this->_rawCookies);
0406     }
0407 
0408     /**
0409      * Tells if the jar is empty of any cookie
0410      *
0411      * @return bool
0412      */
0413     public function isEmpty()
0414     {
0415         return count($this) == 0;
0416     }
0417 
0418     /**
0419      * Empties the cookieJar of any cookie
0420      *
0421      * @return Zend_Http_CookieJar
0422      */
0423     public function reset()
0424     {
0425         $this->cookies = $this->_rawCookies = array();
0426         return $this;
0427     }
0428 }