2 namespace GuzzleHttp\Cookie;
4 use Psr\Http\Message\RequestInterface;
5 use Psr\Http\Message\ResponseInterface;
8 * Cookie jar that stores cookies as an array
10 class CookieJar implements CookieJarInterface
12 /** @var SetCookie[] Loaded cookie data */
13 private $cookies = [];
19 * @param bool $strictMode Set to true to throw exceptions when invalid
20 * cookies are added to the cookie jar.
21 * @param array $cookieArray Array of SetCookie objects or a hash of
22 * arrays that can be used with the SetCookie
25 public function __construct($strictMode = false, $cookieArray = [])
27 $this->strictMode = $strictMode;
29 foreach ($cookieArray as $cookie) {
30 if (!($cookie instanceof SetCookie)) {
31 $cookie = new SetCookie($cookie);
33 $this->setCookie($cookie);
38 * Create a new Cookie jar from an associative array and domain.
40 * @param array $cookies Cookies to create the jar from
41 * @param string $domain Domain to set the cookies to
45 public static function fromArray(array $cookies, $domain)
47 $cookieJar = new self();
48 foreach ($cookies as $name => $value) {
49 $cookieJar->setCookie(new SetCookie([
63 public static function getCookieValue($value)
69 * Evaluate if this cookie should be persisted to storage
70 * that survives between requests.
72 * @param SetCookie $cookie Being evaluated.
73 * @param bool $allowSessionCookies If we should persist session cookies
76 public static function shouldPersist(
78 $allowSessionCookies = false
80 if ($cookie->getExpires() || $allowSessionCookies) {
81 if (!$cookie->getDiscard()) {
89 public function toArray()
91 return array_map(function (SetCookie $cookie) {
92 return $cookie->toArray();
93 }, $this->getIterator()->getArrayCopy());
96 public function clear($domain = null, $path = null, $name = null)
102 $this->cookies = array_filter(
104 function (SetCookie $cookie) use ($path, $domain) {
105 return !$cookie->matchesDomain($domain);
109 $this->cookies = array_filter(
111 function (SetCookie $cookie) use ($path, $domain) {
112 return !($cookie->matchesPath($path) &&
113 $cookie->matchesDomain($domain));
117 $this->cookies = array_filter(
119 function (SetCookie $cookie) use ($path, $domain, $name) {
120 return !($cookie->getName() == $name &&
121 $cookie->matchesPath($path) &&
122 $cookie->matchesDomain($domain));
128 public function clearSessionCookies()
130 $this->cookies = array_filter(
132 function (SetCookie $cookie) {
133 return !$cookie->getDiscard() && $cookie->getExpires();
138 public function setCookie(SetCookie $cookie)
140 // If the name string is empty (but not 0), ignore the set-cookie
142 $name = $cookie->getName();
143 if (!$name && $name !== '0') {
147 // Only allow cookies with set and valid domain, name, value
148 $result = $cookie->validate();
149 if ($result !== true) {
150 if ($this->strictMode) {
151 throw new \RuntimeException('Invalid cookie: ' . $result);
153 $this->removeCookieIfEmpty($cookie);
158 // Resolve conflicts with previously set cookies
159 foreach ($this->cookies as $i => $c) {
161 // Two cookies are identical, when their path, and domain are
163 if ($c->getPath() != $cookie->getPath() ||
164 $c->getDomain() != $cookie->getDomain() ||
165 $c->getName() != $cookie->getName()
170 // The previously set cookie is a discard cookie and this one is
171 // not so allow the new cookie to be set
172 if (!$cookie->getDiscard() && $c->getDiscard()) {
173 unset($this->cookies[$i]);
177 // If the new cookie's expiration is further into the future, then
178 // replace the old cookie
179 if ($cookie->getExpires() > $c->getExpires()) {
180 unset($this->cookies[$i]);
184 // If the value has changed, we better change it
185 if ($cookie->getValue() !== $c->getValue()) {
186 unset($this->cookies[$i]);
190 // The cookie exists, so no need to continue
194 $this->cookies[] = $cookie;
199 public function count()
201 return count($this->cookies);
204 public function getIterator()
206 return new \ArrayIterator(array_values($this->cookies));
209 public function extractCookies(
210 RequestInterface $request,
211 ResponseInterface $response
213 if ($cookieHeader = $response->getHeader('Set-Cookie')) {
214 foreach ($cookieHeader as $cookie) {
215 $sc = SetCookie::fromString($cookie);
216 if (!$sc->getDomain()) {
217 $sc->setDomain($request->getUri()->getHost());
219 $this->setCookie($sc);
224 public function withCookieHeader(RequestInterface $request)
227 $uri = $request->getUri();
228 $scheme = $uri->getScheme();
229 $host = $uri->getHost();
230 $path = $uri->getPath() ?: '/';
232 foreach ($this->cookies as $cookie) {
233 if ($cookie->matchesPath($path) &&
234 $cookie->matchesDomain($host) &&
235 !$cookie->isExpired() &&
236 (!$cookie->getSecure() || $scheme === 'https')
238 $values[] = $cookie->getName() . '='
239 . $cookie->getValue();
244 ? $request->withHeader('Cookie', implode('; ', $values))
249 * If a cookie already exists and the server asks to set it again with a
250 * null value, the cookie must be deleted.
252 * @param SetCookie $cookie
254 private function removeCookieIfEmpty(SetCookie $cookie)
256 $cookieValue = $cookie->getValue();
257 if ($cookieValue === null || $cookieValue === '') {
259 $cookie->getDomain(),