index/src/Cache/Valkey/ValkeyBackend.php

106 lines
3.9 KiB
PHP

<?php
// ValkeyBackend.php
// Created: 2024-04-10
// Updated: 2024-04-10
namespace Index\Cache\Valkey;
use InvalidArgumentException;
use RuntimeException;
use Index\Cache\{ICacheBackend,ICacheProvider,ICacheProviderInfo};
use Index\Net\{EndPoint,UnixEndPoint};
/**
* Information about the Valkey backend.
*
* Also compatible with Redis and KeyDB.
*/
class ValkeyBackend implements ICacheBackend {
public function isAvailable(): bool {
return extension_loaded('redis');
}
/**
* Creates a Valkey cache provider.
*
* @param ValkeyProviderInfo $providerInfo Valkey provider info.
* @return ValkeyProvider Valkey provider instance.
*/
public function createProvider(ICacheProviderInfo $providerInfo): ICacheProvider {
if(!($providerInfo instanceof ValkeyProviderInfo))
throw new InvalidArgumentException('$providerInfo must by of type ValkeyProviderInfo');
return new ValkeyProvider($providerInfo);
}
/**
* Constructs a cache info instance from a dsn.
*
* The Valkey backend supports setting a username and password in the URL.
* `//username:password@` is treated as a username and password.
* `//username@` is treated as just a password despite what is normally expected of a URL. Older versions of Redis did not have the concept of usernames.
* In order to still use a username but no password for some reason you can specify `//username:@`, just inserting a colon without anything after it.
*
* The host part of the URL can be any DNS name, or special value `:unix:`, documented further down.
*
* The path part of the URL is used as a key prefix. Any prefix slashes (`/`) are trimmed and others are converted to a colon (`:`).
* Meaning `/prefix/test/` is converted to `prefix:test:`.
*
* In order to use a Unix socket path, set the host part to `:unix:` and specify `socket=/path/to/socket.sock` in the query.
*
* Other query fields include:
* - `db=<number>`: Allows you to select a different database.
* - `persist`: Uses a persistent connection.
*
* @param string|array $dsn DSN with provider information.
* @return ValkeyProviderInfo Valkey provider info instance.
*/
public function parseDsn(string|array $dsn): ICacheProviderInfo {
if(is_string($dsn)) {
$dsn = parse_url($dsn);
if($dsn === false)
throw new InvalidArgumentException('$dsn is not a valid uri.');
}
if(!isset($dsn['host']))
throw new InvalidArgumentException('Host is missing from DSN.');
$host = $dsn['host'];
$isUnix = $host === ':unix';
if(empty($dsn['user'])) {
$username = $password = '';
} else {
if(isset($dsn['pass'])) {
$username = $dsn['user'];
$password = $dsn['pass'];
} else {
$password = $dsn['user'];
$username = '';
}
}
if(!$isUnix) {
if(isset($dsn['port']))
$host .= ':' . $dsn['port'];
$endPoint = EndPoint::parse($host);
}
$prefix = str_replace('/', ':', ltrim($dsn['path'] ?? '', '/'));
parse_str(str_replace('+', '%2B', $dsn['query'] ?? ''), $query);
$unixPath = isset($query['socket']) && is_string($query['socket']) ? $query['socket'] : '';
$dbNumber = isset($query['db']) && is_string($query['db']) && ctype_digit($query['db']) ? (int)$query['db'] : 0;
$persist = isset($query['persist']);
if($isUnix) {
if(empty($unixPath))
throw new InvalidArgumentException('Unix socket path is missing from DSN.');
$endPoint = new UnixEndPoint($unixPath);
}
return new ValkeyProviderInfo($endPoint, $prefix, $persist, $username, $password, $dbNumber);
}
}