OpenXE/classes/Components/Http/Cookie/Cookie.php
2021-05-21 08:49:41 +02:00

322 lines
7.2 KiB
PHP

<?php
namespace Xentral\Components\Http\Cookie;
use DateTime;
use DateTimeInterface;
use Exception;
use Xentral\Components\Http\Exception\InvalidArgumentException;
use Xentral\Components\Util\StringUtil;
class Cookie
{
/** @var string SAMESITE_LAX */
const SAMESITE_LAX = 'Lax';
/** @var string SAMESITE_STRICT */
const SAMESITE_STRICT = 'Strict';
/** @var string SAMESITE_NONE */
const SAMESITE_NONE = '';
/** @var string $name */
private $name;
/** @var string $value */
private $value;
/** @var DateTime $expire */
private $expire;
/** @var string $path */
private $path;
/** @var string $domain */
private $domain;
/** @var bool $secure */
private $secure;
/** @var bool $httpOnly */
private $httpOnly;
/** @var string $sameSite */
private $sameSite;
/**
* @param string $name
* @param string $value
* @param int $timeToLive
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httpOnly
* @param string $sameSite
*/
public function __construct(
$name,
$value,
$timeToLive = 0,
$path = '/',
$domain = '',
$secure = true,
$httpOnly = true,
$sameSite = self::SAMESITE_STRICT
) {
if (!$this->isValidCookieName($name)) {
throw new InvalidArgumentException('Invalid Cookie name.');
}
if (!$this->isValidCookieValue($value)) {
throw new InvalidArgumentException('Invalid Cookie value.');
}
$this->name = $name;
$this->value = $value;
$this->setTimeToLive($timeToLive);
$this->setPath($path);
$this->setDomain($domain);
$this->secure = $secure;
$this->httpOnly = $httpOnly;
$this->setSameSite($sameSite);
}
/**
* Returns string representation of cookie to be sent in Http response
*
* @return string
*/
public function toHttpHeader()
{
$header = sprintf('Set-Cookie: %s=%s', $this->name, $this->value);
if ($this->expire !== null) {
$header .= sprintf('; Expires=%s',
gmdate(DateTimeInterface::RFC7231, $this->expire->getTimestamp()));
}
if ($this->path !== '') {
$header .= sprintf('; Path=%s', $this->path);
}
if ($this->domain !== '') {
$header .= sprintf('; Domain=%s', $this->domain);
}
if ($this->isSecure()) {
$header .= '; Secure';
}
if ($this->isHttpOnly()) {
$header .= '; HttpOnly';
}
if (in_array($this->sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT], true)) {
$header .= sprintf('; SameSite=%s', $this->sameSite);
}
return StringUtil::toAscii($header);
}
/**
* Sets cookie expiry time to current time
*
* The client will delete this cookie.
*
* @return void
*/
public function expireNow()
{
$this->setTimeToLive(-1);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @return DateTime
*/
public function getExpire()
{
return $this->expire;
}
/**
* Sets date and time of expiration of the cookie
*
* @param DateTimeInterface $expirationDate
*
* @return void
*/
public function setExpirationDate(DateTimeInterface $expirationDate)
{
try {
$this->expire = new DateTime($expirationDate->format(DateTimeInterface::RFC7231));
} catch (Exception $e) {
$this->expire = null;
throw new InvalidArgumentException($e->getMessage());
}
}
/**
* Sets date and time of expiration based on specific time to live
*
* @param int $timeToLive in seconds
*
* @return void
*/
public function setTimeToLive($timeToLive)
{
if ($timeToLive === 0) {
$this->expire = null;
} else {
try {
$this->expire = new DateTime();
$time = time() + $timeToLive;
$this->expire->setTimestamp($time);
} catch (Exception $e) {
$this->expire = null;
throw new InvalidArgumentException($e->getMessage());
}
}
}
/**
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* @param string $path
*/
public function setPath($path)
{
if ($path === '' || $this->isValidCookieValue($path)) {
$this->path = $path;
} else {
throw new InvalidArgumentException('Invalid path value.');
}
}
/**
* @return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* @param string $domain
*/
public function setDomain($domain)
{
if ($domain === '' || $this->isValidCookieValue($domain)) {
$this->domain = $domain;
} else {
throw new InvalidArgumentException('Invalid domain value.');
}
}
/**
* @return bool
*/
public function isSecure()
{
return $this->secure;
}
/**
* Sets the Http secure flag
*
* @param bool $secure true=cookie will only be sent over secure connection
*/
public function setSecure($secure)
{
$this->secure = $secure;
}
/**
* @return bool
*/
public function isHttpOnly()
{
return $this->httpOnly;
}
/**
* Sets the HttpOnly flag
*
* @param bool $httpOnly true=cookie will only be sent in http responses
*/
public function setHttpOnly($httpOnly)
{
$this->httpOnly = $httpOnly;
}
/**
* @return string
*/
public function getSameSite()
{
return $this->sameSite;
}
/**
* Sets the sameSite token
*
*Values:
* 'lax': cookie can be sent cross-site for top-level navigation and GET, HEAD, OPTIONS and TRACE requests
* 'strict': cookie can never be sent cross-site
* '': disable the sameSite token
*
* @param string $sameSite values: 'lax'|'scrict'|'none'
*
* @return void
*/
public function setSameSite($sameSite)
{
if (!in_array($sameSite, [self::SAMESITE_LAX, self::SAMESITE_STRICT, self::SAMESITE_NONE], true)) {
throw new InvalidArgumentException('Invalid "samesite" attribute.');
}
$this->sameSite = $sameSite;
}
/**
* @return string
*/
public function __toString()
{
return $this->toHttpHeader();
}
/**
* @param $name
*
* @return bool
*/
protected function isValidCookieName($name)
{
return (bool)preg_match('/^[a-zA-Z0-9\\\\!#$%&\'*+.\-^_`|~]+$/', $name);
}
/**
* @param $value
*
* @return bool
*/
protected function isValidCookieValue($value)
{
return (bool)preg_match('/^"?[a-zA-Z0-9\\\\!#$%&\'()*+\-.\/:<=>?@\[\]^_`{|}~]+"?$/', $value);
}
}