parent
c0695eca2f
commit
49055e4671
@ -0,0 +1,174 @@ |
||||
<?php |
||||
// SPDX-License-Identifier: EUPL-1.2 |
||||
// Authors: see README.md |
||||
|
||||
namespace SeaCMS\Api; |
||||
|
||||
use DateTimeInterface; |
||||
use Exception; |
||||
use Throwable; |
||||
|
||||
/** |
||||
* Exception for bad http method |
||||
*/ |
||||
class Cookies |
||||
{ |
||||
/** |
||||
* list of reserved chars in cookies name |
||||
* @var array |
||||
*/ |
||||
private const RESERVED_CHARS_FROM = ['=', ',', ';', ' ', "\t", "\r", "\n", "\v", "\f"]; |
||||
/** |
||||
* list of replacements in cookies name |
||||
* @var array |
||||
*/ |
||||
private const RESERVED_CHARS_TO = ['%3D', '%2C', '%3B', '%20', '%09', '%0D', '%0A', '%0B', '%0C']; |
||||
|
||||
/** |
||||
* structured data for cookies |
||||
* first level 'domain' |
||||
* second level 'path' |
||||
* third level 'name' |
||||
* @var array |
||||
*/ |
||||
protected $data; |
||||
|
||||
/** |
||||
* cookies already sent |
||||
* @var bool |
||||
*/ |
||||
protected $sent; |
||||
|
||||
public function __construct() { |
||||
$this->data = []; |
||||
$this->sent = false; |
||||
} |
||||
|
||||
/** |
||||
* add a cookie, if existing, overwrite it |
||||
* @param string $name The name of the cookie |
||||
* @param string $value The value of the cookie |
||||
* @param int|string|DateTimeInterface $expire The time the cookie expires |
||||
* @param string $path The path on the server in which the cookie will be available on |
||||
* @param string $domain The domain that the cookie is available to |
||||
* @param bool $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 |
||||
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol |
||||
* @param string $sameSite Whether the cookie will be available for cross-site requests |
||||
* @throws Exception |
||||
*/ |
||||
public function addCookie( |
||||
string $name, |
||||
string $value = '', |
||||
$expire = 0, |
||||
string $path = '/', |
||||
string $domain = '', |
||||
bool $secure = true, |
||||
bool $httpOnly = true, |
||||
string $sameSite = 'Lax') |
||||
{ |
||||
if ($this->sent){ |
||||
throw new Exception('Cookies already sent ! Not possible to change cookies'); |
||||
} |
||||
if (empty($name)){ |
||||
throw new Exception('\'$name\' should not be empty !', 1); |
||||
} |
||||
if (!in_array($sameSite,['None','Lax','Strict'])){ |
||||
throw new Exception('\'$sameSite\' should be \'None\',\'Lax\' or \'Strict\' !', 1); |
||||
} |
||||
|
||||
// convert expiration time to a Unix timestamp |
||||
if ($expire instanceof DateTimeInterface) { |
||||
$expire = $expire->format('U'); |
||||
} elseif (is_string($expire)) { |
||||
$expire = strtotime($expire); |
||||
|
||||
if (false === $expire) { |
||||
throw new Exception('The cookie expiration time is not valid.'); |
||||
} |
||||
} elseif (!is_integer($expire)) { |
||||
$expire = 0; |
||||
} |
||||
|
||||
$expire = (0 < $expire) ? (int) $expire : 0; |
||||
|
||||
if (!array_key_exists($domain,$this->data)){ |
||||
$this->data[$domain] = []; |
||||
} |
||||
if (!array_key_exists($path,$this->data[$domain])){ |
||||
$this->data[$domain][$path] = []; |
||||
} |
||||
$this->data[$domain][$path][$name] = [ |
||||
'value' => $value, |
||||
'expire' => $expire, |
||||
'secure' => $secure, |
||||
'httpOnly' => $httpOnly, |
||||
'sameSite' => $sameSite |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* delete a cookie, if existing |
||||
* @param string $name The name of the cookie |
||||
* @param string $path The path on the server in which the cookie will be available on |
||||
* @param string $domain The domain that the cookie is available to |
||||
* @throws Exception |
||||
*/ |
||||
public function deleteCookie( |
||||
string $name, |
||||
string $path = '/', |
||||
string $domain = '') |
||||
{ |
||||
if ($this->sent){ |
||||
throw new Exception('Cookies already sent ! Not possible to change cookies'); |
||||
} |
||||
if (empty($name)){ |
||||
throw new Exception('\'$name\' should not be empty !', 1); |
||||
} |
||||
if (array_key_exists($domain,$this->data)){ |
||||
if (array_key_exists($path,$this->data[$domain])){ |
||||
if (array_key_exists($name,$this->data[$domain][$path])){ |
||||
unset($this->data[$domain][$path][$name]); |
||||
} |
||||
if (empty($this->data[$domain][$path])){ |
||||
unset($this->data[$domain][$path]); |
||||
} |
||||
} |
||||
if (empty($this->data[$domain])){ |
||||
unset($this->data[$domain]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* send cookies if not already sent |
||||
*/ |
||||
public function sendCookiesOnce() |
||||
{ |
||||
if (!$this->sent){ |
||||
$this->sent = true; |
||||
foreach($this->data as $domain => $domainCookies){ |
||||
foreach ($domainCookies as $path => $pathCookies) { |
||||
foreach ($pathCookies as $name => $values) { |
||||
try { |
||||
setcookie( |
||||
str_replace(self::RESERVED_CHARS_FROM, self::RESERVED_CHARS_TO, $name), |
||||
$values['value'], |
||||
[ |
||||
'expires' => $values['expire'], |
||||
'path' => $path, |
||||
'domain' => $domain, |
||||
'secure' => $values['secure'], |
||||
'httponly' => $values['httpOnly'], |
||||
'samesite' => $values['sameSite'] |
||||
] |
||||
); |
||||
} catch (Throwable $th) { |
||||
echo json_encode(['error'=>$th->__toString()]); |
||||
exit(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
<?php |
||||
// SPDX-License-Identifier: EUPL-1.2 |
||||
// Authors: see README.md |
||||
|
||||
namespace SeaCMS\Api; |
||||
|
||||
use Exception; |
||||
use SeaCMS\Api\JsonResponse; |
||||
use Throwable; |
||||
|
||||
/** |
||||
* Exception for bad http method |
||||
*/ |
||||
class SpecialOutputException extends Exception |
||||
{ |
||||
/** |
||||
* jsonResponse of the Exception |
||||
* @var JsonResponse |
||||
*/ |
||||
protected $jsonResponse; |
||||
|
||||
// Redefine the exception to be able to define $jsonResponse |
||||
public function __construct($message = "", $code = 0, ?Throwable $previous = null, ?JsonResponse $jsonResponse = null) { |
||||
if ($message instanceof JsonResponse){ |
||||
$this->jsonResponse = $message; |
||||
$message = "Forced output with JsonResponse"; |
||||
if (!is_integer($code)){ |
||||
$int = 0; |
||||
} |
||||
} else { |
||||
if (!is_string($message)){ |
||||
$message = ""; |
||||
} |
||||
if ($code instanceof JsonResponse){ |
||||
$this->jsonResponse = $code; |
||||
$code = 0; |
||||
} else { |
||||
if (!is_integer($code)){ |
||||
$int = 0; |
||||
} |
||||
if (is_null($jsonResponse)){ |
||||
throw new Exception("It is not possible to instanciate a SpecialOutputException because \$jsonResponse is null !"); |
||||
} else { |
||||
$this->jsonResponse = $jsonResponse; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// make sure everything is assigned properly |
||||
parent::__construct($message, $code, $previous); |
||||
} |
||||
|
||||
/** |
||||
* get JsonResponse |
||||
* @return JsonResponse |
||||
*/ |
||||
public function getJsonResponse(): JsonResponse |
||||
{ |
||||
return $this->jsonResponse; |
||||
} |
||||
} |
Loading…
Reference in new issue