diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 82e2934..65380c9 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -49,5 +49,6 @@ jobs: with: folder: php project: ${{ github.event.repository.name }} - secrets: inherit + secrets: + DOC_TOKEN: ${{ secrets.DOC_TOKEN }} diff --git a/docs/class-memcached-engine.md b/docs/class-memcached-engine.md index 46cfe63..30831cb 100644 --- a/docs/class-memcached-engine.md +++ b/docs/class-memcached-engine.md @@ -5,30 +5,44 @@ This class uses the Memcached as the cache engine. ## Defining the Servers The constructor expects an array of servers. -Each server is an item in the array with the following format: +Each server can be provided in one of the following formats: ```php $servers = [ 'localhost:11211', -] + ['host.example', 11211], +]; +``` + +You can also pass Memcached client options (no need to pass a Memcached instance). Options can be provided as an associative array where the keys are Memcached option constants or their string names: + +```php +$options = [ + \Memcached::OPT_DISTRIBUTION => \Memcached::DISTRIBUTION_CONSISTENT, + \Memcached::OPT_LIBKETAMA_COMPATIBLE => true, + \Memcached::OPT_REMOVE_FAILED_SERVERS => true, + \Memcached::OPT_CONNECT_TIMEOUT => 100, // ms + // Or using string keys: + 'OPT_CONNECT_TIMEOUT' => 100, +]; ``` ## PSR-16 Constructor ```php -$cache = new \ByJG\Cache\Psr16\MemcachedEngine($servers) +$cache = new \ByJG\Cache\Psr16\MemcachedEngine($servers, null, $options); ``` ## PSR-6 Constructor ```php -$cachePool = \ByJG\Cache\Factory::createMemcachedPool($servers) +$cachePool = \ByJG\Cache\Factory::createMemcachedPool($servers, 10, null, $options) ``` or ```php -$cachePool = new \ByJG\Cache\Psr6\CachePool(new \ByJG\Cache\Psr16\MemcachedEngine($servers)); +$cachePool = new \ByJG\Cache\Psr6\CachePool(new \ByJG\Cache\Psr16\MemcachedEngine($servers, null, $options)); ``` diff --git a/src/Factory.php b/src/Factory.php index 61c6148..f6a2c6d 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -54,10 +54,10 @@ public static function createArrayPool(int $bufferSize = 10, ?LoggerInterface $l ); } - public static function createMemcachedPool(?array $servers = null, int $bufferSize = 10, ?LoggerInterface $logger = null): CachePool + public static function createMemcachedPool(?array $servers = null, int $bufferSize = 10, ?LoggerInterface $logger = null, ?array $options = null): CachePool { return new CachePool( - new MemcachedEngine($servers, $logger), + new MemcachedEngine($servers, $logger, $options), $bufferSize ); } diff --git a/src/Psr16/MemcachedEngine.php b/src/Psr16/MemcachedEngine.php index 117cc38..8e2ea56 100644 --- a/src/Psr16/MemcachedEngine.php +++ b/src/Psr16/MemcachedEngine.php @@ -25,7 +25,9 @@ class MemcachedEngine extends BaseCacheEngine implements AtomicOperationInterfac protected ?array $servers = null; - public function __construct(?array $servers = null, $logger = null) + protected ?array $options = null; + + public function __construct(?array $servers = null, $logger = null, ?array $options = null) { $this->servers = (array)$servers; if (is_null($servers)) { @@ -34,10 +36,12 @@ public function __construct(?array $servers = null, $logger = null) ]; } - $this->logger = $logger; - if (is_null($logger)) { + $this->logger = $logger instanceof LoggerInterface ? $logger : null; + if (is_null($this->logger)) { $this->logger = new NullLogger(); } + + $this->options = $options; } /** @@ -58,13 +62,49 @@ protected function lazyLoadMemCachedServers(): void { if (is_null($this->memCached)) { $this->memCached = new Memcached(); + + // Apply options if provided + if (is_array($this->options)) { + foreach ($this->options as $opt => $val) { + // Accept both numeric keys (constants) and string keys like 'OPT_CONNECT_TIMEOUT' + if (is_string($opt) && defined(Memcached::class . '::' . $opt)) { + $opt = constant(Memcached::class . '::' . $opt); + } + if (is_int($opt)) { + $this->memCached->setOption($opt, $val); + } else { + $this->logger->warning("[Memcached] Failed to set option {$opt} with value " . json_encode($val)); + } + } + } + + // Add servers. Accept formats: + // - ['host:port', ...] + // - [['host', port], ...] foreach ($this->servers as $server) { - $data = explode(":", $server); - $this->memCached->addServer($data[0], intval($data[1])); + $host = null; + $port = null; + if (is_string($server)) { + $data = explode(":", $server); + $host = $data[0] ?? '127.0.0.1'; + $port = isset($data[1]) ? intval($data[1]) : 11211; + } elseif (is_array($server) && isset($server[0])) { + $host = (string)$server[0]; + $port = isset($server[1]) ? intval($server[1]) : 11211; + } + + if ($host === null || $port === null) { + $this->logger->warning("[Memcached] Invalid server configuration skipped: " . json_encode($server)); + continue; // skip invalid entry + } + + $this->memCached->addServer($host, $port); + // Server health check $stats = $this->memCached->getStats(); - if (!isset($stats[$server]) || $stats[$server]['pid'] === -1) { - throw new StorageErrorException("Memcached server $server is down"); + $key = $host . ':' . $port; + if (!isset($stats[$key]) || (isset($stats[$key]['pid']) && $stats[$key]['pid'] === -1)) { + throw new StorageErrorException("Memcached server {$key} is down"); } } }