266 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace GuzzleHttp\Cookie;
 | |
| 
 | |
| use Psr\Http\Message\RequestInterface;
 | |
| use Psr\Http\Message\ResponseInterface;
 | |
| 
 | |
| /**
 | |
|  * Cookie jar that stores cookies as an array
 | |
|  */
 | |
| class CookieJar implements CookieJarInterface
 | |
| {
 | |
|     /** @var SetCookie[] Loaded cookie data */
 | |
|     private $cookies = [];
 | |
| 
 | |
|     /** @var bool */
 | |
|     private $strictMode;
 | |
| 
 | |
|     /**
 | |
|      * @param bool $strictMode   Set to true to throw exceptions when invalid
 | |
|      *                           cookies are added to the cookie jar.
 | |
|      * @param array $cookieArray Array of SetCookie objects or a hash of
 | |
|      *                           arrays that can be used with the SetCookie
 | |
|      *                           constructor
 | |
|      */
 | |
|     public function __construct($strictMode = false, $cookieArray = [])
 | |
|     {
 | |
|         $this->strictMode = $strictMode;
 | |
| 
 | |
|         foreach ($cookieArray as $cookie) {
 | |
|             if (!($cookie instanceof SetCookie)) {
 | |
|                 $cookie = new SetCookie($cookie);
 | |
|             }
 | |
|             $this->setCookie($cookie);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create a new Cookie jar from an associative array and domain.
 | |
|      *
 | |
|      * @param array  $cookies Cookies to create the jar from
 | |
|      * @param string $domain  Domain to set the cookies to
 | |
|      *
 | |
|      * @return self
 | |
|      */
 | |
|     public static function fromArray(array $cookies, $domain)
 | |
|     {
 | |
|         $cookieJar = new self();
 | |
|         foreach ($cookies as $name => $value) {
 | |
|             $cookieJar->setCookie(new SetCookie([
 | |
|                 'Domain'  => $domain,
 | |
|                 'Name'    => $name,
 | |
|                 'Value'   => $value,
 | |
|                 'Discard' => true
 | |
|             ]));
 | |
|         }
 | |
| 
 | |
|         return $cookieJar;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @deprecated
 | |
|      */
 | |
|     public static function getCookieValue($value)
 | |
|     {
 | |
|         return $value;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Evaluate if this cookie should be persisted to storage
 | |
|      * that survives between requests.
 | |
|      *
 | |
|      * @param SetCookie $cookie Being evaluated.
 | |
|      * @param bool $allowSessionCookies If we should persist session cookies
 | |
|      * @return bool
 | |
|      */
 | |
|     public static function shouldPersist(
 | |
|         SetCookie $cookie,
 | |
|         $allowSessionCookies = false
 | |
|     ) {
 | |
|         if ($cookie->getExpires() || $allowSessionCookies) {
 | |
|             if (!$cookie->getDiscard()) {
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     public function toArray()
 | |
|     {
 | |
|         return array_map(function (SetCookie $cookie) {
 | |
|             return $cookie->toArray();
 | |
|         }, $this->getIterator()->getArrayCopy());
 | |
|     }
 | |
| 
 | |
|     public function clear($domain = null, $path = null, $name = null)
 | |
|     {
 | |
|         if (!$domain) {
 | |
|             $this->cookies = [];
 | |
|             return;
 | |
|         } elseif (!$path) {
 | |
|             $this->cookies = array_filter(
 | |
|                 $this->cookies,
 | |
|                 function (SetCookie $cookie) use ($path, $domain) {
 | |
|                     return !$cookie->matchesDomain($domain);
 | |
|                 }
 | |
|             );
 | |
|         } elseif (!$name) {
 | |
|             $this->cookies = array_filter(
 | |
|                 $this->cookies,
 | |
|                 function (SetCookie $cookie) use ($path, $domain) {
 | |
|                     return !($cookie->matchesPath($path) &&
 | |
|                         $cookie->matchesDomain($domain));
 | |
|                 }
 | |
|             );
 | |
|         } else {
 | |
|             $this->cookies = array_filter(
 | |
|                 $this->cookies,
 | |
|                 function (SetCookie $cookie) use ($path, $domain, $name) {
 | |
|                     return !($cookie->getName() == $name &&
 | |
|                         $cookie->matchesPath($path) &&
 | |
|                         $cookie->matchesDomain($domain));
 | |
|                 }
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public function clearSessionCookies()
 | |
|     {
 | |
|         $this->cookies = array_filter(
 | |
|             $this->cookies,
 | |
|             function (SetCookie $cookie) {
 | |
|                 return !$cookie->getDiscard() && $cookie->getExpires();
 | |
|             }
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     public function setCookie(SetCookie $cookie)
 | |
|     {
 | |
|         // If the name string is empty (but not 0), ignore the set-cookie
 | |
|         // string entirely.
 | |
|         $name = $cookie->getName();
 | |
|         if (!$name && $name !== '0') {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // Only allow cookies with set and valid domain, name, value
 | |
|         $result = $cookie->validate();
 | |
|         if ($result !== true) {
 | |
|             if ($this->strictMode) {
 | |
|                 throw new \RuntimeException('Invalid cookie: ' . $result);
 | |
|             } else {
 | |
|                 $this->removeCookieIfEmpty($cookie);
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Resolve conflicts with previously set cookies
 | |
|         foreach ($this->cookies as $i => $c) {
 | |
| 
 | |
|             // Two cookies are identical, when their path, and domain are
 | |
|             // identical.
 | |
|             if ($c->getPath() != $cookie->getPath() ||
 | |
|                 $c->getDomain() != $cookie->getDomain() ||
 | |
|                 $c->getName() != $cookie->getName()
 | |
|             ) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // The previously set cookie is a discard cookie and this one is
 | |
|             // not so allow the new cookie to be set
 | |
|             if (!$cookie->getDiscard() && $c->getDiscard()) {
 | |
|                 unset($this->cookies[$i]);
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // If the new cookie's expiration is further into the future, then
 | |
|             // replace the old cookie
 | |
|             if ($cookie->getExpires() > $c->getExpires()) {
 | |
|                 unset($this->cookies[$i]);
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // If the value has changed, we better change it
 | |
|             if ($cookie->getValue() !== $c->getValue()) {
 | |
|                 unset($this->cookies[$i]);
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // The cookie exists, so no need to continue
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         $this->cookies[] = $cookie;
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     public function count()
 | |
|     {
 | |
|         return count($this->cookies);
 | |
|     }
 | |
| 
 | |
|     public function getIterator()
 | |
|     {
 | |
|         return new \ArrayIterator(array_values($this->cookies));
 | |
|     }
 | |
| 
 | |
|     public function extractCookies(
 | |
|         RequestInterface $request,
 | |
|         ResponseInterface $response
 | |
|     ) {
 | |
|         if ($cookieHeader = $response->getHeader('Set-Cookie')) {
 | |
|             foreach ($cookieHeader as $cookie) {
 | |
|                 $sc = SetCookie::fromString($cookie);
 | |
|                 if (!$sc->getDomain()) {
 | |
|                     $sc->setDomain($request->getUri()->getHost());
 | |
|                 }
 | |
|                 $this->setCookie($sc);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public function withCookieHeader(RequestInterface $request)
 | |
|     {
 | |
|         $values = [];
 | |
|         $uri = $request->getUri();
 | |
|         $scheme = $uri->getScheme();
 | |
|         $host = $uri->getHost();
 | |
|         $path = $uri->getPath() ?: '/';
 | |
| 
 | |
|         foreach ($this->cookies as $cookie) {
 | |
|             if ($cookie->matchesPath($path) &&
 | |
|                 $cookie->matchesDomain($host) &&
 | |
|                 !$cookie->isExpired() &&
 | |
|                 (!$cookie->getSecure() || $scheme === 'https')
 | |
|             ) {
 | |
|                 $values[] = $cookie->getName() . '='
 | |
|                     . $cookie->getValue();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $values
 | |
|             ? $request->withHeader('Cookie', implode('; ', $values))
 | |
|             : $request;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * If a cookie already exists and the server asks to set it again with a
 | |
|      * null value, the cookie must be deleted.
 | |
|      *
 | |
|      * @param SetCookie $cookie
 | |
|      */
 | |
|     private function removeCookieIfEmpty(SetCookie $cookie)
 | |
|     {
 | |
|         $cookieValue = $cookie->getValue();
 | |
|         if ($cookieValue === null || $cookieValue === '') {
 | |
|             $this->clear(
 | |
|                 $cookie->getDomain(),
 | |
|                 $cookie->getPath(),
 | |
|                 $cookie->getName()
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| }
 |