Add Rhymix\Framework\Cookie class with sane defaults

This commit is contained in:
Kijin Sung 2023-10-24 22:37:21 +09:00
parent 53ec02b6bb
commit 5005a09f06
4 changed files with 128 additions and 7 deletions

124
common/framework/Cookie.php Normal file
View file

@ -0,0 +1,124 @@
<?php
namespace Rhymix\Framework;
/**
* The cookie class.
*/
class Cookie
{
/**
* Get a cookie.
*
* @param string $name
* @return ?string
*/
public static function get(string $name): ?string
{
return isset($_COOKIE[$name]) ? strval($_COOKIE[$name]) : null;
}
/**
* Set a cookie.
*
* Options may contain the following keys:
* - expires (days or Unix timestamp)
* - path
* - domain
* - secure
* - httponly
* - samesite
*
* Missing options will be replaced with Rhymix security configuration
* where applicable, e.g. secure and samesite.
*
* @param string $name
* @param string $value
* @param array $options
* @return bool
*/
public static function set(string $name, string $value, array $options = []): bool
{
// Normalize samesite and sameSite, httponly and httpOnly.
$options = array_change_key_case($options, \CASE_LOWER);
// Convert the expires timestamp.
$options['expires'] = $options['expires'] ?? 0;
if ($options['expires'] < 0)
{
$options['expires'] = time() - (366 * 86400);
}
elseif ($options['expires'] > 0 && $options['expires'] < 36500)
{
$options['expires'] = time() + ($options['expires'] * 86400);
}
else
{
// Session cookie or Unix timestamp, no change
}
// Set defaults.
if (!isset($options['path']))
{
$options['path'] = \RX_BASEURL;
}
if (!isset($options['secure']))
{
$options['secure'] = \RX_SSL && !!config('session.use_ssl_cookies');
}
if (!isset($options['samesite']))
{
$options['samesite'] = 'Lax';
}
// PHP 7.3+ supports the samesite attribute natively. PHP 7.2 requires a hack.
if (\PHP_VERSION_ID >= 70300)
{
$result = setcookie($name, $value, $options);
}
else
{
$expires = $options['expires'];
$path = $options['path'] ?? \RX_BASEURL;
$domain = $options['domain'] ?? null;
$secure = $options['secure'] ?? false;
$httponly = $options['httponly'] ?? false;
if (!empty($options['samesite']))
{
$path = ($path ?: '/') . '; SameSite=' . $options['samesite'];
}
$result = setcookie($name, $value, $expires, $path, $domain, $secure, $httponly);
}
// Make the cookie immediately available server-side.
if ($result && $options['expires'] >= 0)
{
$_COOKIE[$name] = $value;
}
return $result;
}
/**
* Delete a cookie.
*
* You must pass an options array with the same values that were used to
* create the cookie, except 'expires' which doesn't apply here.
*
* @param string $name
* @param array $options
* @return bool
*/
public static function remove(string $name, array $options = []): bool
{
// Setting the expiry date to a negative number will take care of it.
$options['expires'] = -1;
$result = self::set($name, '', $options);
// Make the cookie immediately unavailable server-side.
if ($result)
{
unset($_COOKIE[$name]);
}
return $result;
}
}

View file

@ -470,13 +470,11 @@ class UA
{
if (in_array($color_scheme, ['light', 'dark']))
{
$_COOKIE['rx_color_scheme'] = $color_scheme;
setcookie('rx_color_scheme', $color_scheme, time() + 86400 * 365, \RX_BASEURL, '', !!config('session.use_ssl_cookies'));
Cookie::set('rx_color_scheme', $color_scheme, ['expires' => 365]);
}
else
{
unset($_COOKIE['rx_color_scheme']);
setcookie('rx_color_scheme', 'deleted', time() - 86400, \RX_BASEURL);
Cookie::remove('rx_color_scheme');
}
}
}