Added cache backends registry.

This commit is contained in:
flash 2024-10-18 20:56:56 +00:00
parent 0ebd0fa298
commit bf7977f511
14 changed files with 181 additions and 132 deletions

View file

@ -1 +1 @@
0.2410.160124 0.2410.182054

View file

@ -34,6 +34,9 @@
"Index\\": "src" "Index\\": "src"
}, },
"files": [ "files": [
"src/Cache/ArrayCache/_ndx.php",
"src/Cache/Memcached/_ndx.php",
"src/Cache/Valkey/_ndx.php",
"src/Db/MariaDb/_ndx.php", "src/Db/MariaDb/_ndx.php",
"src/Db/NullDb/_ndx.php", "src/Db/NullDb/_ndx.php",
"src/Db/Sqlite/_ndx.php" "src/Db/Sqlite/_ndx.php"

22
composer.lock generated
View file

@ -943,16 +943,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.12.6", "version": "1.12.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae" "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc4d2f145a88ea7141ae698effd64d9df46527ae", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0",
"reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae", "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -997,7 +997,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-10-06T15:03:59+00:00" "time": "2024-10-18T11:12:07+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -1594,16 +1594,16 @@
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
"version": "6.1.0", "version": "6.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git", "url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d" "reference": "5ef523a49ae7a302b87b2102b72b1eda8918d686"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa37b9e2ca618cb051d71b60120952ee8ca8b03d", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5ef523a49ae7a302b87b2102b72b1eda8918d686",
"reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d", "reference": "5ef523a49ae7a302b87b2102b72b1eda8918d686",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1659,7 +1659,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues", "issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy", "security": "https://github.com/sebastianbergmann/comparator/security/policy",
"source": "https://github.com/sebastianbergmann/comparator/tree/6.1.0" "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.1"
}, },
"funding": [ "funding": [
{ {
@ -1667,7 +1667,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-09-11T15:42:56+00:00" "time": "2024-10-18T15:00:48+00:00"
}, },
{ {
"name": "sebastian/complexity", "name": "sebastian/complexity",

View file

@ -0,0 +1,11 @@
<?php
// _ndx.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Cache\ArrayCache;
use Index\Cache\CacheBackends;
CacheBackends::register('array', ArrayCacheBackend::class);
CacheBackends::register('null', ArrayCacheBackend::class);

108
src/Cache/CacheBackends.php Normal file
View file

@ -0,0 +1,108 @@
<?php
// CacheBackends.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Cache;
use InvalidArgumentException;
use RuntimeException;
/**
* Provides a registry of cache backends.
*/
final class CacheBackends {
/** @var array<string, string> */
private static array $schemes = [];
/** @var array<string, CacheBackend> */
private static array $backends = [];
/**
* @throws RuntimeException Will always throw because this is a static class exclusively.
*/
public function __construct() {
throw new RuntimeException('This is a static class, you cannot create an instance of it.');
}
/**
* Registers a cache backend with a protocol scheme.
*
* @param string $scheme URL scheme.
* @param string $className Fully qualified class name of the backend.
* @throws InvalidArgumentException If $scheme is already registered.
*/
public static function register(string $scheme, string $className): void {
$scheme = strtolower($scheme);
if(array_key_exists($scheme, self::$schemes))
throw new InvalidArgumentException('$scheme has already been registered');
self::$schemes[$scheme] = $className;
}
/**
* Creates a cache provider from a DSN URI.
*
* @param string $uri DSN URI.
* @throws RuntimeException If the requested cache backend is not available.
* @return CacheProvider Connection instance.
*/
public static function create(string $uri): CacheProvider {
$parsed = self::parseUri($uri);
$backend = self::backend($parsed['scheme']);
if(!$backend->isAvailable())
throw new RuntimeException('requested cache backend is not available');
return $backend->createProvider($backend->parseDsn($parsed));
}
/**
* Parses a DSN URI into a provider info class.
*
* @param string $uri DSN URI.
* @return CacheProviderInfo Provider info instance.
*/
public static function parse(string $uri): CacheProviderInfo {
$parsed = self::parseUri($uri);
return self::backend($parsed['scheme'])->parseDsn($parsed);
}
/**
* Creates a cache provider information class from a scheme.
*
* @param string $scheme Cache protocol scheme.
* @throws InvalidArgumentException If $scheme is not a registered protocol scheme.
* @throws RuntimeException If the implementation class is not available or doesn't implement CacheBackend.
* @return CacheBackend Cache backend information instance.
*/
public static function backend(string $scheme): CacheBackend {
$scheme = strtolower($scheme);
if(!array_key_exists($scheme, self::$schemes))
throw new InvalidArgumentException('$scheme is not a registered scheme');
$className = self::$schemes[$scheme];
if(array_key_exists($className, self::$backends))
return self::$backends[$className];
if(!class_exists($className))
throw new RuntimeException('implementation class for the given $scheme does not exist');
$backend = new $className;
if(!($backend instanceof CacheBackend))
throw new RuntimeException('implementation class for the given $scheme does not implement CacheBackend');
self::$backends[$className] = $backend;
return $backend;
}
/** @return array{scheme: string, host?: string, port?: int, user?: string, pass?: string, path?: string, query?: string, fragment?: string} */
private static function parseUri(string $uri): array {
$uri = parse_url($uri);
if($uri === false)
throw new InvalidArgumentException('$uri is not a valid uri');
if(empty($uri['scheme']))
throw new InvalidArgumentException('$uri must contain a non-empty scheme component');
return $uri;
}
}

View file

@ -1,112 +0,0 @@
<?php
// CacheTools.php
// Created: 2024-04-10
// Updated: 2024-10-16
namespace Index\Cache;
use InvalidArgumentException;
use RuntimeException;
/**
* Common cache actions.
*
* DSN documentation:
*
* CacheTools only handles the scheme part of the URL,
* the rest of the URL is described in the documentation of the parseDsn implementation of the respective backend.
*
* The scheme can be a PHP classpath to an implementation of CacheBackend, or any of these aliases:
* - `array`, `null`: maps to `ArrayCache\ArrayCacheBackend`, uses a simple array backed cache that doesn't get saved.
* - `memcached`, `memcache`: maps to `Memcached\MemcachedBackend`, provides a backend based on the memcached or memcache extension.
* - `valkey`, `keydb`, `redis`: maps to `Valkey\ValkeyBackend`, provides a backend based on the phpredis extension.
*
* Short names are currently hardcoded and cannot be expanded.
*/
final class CacheTools {
private const CACHE_PROTOS = [
'array' => ArrayCache\ArrayCacheBackend::class,
'null' => ArrayCache\ArrayCacheBackend::class,
'memcached' => Memcached\MemcachedBackend::class,
'memcache' => Memcached\MemcachedBackend::class,
'valkey' => Valkey\ValkeyBackend::class,
'redis' => Valkey\ValkeyBackend::class,
'keydb' => Valkey\ValkeyBackend::class,
];
/** @return array<string, int|string> */
private static function parseDsnUri(string $dsn): array {
$uri = parse_url($dsn);
if($uri === false)
throw new InvalidArgumentException('$dsn is not a valid uri.');
return $uri;
}
/** @param array<string, int|string> $uri */
private static function resolveBackend(array $uri): CacheBackend {
static $backends = null;
if(!is_array($backends))
$backends = [];
$scheme = $uri['scheme'];
$backend = $backends[$scheme] ?? null;
if(!($backend instanceof CacheBackend)) {
if(!array_key_exists($scheme, self::CACHE_PROTOS))
throw new RuntimeException('No implementation is available for the specified scheme.');
$backend = new (self::CACHE_PROTOS[$scheme]);
if(!$backend->isAvailable())
throw new RuntimeException('Requested cache backend is not available, likely due to missing dependencies.');
$backends[$scheme] = $backend;
}
return $backend;
}
/**
* Retrieves cache provider based on DSN URL.
*
* Format of the DSN URLs are described in the documentation of the CacheTools class as a whole.
*
* @param string $dsn URL to create cache provider from.
* @throws InvalidArgumentException if $dsn is not a valid URL.
* @throws RuntimeException if no cache provider can be made using the URL.
* @return CacheBackend Cache backend instance.
*/
public static function backend(string $dsn): CacheBackend {
return self::resolveBackend(self::parseDsnUri($dsn));
}
/**
* Parses a DSN URL.
*
* Format of the DSN URLs are described in the documentation of the CacheTools class as a whole.
*
* @param string $dsn URL to create cache provider from.
* @throws InvalidArgumentException if $dsn is not a valid URL.
* @throws RuntimeException if no cache provider can be made using the URL.
* @return CacheProviderInfo Cache provider info.
*/
public static function parse(string $dsn): CacheProviderInfo {
$uri = self::parseDsnUri($dsn);
return self::resolveBackend($uri)->parseDsn($uri);
}
/**
* Uses a DSN URL to create a cache provider instance.
*
* Format of the DSN URLs are described in the documentation of the CacheTools class as a whole.
*
* @param string $dsn URL to create cache provider from.
* @throws InvalidArgumentException if $dsn is not a valid URL.
* @throws RuntimeException if no cache provider can be made using the URL.
* @return CacheProvider A cache provider.
*/
public static function create(string $dsn): CacheProvider {
$uri = self::parseDsnUri($dsn);
$backend = self::resolveBackend($uri);
return $backend->createProvider($backend->parseDsn($uri));
}
}

View file

@ -0,0 +1,11 @@
<?php
// _ndx.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Cache\Memcached;
use Index\Cache\CacheBackends;
CacheBackends::register('memcached', MemcachedBackend::class);
CacheBackends::register('memcache', MemcachedBackend::class);

12
src/Cache/Valkey/_ndx.php Normal file
View file

@ -0,0 +1,12 @@
<?php
// _ndx.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Cache\Valkey;
use Index\Cache\CacheBackends;
CacheBackends::register('valkey', ValkeyBackend::class);
CacheBackends::register('redis', ValkeyBackend::class);
CacheBackends::register('keydb', ValkeyBackend::class);

View file

@ -1,4 +1,8 @@
<?php <?php
// DbBackends.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Db; namespace Index\Db;
use InvalidArgumentException; use InvalidArgumentException;
@ -25,7 +29,7 @@ final class DbBackends {
* Registers a database backend with a protocol scheme. * Registers a database backend with a protocol scheme.
* *
* @param string $scheme URL scheme. * @param string $scheme URL scheme.
* @param string $className Fully qualified class name of backend. * @param string $className Fully qualified class name of the backend.
* @throws InvalidArgumentException If $scheme is already registered. * @throws InvalidArgumentException If $scheme is already registered.
*/ */
public static function register(string $scheme, string $className): void { public static function register(string $scheme, string $className): void {

View file

@ -1,4 +1,8 @@
<?php <?php
// _ndx.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Db\MariaDb; namespace Index\Db\MariaDb;
use Index\Db\DbBackends; use Index\Db\DbBackends;

View file

@ -1,4 +1,8 @@
<?php <?php
// _ndx.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Db\NullDb; namespace Index\Db\NullDb;
use Index\Db\DbBackends; use Index\Db\DbBackends;

View file

@ -1,4 +1,8 @@
<?php <?php
// _ndx.php
// Created: 2024-10-18
// Updated: 2024-10-18
namespace Index\Db\Sqlite; namespace Index\Db\Sqlite;
use Index\Db\DbBackends; use Index\Db\DbBackends;

View file

@ -1,19 +1,19 @@
<?php <?php
// CacheToolsTest.php // CacheBackendsTest.php
// Created: 2024-04-10 // Created: 2024-04-10
// Updated: 2024-10-16 // Updated: 2024-10-18
declare(strict_types=1); declare(strict_types=1);
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\{CoversClass,UsesClass}; use PHPUnit\Framework\Attributes\{CoversClass,UsesClass};
use Index\Cache\CacheTools; use Index\Cache\CacheBackends;
use Index\Cache\ArrayCache\{ArrayCacheBackend,ArrayCacheProvider}; use Index\Cache\ArrayCache\{ArrayCacheBackend,ArrayCacheProvider};
use Index\Cache\Memcached\MemcachedBackend; use Index\Cache\Memcached\MemcachedBackend;
use Index\Cache\Valkey\ValkeyBackend; use Index\Cache\Valkey\ValkeyBackend;
use Index\Net\{DnsEndPoint,IpEndPoint,UnixEndPoint}; use Index\Net\{DnsEndPoint,IpEndPoint,UnixEndPoint};
#[CoversClass(CacheTools::class)] #[CoversClass(CacheBackends::class)]
#[CoversClass(ArrayCacheBackend::class)] #[CoversClass(ArrayCacheBackend::class)]
#[CoversClass(ArrayCacheProvider::class)] #[CoversClass(ArrayCacheProvider::class)]
#[CoversClass(MemcachedBackend::class)] #[CoversClass(MemcachedBackend::class)]
@ -21,9 +21,9 @@ use Index\Net\{DnsEndPoint,IpEndPoint,UnixEndPoint};
#[UsesClass(DnsEndPoint::class)] #[UsesClass(DnsEndPoint::class)]
#[UsesClass(IpEndPoint::class)] #[UsesClass(IpEndPoint::class)]
#[UsesClass(UnixEndPoint::class)] #[UsesClass(UnixEndPoint::class)]
final class CacheToolsTest extends TestCase { final class CacheBackendsTest extends TestCase {
public function testBasicDSN(): void { public function testBasicDSN(): void {
$arrayCache = CacheTools::create('array:'); $arrayCache = CacheBackends::create('array:');
$this->assertInstanceOf(ArrayCacheProvider::class, $arrayCache); $this->assertInstanceOf(ArrayCacheProvider::class, $arrayCache);
} }

View file

@ -1,5 +1,5 @@
<?php <?php
// DbToolsTest.php // DbBackendsTest.php
// Created: 2021-04-28 // Created: 2021-04-28
// Updated: 2024-10-16 // Updated: 2024-10-16