Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions docs/class-memcached-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -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));
```


4 changes: 2 additions & 2 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
Expand Down
52 changes: 45 additions & 7 deletions src/Psr16/MemcachedEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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;
}

/**
Expand All @@ -58,13 +62,47 @@ 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);
}
}
}

// 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");
}
}
}
Expand Down