vendor/symfony/http-foundation/Cookie.php line 19

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\HttpFoundation;
  11. /**
  12.  * Represents a cookie.
  13.  *
  14.  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  15.  */
  16. class Cookie
  17. {
  18.     public const SAMESITE_NONE 'none';
  19.     public const SAMESITE_LAX 'lax';
  20.     public const SAMESITE_STRICT 'strict';
  21.     protected $name;
  22.     protected $value;
  23.     protected $domain;
  24.     protected $expire;
  25.     protected $path;
  26.     protected $secure;
  27.     protected $httpOnly;
  28.     private $raw;
  29.     private $sameSite;
  30.     private $secureDefault false;
  31.     private static $reservedCharsList "=,; \t\r\n\v\f";
  32.     private const RESERVED_CHARS_FROM = ['='','';'' '"\t""\r""\n""\v""\f"];
  33.     private const RESERVED_CHARS_TO = ['%3D''%2C''%3B''%20''%09''%0D''%0A''%0B''%0C'];
  34.     /**
  35.      * Creates cookie from raw header string.
  36.      *
  37.      * @return static
  38.      */
  39.     public static function fromString(string $cookiebool $decode false)
  40.     {
  41.         $data = [
  42.             'expires' => 0,
  43.             'path' => '/',
  44.             'domain' => null,
  45.             'secure' => false,
  46.             'httponly' => false,
  47.             'raw' => !$decode,
  48.             'samesite' => null,
  49.         ];
  50.         $parts HeaderUtils::split($cookie';=');
  51.         $part array_shift($parts);
  52.         $name $decode urldecode($part[0]) : $part[0];
  53.         $value = isset($part[1]) ? ($decode urldecode($part[1]) : $part[1]) : null;
  54.         $data HeaderUtils::combine($parts) + $data;
  55.         $data['expires'] = self::expiresTimestamp($data['expires']);
  56.         if (isset($data['max-age']) && ($data['max-age'] > || $data['expires'] > time())) {
  57.             $data['expires'] = time() + (int) $data['max-age'];
  58.         }
  59.         return new static($name$value$data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
  60.     }
  61.     public static function create(string $namestring $value null$expire 0, ?string $path '/'string $domain nullbool $secure nullbool $httpOnly truebool $raw false, ?string $sameSite self::SAMESITE_LAX): self
  62.     {
  63.         return new self($name$value$expire$path$domain$secure$httpOnly$raw$sameSite);
  64.     }
  65.     /**
  66.      * @param string                        $name     The name of the cookie
  67.      * @param string|null                   $value    The value of the cookie
  68.      * @param int|string|\DateTimeInterface $expire   The time the cookie expires
  69.      * @param string                        $path     The path on the server in which the cookie will be available on
  70.      * @param string|null                   $domain   The domain that the cookie is available to
  71.      * @param bool|null                     $secure   Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
  72.      * @param bool                          $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
  73.      * @param bool                          $raw      Whether the cookie value should be sent with no url encoding
  74.      * @param string|null                   $sameSite Whether the cookie will be available for cross-site requests
  75.      *
  76.      * @throws \InvalidArgumentException
  77.      */
  78.     public function __construct(string $namestring $value null$expire 0, ?string $path '/'string $domain nullbool $secure nullbool $httpOnly truebool $raw false, ?string $sameSite 'lax')
  79.     {
  80.         // from PHP source code
  81.         if ($raw && false !== strpbrk($nameself::$reservedCharsList)) {
  82.             throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.'$name));
  83.         }
  84.         if (empty($name)) {
  85.             throw new \InvalidArgumentException('The cookie name cannot be empty.');
  86.         }
  87.         $this->name $name;
  88.         $this->value $value;
  89.         $this->domain $domain;
  90.         $this->expire self::expiresTimestamp($expire);
  91.         $this->path = empty($path) ? '/' $path;
  92.         $this->secure $secure;
  93.         $this->httpOnly $httpOnly;
  94.         $this->raw $raw;
  95.         $this->sameSite $this->withSameSite($sameSite)->sameSite;
  96.     }
  97.     /**
  98.      * Creates a cookie copy with a new value.
  99.      *
  100.      * @return static
  101.      */
  102.     public function withValue(?string $value): self
  103.     {
  104.         $cookie = clone $this;
  105.         $cookie->value $value;
  106.         return $cookie;
  107.     }
  108.     /**
  109.      * Creates a cookie copy with a new domain that the cookie is available to.
  110.      *
  111.      * @return static
  112.      */
  113.     public function withDomain(?string $domain): self
  114.     {
  115.         $cookie = clone $this;
  116.         $cookie->domain $domain;
  117.         return $cookie;
  118.     }
  119.     /**
  120.      * Creates a cookie copy with a new time the cookie expires.
  121.      *
  122.      * @param int|string|\DateTimeInterface $expire
  123.      *
  124.      * @return static
  125.      */
  126.     public function withExpires($expire 0): self
  127.     {
  128.         $cookie = clone $this;
  129.         $cookie->expire self::expiresTimestamp($expire);
  130.         return $cookie;
  131.     }
  132.     /**
  133.      * Converts expires formats to a unix timestamp.
  134.      *
  135.      * @param int|string|\DateTimeInterface $expire
  136.      *
  137.      * @return int
  138.      */
  139.     private static function expiresTimestamp($expire 0)
  140.     {
  141.         // convert expiration time to a Unix timestamp
  142.         if ($expire instanceof \DateTimeInterface) {
  143.             $expire $expire->format('U');
  144.         } elseif (!is_numeric($expire)) {
  145.             $expire strtotime($expire);
  146.             if (false === $expire) {
  147.                 throw new \InvalidArgumentException('The cookie expiration time is not valid.');
  148.             }
  149.         }
  150.         return $expire ? (int) $expire 0;
  151.     }
  152.     /**
  153.      * Creates a cookie copy with a new path on the server in which the cookie will be available on.
  154.      *
  155.      * @return static
  156.      */
  157.     public function withPath(string $path): self
  158.     {
  159.         $cookie = clone $this;
  160.         $cookie->path '' === $path '/' $path;
  161.         return $cookie;
  162.     }
  163.     /**
  164.      * Creates a cookie copy that only be transmitted over a secure HTTPS connection from the client.
  165.      *
  166.      * @return static
  167.      */
  168.     public function withSecure(bool $secure true): self
  169.     {
  170.         $cookie = clone $this;
  171.         $cookie->secure $secure;
  172.         return $cookie;
  173.     }
  174.     /**
  175.      * Creates a cookie copy that be accessible only through the HTTP protocol.
  176.      *
  177.      * @return static
  178.      */
  179.     public function withHttpOnly(bool $httpOnly true): self
  180.     {
  181.         $cookie = clone $this;
  182.         $cookie->httpOnly $httpOnly;
  183.         return $cookie;
  184.     }
  185.     /**
  186.      * Creates a cookie copy that uses no url encoding.
  187.      *
  188.      * @return static
  189.      */
  190.     public function withRaw(bool $raw true): self
  191.     {
  192.         if ($raw && false !== strpbrk($this->nameself::$reservedCharsList)) {
  193.             throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.'$this->name));
  194.         }
  195.         $cookie = clone $this;
  196.         $cookie->raw $raw;
  197.         return $cookie;
  198.     }
  199.     /**
  200.      * Creates a cookie copy with SameSite attribute.
  201.      *
  202.      * @return static
  203.      */
  204.     public function withSameSite(?string $sameSite): self
  205.     {
  206.         if ('' === $sameSite) {
  207.             $sameSite null;
  208.         } elseif (null !== $sameSite) {
  209.             $sameSite strtolower($sameSite);
  210.         }
  211.         if (!\in_array($sameSite, [self::SAMESITE_LAXself::SAMESITE_STRICTself::SAMESITE_NONEnull], true)) {
  212.             throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.');
  213.         }
  214.         $cookie = clone $this;
  215.         $cookie->sameSite $sameSite;
  216.         return $cookie;
  217.     }
  218.     /**
  219.      * Returns the cookie as a string.
  220.      *
  221.      * @return string The cookie
  222.      */
  223.     public function __toString()
  224.     {
  225.         if ($this->isRaw()) {
  226.             $str $this->getName();
  227.         } else {
  228.             $str str_replace(self::RESERVED_CHARS_FROMself::RESERVED_CHARS_TO$this->getName());
  229.         }
  230.         $str .= '=';
  231.         if ('' === (string) $this->getValue()) {
  232.             $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T'time() - 31536001).'; Max-Age=0';
  233.         } else {
  234.             $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue());
  235.             if (!== $this->getExpiresTime()) {
  236.                 $str .= '; expires='.gmdate('D, d-M-Y H:i:s T'$this->getExpiresTime()).'; Max-Age='.$this->getMaxAge();
  237.             }
  238.         }
  239.         if ($this->getPath()) {
  240.             $str .= '; path='.$this->getPath();
  241.         }
  242.         if ($this->getDomain()) {
  243.             $str .= '; domain='.$this->getDomain();
  244.         }
  245.         if (true === $this->isSecure()) {
  246.             $str .= '; secure';
  247.         }
  248.         if (true === $this->isHttpOnly()) {
  249.             $str .= '; httponly';
  250.         }
  251.         if (null !== $this->getSameSite()) {
  252.             $str .= '; samesite='.$this->getSameSite();
  253.         }
  254.         return $str;
  255.     }
  256.     /**
  257.      * Gets the name of the cookie.
  258.      *
  259.      * @return string
  260.      */
  261.     public function getName()
  262.     {
  263.         return $this->name;
  264.     }
  265.     /**
  266.      * Gets the value of the cookie.
  267.      *
  268.      * @return string|null
  269.      */
  270.     public function getValue()
  271.     {
  272.         return $this->value;
  273.     }
  274.     /**
  275.      * Gets the domain that the cookie is available to.
  276.      *
  277.      * @return string|null
  278.      */
  279.     public function getDomain()
  280.     {
  281.         return $this->domain;
  282.     }
  283.     /**
  284.      * Gets the time the cookie expires.
  285.      *
  286.      * @return int
  287.      */
  288.     public function getExpiresTime()
  289.     {
  290.         return $this->expire;
  291.     }
  292.     /**
  293.      * Gets the max-age attribute.
  294.      *
  295.      * @return int
  296.      */
  297.     public function getMaxAge()
  298.     {
  299.         $maxAge $this->expire time();
  300.         return >= $maxAge $maxAge;
  301.     }
  302.     /**
  303.      * Gets the path on the server in which the cookie will be available on.
  304.      *
  305.      * @return string
  306.      */
  307.     public function getPath()
  308.     {
  309.         return $this->path;
  310.     }
  311.     /**
  312.      * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client.
  313.      *
  314.      * @return bool
  315.      */
  316.     public function isSecure()
  317.     {
  318.         return $this->secure ?? $this->secureDefault;
  319.     }
  320.     /**
  321.      * Checks whether the cookie will be made accessible only through the HTTP protocol.
  322.      *
  323.      * @return bool
  324.      */
  325.     public function isHttpOnly()
  326.     {
  327.         return $this->httpOnly;
  328.     }
  329.     /**
  330.      * Whether this cookie is about to be cleared.
  331.      *
  332.      * @return bool
  333.      */
  334.     public function isCleared()
  335.     {
  336.         return !== $this->expire && $this->expire time();
  337.     }
  338.     /**
  339.      * Checks if the cookie value should be sent with no url encoding.
  340.      *
  341.      * @return bool
  342.      */
  343.     public function isRaw()
  344.     {
  345.         return $this->raw;
  346.     }
  347.     /**
  348.      * Gets the SameSite attribute.
  349.      *
  350.      * @return string|null
  351.      */
  352.     public function getSameSite()
  353.     {
  354.         return $this->sameSite;
  355.     }
  356.     /**
  357.      * @param bool $default The default value of the "secure" flag when it is set to null
  358.      */
  359.     public function setSecureDefault(bool $default): void
  360.     {
  361.         $this->secureDefault $default;
  362.     }
  363. }