diff --git a/composer.json b/composer.json index e6f3e37..ff55584 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ }, "require": { "php": ">=7.4", + "ext-apcu": "*", "ext-curl": "*", "ext-json": "*", "ext-openssl": "*", diff --git a/src/App.php b/src/App.php index 1a19de6..f34cdf0 100644 --- a/src/App.php +++ b/src/App.php @@ -254,7 +254,7 @@ class App return $faker; }); - $container->set(CachePoolChain::class, function (\Redis $redis) { + $container->set(CachePoolChain::class, function (Redis $redis) { $caches = []; // If apc/apcu present, add it to the pool @@ -265,7 +265,9 @@ class App } // If Redis is configured, add it to the pool. - $caches[] = new RedisCachePool($redis); + if($redis->isAvailable()) { + $caches[] = new RedisCachePool($redis); + } $caches[] = new ArrayCachePool(); return new CachePoolChain($caches); @@ -287,13 +289,15 @@ class App return $monolog; }); - $container->set(\Redis::class, function (EnvironmentService $environmentService) { - $redis = new Redis(); - $redis->connect( + $container->set(Redis::class, function (EnvironmentService $environmentService) { + return (new Redis( $environmentService->get('REDIS_HOST', 'redis'), - $environmentService->get('REDIS_PORT', 6379) - ); + $environmentService->get('REDIS_PORT', 6379), + $environmentService->get('REDIS_TIMEOUT', 0.0) + )); + }); + $container->set(\Redis::class, function(Redis $redis){ return $redis; }); diff --git a/src/Redis/Redis.php b/src/Redis/Redis.php index f8efeaf..8a1f5e6 100644 --- a/src/Redis/Redis.php +++ b/src/Redis/Redis.php @@ -4,11 +4,34 @@ namespace Benzine\Redis; class Redis extends \Redis { + private string $host; + private int $port; + private int $timeout; + + public function __construct($host, $port = 6379, $timeout = 0.0) + { + $this->host = $host; + $this->port = $port; + $this->timeout = $timeout; + parent::__construct(); + } + + public function isAvailable() : bool + { + try { + $this->ping('am I human?'); + return true; + }catch(\RedisException $redisException){ + return false; + } + } + /** @var Lua\AbstractLuaExtension[] */ private array $scripts; public function __call($name, $arguments) { + \Kint::dump($name, $arguments);exit; foreach ($this->scripts as $script) { foreach ($script->getFunctionNames() as $functionName) { if (strtolower($name) == strtolower($functionName)) { @@ -20,6 +43,15 @@ class Redis extends \Redis } } + public function get($key) + { + if(!$this->isConnected()){ + parent::pconnect($this->host, $this->port, $this->timeout); + $this->initialiseExtensions(); + } + parent::get($key); // TODO: Change the autogenerated stub + } + public function initialiseExtensions(): void { $this->scripts[] = new Lua\SetIfHigher($this); @@ -30,7 +62,6 @@ class Redis extends \Redis public function connect($host, $port = 6379, $timeout = 0.0, $reserved = null, $retryInterval = 0, $readTimeout = 0.0): void { - parent::connect($host, $port, $timeout, $reserved, $retryInterval, $readTimeout); - $this->initialiseExtensions(); + throw new \RedisException("Do not directly call connect()"); } } diff --git a/src/Services/SessionService.php b/src/Services/SessionService.php index 9451036..d05ad8e 100644 --- a/src/Services/SessionService.php +++ b/src/Services/SessionService.php @@ -2,15 +2,18 @@ namespace Benzine\Services; +use Benzine\Redis\Redis; + class SessionService implements \SessionHandlerInterface { - protected \Redis $redis; + protected Redis $redis; + private ?bool $redisIsAvailable = null; protected $oldID; private int $lifetime = 43200; private array $dirtyCheck = []; - public function __construct(\Redis $redis) + public function __construct(Redis $redis) { $this->redis = $redis; } @@ -72,6 +75,13 @@ class SessionService implements \SessionHandlerInterface return true; } + public function useRedis() : bool { + if($this->redisIsAvailable === null){ + $this->redisIsAvailable = $this->redis->isAvailable(); + } + return $this->redisIsAvailable; + } + public function read($session_id) { if ($this->useAPCU()) { @@ -84,16 +94,17 @@ class SessionService implements \SessionHandlerInterface $session_id = $this->oldID ? $this->oldID : $session_id; } - $serialised = $this->redis->get("session:{$session_id}"); - if (null != $serialised) { - if (!empty($this->oldID)) { - // clean up old session after regenerate - $this->redis->del("session:{$session_id}"); - $this->oldID = null; + $result = ''; + if($this->useRedis()) { + $serialised = $this->redis->get("session:{$session_id}"); + if (null != $serialised) { + if (!empty($this->oldID)) { + // clean up old session after regenerate + $this->redis->del("session:{$session_id}"); + $this->oldID = null; + } + $result = unserialize($serialised); } - $result = unserialize($serialised); - } else { - $result = ''; } if ($this->useAPCU()) { @@ -105,19 +116,27 @@ class SessionService implements \SessionHandlerInterface return $result; } - public function write($session_id, $session_data) + /** + * @param string $session_id + * @param string $session_data + * @return bool Always returns true. + */ + public function write($session_id, $session_data) : bool { - $dirty = false; if ($this->useAPCU()) { $dirty = crc32(apcu_fetch('read-'.$session_id)) != crc32($session_data); } else { $dirty = $this->dirtyCheck['read-'.$session_id] != crc32($session_data); } - if ($dirty) { + + if ($this->useRedis() && $dirty) { $this->redis->set("session:{$session_id}", serialize($session_data)); $this->redis->expire("session:{$session_id}", $this->getLifetime()); } - apcu_store('read-'.$session_id, $session_data); + + if($this->useAPCU()) { + apcu_store('read-' . $session_id, $session_data); + } return true; } @@ -180,9 +199,6 @@ class SessionService implements \SessionHandlerInterface private function useAPCU(): bool { - // @todo fix apcu damnit - return false; - return function_exists('apcu_store'); } }