1<?php 2 3declare(strict_types=1); 4 5namespace voku\cache; 6 7use voku\cache\Exception\InvalidArgumentException; 8 9class CacheAdapterAutoManager 10{ 11 /** 12 * @var string[] 13 */ 14 private $adapter = []; 15 16 /** 17 * @var callable[]|null[] 18 */ 19 private $callableFunctions = []; 20 21 /** 22 * @param string $adapter 23 * @param callable|null $callableFunction 24 * 25 * @throws InvalidArgumentException 26 * 27 * @return $this 28 */ 29 public function addAdapter( 30 string $adapter, 31 callable $callableFunction = null 32 ): self 33 { 34 $this->validateAdapter($adapter); 35 $this->validateCallable($callableFunction); 36 37 $this->adapter[] = $adapter; 38 $this->callableFunctions[] = $callableFunction; 39 40 return $this; 41 } 42 43 /** 44 * @return \Generator|\Generator<string, callable> 45 */ 46 public function getAdapters(): \Generator 47 { 48 foreach ($this->adapter as $key => $value) { 49 yield $this->adapter[$key] => $this->callableFunctions[$key]; 50 } 51 } 52 53 /** 54 * @param self $adapterManager 55 * 56 * @throws InvalidArgumentException 57 * 58 * @return CacheAdapterAutoManager 59 */ 60 public function merge(self $adapterManager): self 61 { 62 foreach ($adapterManager->getAdapters() as $adapterTmp => $callableFunctionTmp) { 63 $this->validateAdapter($adapterTmp); 64 $this->validateCallable($callableFunctionTmp); 65 66 $key = \array_search($adapterTmp, $this->adapter, true); 67 68 if ($key) { 69 $this->adapter[$key] = $adapterTmp; 70 $this->callableFunctions[$key] = $callableFunctionTmp; 71 } else { 72 $this->adapter[] = $adapterTmp; 73 $this->callableFunctions[] = $callableFunctionTmp; 74 } 75 } 76 77 return $this; 78 } 79 80 /** 81 * @param string $replaceAdapter 82 * 83 * @throws InvalidArgumentException 84 */ 85 private function validateAdapter(string $replaceAdapter) 86 { 87 /** @noinspection PhpUnhandledExceptionInspection */ 88 $interfaces = (new \ReflectionClass($replaceAdapter))->getInterfaces(); 89 if (!\array_key_exists(iAdapter::class, $interfaces)) { 90 throw new InvalidArgumentException('"' . $replaceAdapter . '" did not implement the "iAdapter"-interface [' . \print_r($interfaces, true) . ']'); 91 } 92 } 93 94 /** 95 * @param callable $callableFunction 96 * 97 * @throws InvalidArgumentException 98 */ 99 private function validateCallable(callable $callableFunction = null) 100 { 101 if ( 102 $callableFunction !== null 103 && 104 !\is_callable($callableFunction) 105 ) { 106 throw new InvalidArgumentException('$callableFunction is not callable'); 107 } 108 } 109 110 /** 111 * @return CacheAdapterAutoManager 112 */ 113 public static function getDefaultsForAutoInit(): self 114 { 115 $cacheAdapterManager = new self(); 116 117 /** @noinspection PhpUnhandledExceptionInspection */ 118 $cacheAdapterManager->addAdapter( 119 AdapterMemcached::class, 120 static function () { 121 $memcached = null; 122 $isMemcachedAvailable = false; 123 if (\extension_loaded('memcached')) { 124 /** @noinspection PhpComposerExtensionStubsInspection */ 125 $memcached = new \Memcached(); 126 /** @noinspection PhpUsageOfSilenceOperatorInspection */ 127 $isMemcachedAvailable = @$memcached->addServer('127.0.0.1', 11211); 128 } 129 130 if (!$isMemcachedAvailable) { 131 $memcached = null; 132 } 133 134 return $memcached; 135 } 136 ); 137 138 /** @noinspection PhpUnhandledExceptionInspection */ 139 $cacheAdapterManager->addAdapter( 140 AdapterMemcache::class, 141 static function () { 142 $memcache = null; 143 $isMemcacheAvailable = false; 144 /** @noinspection ClassConstantCanBeUsedInspection */ 145 if (\class_exists('\Memcache')) { 146 /** @noinspection PhpComposerExtensionStubsInspection */ 147 $memcache = new \Memcache(); 148 /** @noinspection PhpUsageOfSilenceOperatorInspection */ 149 $isMemcacheAvailable = @$memcache->connect('127.0.0.1', 11211); 150 } 151 152 if (!$isMemcacheAvailable) { 153 $memcache = null; 154 } 155 156 return $memcache; 157 } 158 ); 159 160 /** @noinspection PhpUnhandledExceptionInspection */ 161 $cacheAdapterManager->addAdapter( 162 AdapterPredis::class, 163 static function () { 164 $redis = null; 165 $isRedisAvailable = false; 166 if ( 167 \extension_loaded('redis') 168 && 169 \class_exists('\Predis\Client') 170 ) { 171 /** @noinspection PhpUndefinedNamespaceInspection */ 172 /** @noinspection PhpUndefinedClassInspection */ 173 $redis = new \Predis\Client( 174 [ 175 'scheme' => 'tcp', 176 'host' => '127.0.0.1', 177 'port' => 6379, 178 'timeout' => '2.0', 179 ] 180 ); 181 182 try { 183 /** @noinspection PhpUndefinedMethodInspection */ 184 $redis->connect(); 185 /** @noinspection PhpUndefinedMethodInspection */ 186 $isRedisAvailable = $redis->getConnection()->isConnected(); 187 } catch (\Exception $e) { 188 // nothing 189 } 190 } 191 192 if ($isRedisAvailable === false) { 193 $redis = null; 194 } 195 196 return $redis; 197 } 198 ); 199 200 /** @noinspection PhpUnhandledExceptionInspection */ 201 $cacheAdapterManager->addAdapter( 202 AdapterXcache::class 203 ); 204 205 /** @noinspection PhpUnhandledExceptionInspection */ 206 $cacheAdapterManager->addAdapter( 207 AdapterApcu::class 208 ); 209 210 /** @noinspection PhpUnhandledExceptionInspection */ 211 $cacheAdapterManager->addAdapter( 212 AdapterApc::class 213 ); 214 215 /** @noinspection PhpUnhandledExceptionInspection */ 216 $cacheAdapterManager->addAdapter( 217 AdapterOpCache::class, 218 static function () { 219 return \realpath(\sys_get_temp_dir()) . '/simple_php_cache'; 220 } 221 ); 222 223 /** @noinspection PhpUnhandledExceptionInspection */ 224 $cacheAdapterManager->addAdapter( 225 AdapterArray::class 226 ); 227 228 return $cacheAdapterManager; 229 } 230} 231