kodedphp /
cache-simple
| 1 | <?php |
||||
| 2 | /* |
||||
| 3 | * This file is part of the Koded package. |
||||
| 4 | * |
||||
| 5 | * (c) Mihail Binev <[email protected]> |
||||
| 6 | * |
||||
| 7 | * Please view the LICENSE distributed with this source code |
||||
| 8 | * for the full copyright and license information. |
||||
| 9 | */ |
||||
| 10 | |||||
| 11 | namespace Koded\Caching\Client; |
||||
| 12 | |||||
| 13 | use Exception; |
||||
| 14 | use Koded\Caching\{Cache, CacheException}; |
||||
| 15 | use Memcached; |
||||
| 16 | use Koded\Caching\Configuration\{MemcachedConfiguration, PredisConfiguration, RedisConfiguration}; |
||||
| 17 | use Koded\Stdlib\{Configuration, Serializer}; |
||||
| 18 | use Koded\Stdlib\Serializer\SerializerFactory; |
||||
| 19 | use Psr\Log\{LoggerInterface, NullLogger}; |
||||
| 20 | use Redis; |
||||
| 21 | use Throwable; |
||||
| 22 | use function error_log; |
||||
| 23 | use function getenv; |
||||
| 24 | use function sprintf; |
||||
| 25 | use function strtolower; |
||||
| 26 | |||||
| 27 | final class ClientFactory |
||||
| 28 | { |
||||
| 29 | public const CACHE_CLIENT = 'CACHE_CLIENT'; |
||||
| 30 | |||||
| 31 | public function __construct(private Configuration $factory) {} |
||||
| 32 | |||||
| 33 | /** |
||||
| 34 | * Create an instance of specific cache client. |
||||
| 35 | * |
||||
| 36 | * @param string $client The required cache client |
||||
| 37 | * (memcached, redis, predis, shmop, file, memory) |
||||
| 38 | * |
||||
| 39 | * @return Cache An instance of the cache client |
||||
| 40 | * @throws CacheException |
||||
| 41 | * @throws Exception |
||||
| 42 | */ |
||||
| 43 | 560 | public function new(string $client = ''): Cache |
|||
| 44 | { |
||||
| 45 | 560 | $client = strtolower($client ?: getenv(self::CACHE_CLIENT) ?: 'memory'); |
|||
| 46 | 560 | $config = $this->factory->build($client); |
|||
| 47 | |||||
| 48 | 560 | return match ($client) { |
|||
| 49 | 560 | 'memory' => new MemoryClient($config->get('ttl')), |
|||
| 50 | 560 | 'memcached' => $this->createMemcachedClient($config), |
|||
| 51 | 560 | 'redis' => $this->createRedisClient($config), |
|||
| 52 | 560 | 'predis' => $this->createPredisClient($config), |
|||
| 53 | 560 | 'shmop' => new ShmopClient((string)$config->get('dir'), $config->get('ttl')), |
|||
| 54 | 560 | 'file' => new FileClient($this->getLogger($config), (string)$config->get('dir'), $config->get('ttl')), |
|||
| 55 | 560 | default => throw CacheException::forUnsupportedClient($client) |
|||
| 56 | 560 | }; |
|||
| 57 | } |
||||
| 58 | |||||
| 59 | 70 | private function createMemcachedClient(MemcachedConfiguration|Configuration $conf): Cache |
|||
| 60 | { |
||||
| 61 | 70 | $client = new Memcached($conf->get('id')); |
|||
| 62 | 70 | $client->setOptions($conf->getOptions()); |
|||
| 63 | 70 | if (empty($client->getServerList())) { |
|||
| 64 | 70 | $client->addServers($conf->getServers()); |
|||
| 65 | } |
||||
| 66 | 70 | return new MemcachedClient($client, $conf->getTtl()); |
|||
| 67 | } |
||||
| 68 | |||||
| 69 | 155 | private function createRedisClient(RedisConfiguration|Configuration $conf): Cache |
|||
| 70 | { |
||||
| 71 | 155 | $serializer = $conf->get('serializer'); |
|||
| 72 | 155 | $binary = $conf->get('binary'); |
|||
| 73 | 155 | if (Serializer::JSON === $serializer && $binary) { |
|||
| 74 | 71 | return new RedisJsonClient( |
|||
| 75 | 71 | $this->newRedisClient($conf), |
|||
| 76 | 71 | SerializerFactory::new((string)$binary, ...$conf->get('options', [0])), |
|||
| 77 | 71 | (int)$conf->get('options'), |
|||
| 78 | 71 | $conf->get('ttl') |
|||
| 79 | 71 | ); |
|||
| 80 | } |
||||
| 81 | 84 | return new RedisClient( |
|||
| 82 | 84 | $this->newRedisClient($conf), |
|||
| 83 | 84 | SerializerFactory::new($serializer, ...$conf->get('options', [0])), |
|||
| 84 | 84 | $conf->get('ttl') |
|||
| 85 | 84 | ); |
|||
| 86 | } |
||||
| 87 | |||||
| 88 | 135 | private function createPredisClient(PredisConfiguration|Configuration $conf): Cache |
|||
| 89 | { |
||||
| 90 | 135 | $binary = $conf->get('binary'); |
|||
| 91 | 135 | if (Serializer::JSON === $conf->get('serializer') && $binary) { |
|||
| 92 | 71 | return new PredisJsonClient( |
|||
| 93 | 71 | $this->newPredisClient($conf), |
|||
| 94 | 71 | SerializerFactory::new((string)$binary, ...$conf->get('options', [0])), |
|||
| 95 | 71 | (int)$conf->get('options'), |
|||
| 96 | 71 | $conf->get('ttl') |
|||
| 97 | 71 | ); |
|||
| 98 | } |
||||
| 99 | 64 | return new PredisClient( |
|||
| 100 | 64 | $this->newPredisClient($conf), |
|||
| 101 | 64 | SerializerFactory::new($conf->get('serializer'), ...$conf->get('options', [0])), |
|||
| 102 | 64 | $conf->get('ttl') |
|||
| 103 | 64 | ); |
|||
| 104 | } |
||||
| 105 | |||||
| 106 | 155 | private function newRedisClient(RedisConfiguration $conf): Redis |
|||
| 107 | { |
||||
| 108 | 155 | $client = new Redis; |
|||
| 109 | try { |
||||
| 110 | 155 | @$client->connect(...$conf->getConnectionParams()); |
|||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
It seems like you do not handle an error condition for
connect(). This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||||
| 111 | 154 | $client->setOption(Redis::OPT_SERIALIZER, $conf->get('type')); |
|||
| 112 | 154 | $client->setOption(Redis::OPT_PREFIX, $conf->get('prefix')); |
|||
| 113 | 153 | $client->select((int)$conf->get('db')); |
|||
| 114 | 153 | if ($auth = $conf->get('auth')) { |
|||
| 115 | 1 | $client->auth($auth); |
|||
| 116 | } |
||||
| 117 | 152 | if ($message = $client->getLastError()) { |
|||
| 118 | // [NOTE] Redis module complains if auth is set, |
||||
| 119 | // but <Redis v5 or less> does not have auth |
||||
| 120 | throw new Exception($message); |
||||
| 121 | } |
||||
| 122 | 152 | return $client; |
|||
| 123 | |||||
| 124 | } /** @noinspection PhpRedundantCatchClauseInspection */ |
||||
| 125 | 3 | catch (Throwable $e) { |
|||
| 126 | 3 | error_log(sprintf(PHP_EOL . '[Redis] %s: %s', $e::class, $e->getMessage())); |
|||
| 127 | 3 | error_log('[Redis] with conf: ' . $conf->delete('auth')->toJSON()); |
|||
| 128 | 3 | throw CacheException::withConnectionErrorFor('Redis'); |
|||
| 129 | } |
||||
| 130 | } |
||||
| 131 | |||||
| 132 | 135 | private function newPredisClient(PredisConfiguration $conf): \Predis\Client |
|||
| 133 | { |
||||
| 134 | 135 | $client = new \Predis\Client($conf->getConnectionParams(), $conf->getOptions()); |
|||
| 135 | |||||
| 136 | try { |
||||
| 137 | 135 | $client->connect(); |
|||
| 138 | 134 | $client->select((int)$conf->get('db')); |
|||
| 139 | 134 | if ($auth = $conf->get('auth')) { |
|||
| 140 | 1 | $client->auth($auth); |
|||
| 141 | } |
||||
| 142 | 133 | return $client; |
|||
| 143 | |||||
| 144 | } /** @noinspection PhpRedundantCatchClauseInspection */ |
||||
| 145 | 2 | catch (Throwable $e) { |
|||
| 146 | 2 | error_log(sprintf(PHP_EOL . '[Predis] %s: %s', $e::class, $e->getMessage())); |
|||
| 147 | 2 | error_log('[Predis] with conf: ' . $conf->delete('auth')->toJSON()); |
|||
| 148 | 2 | throw CacheException::withConnectionErrorFor('Predis'); |
|||
| 149 | } |
||||
| 150 | } |
||||
| 151 | |||||
| 152 | 63 | private function getLogger(Configuration $conf): LoggerInterface |
|||
| 153 | { |
||||
| 154 | 63 | $logger = $conf->logger ?? new NullLogger; |
|||
|
0 ignored issues
–
show
|
|||||
| 155 | 63 | if ($logger instanceof LoggerInterface) { |
|||
| 156 | 62 | return $logger; |
|||
| 157 | } |
||||
| 158 | 1 | throw CacheException::forUnsupportedLogger(LoggerInterface::class, $logger::class); |
|||
| 159 | } |
||||
| 160 | } |
||||
| 161 |