mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-11-16 04:57:15 +01:00
322 lines
7.2 KiB
PHP
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);
|
||
|
}
|
||
|
}
|