242 lines
5.5 KiB
PHP
242 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace Web;
|
|
|
|
/**
|
|
* Cookie manager for consistent cookie handling
|
|
*/
|
|
class Cookies
|
|
{
|
|
private array $defaults;
|
|
|
|
public function __construct(array $defaults = [])
|
|
{
|
|
$this->defaults = array_merge([
|
|
'expires' => 0,
|
|
'path' => '/',
|
|
'domain' => '',
|
|
'secure' => false,
|
|
'httponly' => true,
|
|
'samesite' => 'Lax'
|
|
], $defaults);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie
|
|
*/
|
|
public function set(string $name, string $value, array $options = []): bool
|
|
{
|
|
$options = array_merge($this->defaults, $options);
|
|
|
|
// Convert lifetime to expires timestamp if provided
|
|
if (isset($options['lifetime'])) {
|
|
$options['expires'] = time() + $options['lifetime'];
|
|
unset($options['lifetime']);
|
|
}
|
|
|
|
return setcookie($name, $value, $options);
|
|
}
|
|
|
|
/**
|
|
* Get a cookie value
|
|
*/
|
|
public function get(string $name, ?string $default = null): ?string
|
|
{
|
|
return $_COOKIE[$name] ?? $default;
|
|
}
|
|
|
|
/**
|
|
* Check if a cookie exists
|
|
*/
|
|
public function has(string $name): bool
|
|
{
|
|
return isset($_COOKIE[$name]);
|
|
}
|
|
|
|
/**
|
|
* Delete a cookie
|
|
*/
|
|
public function delete(string $name, array $options = []): bool
|
|
{
|
|
$options = array_merge($this->defaults, $options);
|
|
$options['expires'] = time() - 3600;
|
|
|
|
// Remove from $_COOKIE superglobal
|
|
unset($_COOKIE[$name]);
|
|
|
|
return setcookie($name, '', $options);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie that expires when browser closes
|
|
*/
|
|
public function setSession(string $name, string $value, array $options = []): bool
|
|
{
|
|
$options['expires'] = 0;
|
|
return $this->set($name, $value, $options);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie with specific lifetime in seconds
|
|
*/
|
|
public function setWithLifetime(string $name, string $value, int $lifetime, array $options = []): bool
|
|
{
|
|
$options['lifetime'] = $lifetime;
|
|
return $this->set($name, $value, $options);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie that expires in specified days
|
|
*/
|
|
public function setForDays(string $name, string $value, int $days, array $options = []): bool
|
|
{
|
|
return $this->setWithLifetime($name, $value, $days * 86400, $options);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie that expires in specified hours
|
|
*/
|
|
public function setForHours(string $name, string $value, int $hours, array $options = []): bool
|
|
{
|
|
return $this->setWithLifetime($name, $value, $hours * 3600, $options);
|
|
}
|
|
|
|
/**
|
|
* Set a cookie that expires in specified minutes
|
|
*/
|
|
public function setForMinutes(string $name, string $value, int $minutes, array $options = []): bool
|
|
{
|
|
return $this->setWithLifetime($name, $value, $minutes * 60, $options);
|
|
}
|
|
|
|
/**
|
|
* Set a forever cookie (5 years)
|
|
*/
|
|
public function forever(string $name, string $value, array $options = []): bool
|
|
{
|
|
return $this->setWithLifetime($name, $value, 157680000, $options); // 5 years
|
|
}
|
|
|
|
/**
|
|
* Get all cookies
|
|
*/
|
|
public function all(): array
|
|
{
|
|
return $_COOKIE;
|
|
}
|
|
|
|
/**
|
|
* Clear all cookies
|
|
*/
|
|
public function clear(): void
|
|
{
|
|
foreach ($_COOKIE as $name => $value) $this->delete($name);
|
|
}
|
|
|
|
/**
|
|
* Set default options
|
|
*/
|
|
public function setDefaults(array $defaults): void
|
|
{
|
|
$this->defaults = array_merge($this->defaults, $defaults);
|
|
}
|
|
|
|
/**
|
|
* Get default options
|
|
*/
|
|
public function getDefaults(): array
|
|
{
|
|
return $this->defaults;
|
|
}
|
|
|
|
/**
|
|
* Create a signed cookie value
|
|
*/
|
|
public function sign(string $value, string $secret): string
|
|
{
|
|
$signature = hash_hmac('sha256', $value, $secret);
|
|
return base64_encode($value . '|' . $signature);
|
|
}
|
|
|
|
/**
|
|
* Verify and extract signed cookie value
|
|
*/
|
|
public function verify(string $signedValue, string $secret): ?string
|
|
{
|
|
$decoded = base64_decode($signedValue);
|
|
if (!$decoded || !str_contains($decoded, '|')) return null;
|
|
|
|
[$value, $signature] = explode('|', $decoded, 2);
|
|
$expectedSignature = hash_hmac('sha256', $value, $secret);
|
|
|
|
if (!hash_equals($expectedSignature, $signature)) return null;
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Set a signed cookie
|
|
*/
|
|
public function setSigned(string $name, string $value, string $secret, array $options = []): bool
|
|
{
|
|
$signedValue = $this->sign($value, $secret);
|
|
return $this->set($name, $signedValue, $options);
|
|
}
|
|
|
|
/**
|
|
* Get and verify a signed cookie
|
|
*/
|
|
public function getSigned(string $name, string $secret, ?string $default = null): ?string
|
|
{
|
|
$signedValue = $this->get($name);
|
|
if ($signedValue === null) return $default;
|
|
|
|
$value = $this->verify($signedValue, $secret);
|
|
return $value !== null ? $value : $default;
|
|
}
|
|
|
|
/**
|
|
* Encrypt a cookie value
|
|
*/
|
|
public function encrypt(string $value, string $key): string
|
|
{
|
|
$iv = random_bytes(16);
|
|
$encrypted = openssl_encrypt($value, 'AES-256-CBC', $key, 0, $iv);
|
|
return base64_encode($iv . '|' . $encrypted);
|
|
}
|
|
|
|
/**
|
|
* Decrypt a cookie value
|
|
*/
|
|
public function decrypt(string $encryptedValue, string $key): ?string
|
|
{
|
|
$decoded = base64_decode($encryptedValue);
|
|
if (!$decoded || !str_contains($decoded, '|')) return null;
|
|
|
|
[$iv, $encrypted] = explode('|', $decoded, 2);
|
|
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv);
|
|
|
|
return $decrypted !== false ? $decrypted : null;
|
|
}
|
|
|
|
/**
|
|
* Set an encrypted cookie
|
|
*/
|
|
public function setEncrypted(string $name, string $value, string $key, array $options = []): bool
|
|
{
|
|
$encryptedValue = $this->encrypt($value, $key);
|
|
return $this->set($name, $encryptedValue, $options);
|
|
}
|
|
|
|
/**
|
|
* Get and decrypt a cookie
|
|
*/
|
|
public function getEncrypted(string $name, string $key, ?string $default = null): ?string
|
|
{
|
|
$encryptedValue = $this->get($name);
|
|
if ($encryptedValue === null) return $default;
|
|
|
|
$value = $this->decrypt($encryptedValue, $key);
|
|
return $value !== null ? $value : $default;
|
|
}
|
|
} |