format('U'); } elseif (! is_numeric($expire)) { $expire = strtotime($expire); if ($expire === false) { throw new InvalidArgumentException('The cookie expiration time is not valid.'); } } $this->expire = 0 < $expire ? (int) $expire : 0; $this->path = empty($path) ? '/' : $path; if ($sameSite !== null) { $sameSite = strtolower($sameSite); } if (! in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE, null], true)) { throw new InvalidArgumentException('The "sameSite" parameter value is not valid.'); } $this->sameSite = $sameSite; } /** * Returns the cookie as a string. * * @return string The cookie */ public function __toString() { $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())) . '='; if ($this->getValue() === '') { $str .= 'deleted; expires=' . gmdate('D, d-M-Y H:i:s T', time() - 31536001) . '; max-age=-31536001'; } else { $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue()); if ($this->getExpiresTime() !== 0) { $str .= '; expires=' . gmdate( 'D, d-M-Y H:i:s T', $this->getExpiresTime() ) . '; max-age=' . $this->getMaxAge(); } } if ($this->getPath()) { $str .= '; path=' . $this->getPath(); } if ($this->getDomain()) { $str .= '; domain=' . $this->getDomain(); } if ($this->isSecure() === true) { $str .= '; secure'; } if ($this->isHttpOnly() === true) { $str .= '; httponly'; } if ($this->getSameSite() !== null) { $str .= '; samesite=' . $this->getSameSite(); } return $str; } /** * Creates cookie from raw header string. */ public static function fromString(string $cookie, bool $decode = false): self { $data = [ 'expires' => 0, 'path' => '/', 'domain' => '', 'secure' => false, 'httponly' => false, 'raw' => ! $decode, 'samesite' => null, ]; foreach (explode(';', $cookie) as $part) { if (! str_contains($part, '=')) { $key = trim($part); $value = true; } else { [$key, $value] = explode('=', trim($part), 2); $key = trim($key); $value = trim($value); } if (! isset($data['name'])) { $data['name'] = $decode ? urldecode($key) : $key; $data['value'] = $value === true ? null : ($decode ? urldecode($value) : $value); continue; } switch ($key = strtolower($key)) { case 'name': case 'value': break; case 'max-age': $data['expires'] = time() + (int) $value; break; default: $data[$key] = $value; break; } } return new Cookie( $data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite'] ); } /** * Gets the name of the cookie. */ public function getName(): string { return $this->name; } /** * Gets the value of the cookie. */ public function getValue(): string { return $this->value; } /** * Gets the domain that the cookie is available to. */ public function getDomain(): string { return $this->domain; } /** * Gets the time the cookie expires. */ public function getExpiresTime(): int { return $this->expire; } /** * Gets the max-age attribute. */ public function getMaxAge(): int { return $this->expire !== 0 ? $this->expire - time() : 0; } /** * Gets the path on the server in which the cookie will be available on. */ public function getPath(): string { return $this->path; } /** * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client. */ public function isSecure(): bool { return $this->secure; } /** * Checks whether the cookie will be made accessible only through the HTTP protocol. */ public function isHttpOnly(): bool { return $this->httpOnly; } /** * Whether this cookie is about to be cleared. */ public function isCleared(): bool { return $this->expire < time(); } /** * Checks if the cookie value should be sent with no url encoding. */ public function isRaw(): bool { return $this->raw; } /** * Gets the SameSite attribute. */ public function getSameSite(): ?string { return $this->sameSite; } }