CacheManager.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <?php
  2. namespace Illuminate\Cache;
  3. use Aws\DynamoDb\DynamoDbClient;
  4. use Closure;
  5. use Illuminate\Contracts\Cache\Factory as FactoryContract;
  6. use Illuminate\Contracts\Cache\Store;
  7. use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
  8. use Illuminate\Support\Arr;
  9. use InvalidArgumentException;
  10. /**
  11. * @mixin \Illuminate\Cache\Repository
  12. * @mixin \Illuminate\Contracts\Cache\LockProvider
  13. */
  14. class CacheManager implements FactoryContract
  15. {
  16. /**
  17. * The application instance.
  18. *
  19. * @var \Illuminate\Contracts\Foundation\Application
  20. */
  21. protected $app;
  22. /**
  23. * The array of resolved cache stores.
  24. *
  25. * @var array
  26. */
  27. protected $stores = [];
  28. /**
  29. * The registered custom driver creators.
  30. *
  31. * @var array
  32. */
  33. protected $customCreators = [];
  34. /**
  35. * Create a new Cache manager instance.
  36. *
  37. * @param \Illuminate\Contracts\Foundation\Application $app
  38. * @return void
  39. */
  40. public function __construct($app)
  41. {
  42. $this->app = $app;
  43. }
  44. /**
  45. * Get a cache store instance by name, wrapped in a repository.
  46. *
  47. * @param string|null $name
  48. * @return \Illuminate\Contracts\Cache\Repository
  49. */
  50. public function store($name = null)
  51. {
  52. $name = $name ?: $this->getDefaultDriver();
  53. return $this->stores[$name] ??= $this->resolve($name);
  54. }
  55. /**
  56. * Get a cache driver instance.
  57. *
  58. * @param string|null $driver
  59. * @return \Illuminate\Contracts\Cache\Repository
  60. */
  61. public function driver($driver = null)
  62. {
  63. return $this->store($driver);
  64. }
  65. /**
  66. * Resolve the given store.
  67. *
  68. * @param string $name
  69. * @return \Illuminate\Contracts\Cache\Repository
  70. *
  71. * @throws \InvalidArgumentException
  72. */
  73. public function resolve($name)
  74. {
  75. $config = $this->getConfig($name);
  76. if (is_null($config)) {
  77. throw new InvalidArgumentException("Cache store [{$name}] is not defined.");
  78. }
  79. if (isset($this->customCreators[$config['driver']])) {
  80. return $this->callCustomCreator($config);
  81. }
  82. $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
  83. if (method_exists($this, $driverMethod)) {
  84. return $this->{$driverMethod}($config);
  85. }
  86. throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
  87. }
  88. /**
  89. * Call a custom driver creator.
  90. *
  91. * @param array $config
  92. * @return mixed
  93. */
  94. protected function callCustomCreator(array $config)
  95. {
  96. return $this->customCreators[$config['driver']]($this->app, $config);
  97. }
  98. /**
  99. * Create an instance of the APC cache driver.
  100. *
  101. * @param array $config
  102. * @return \Illuminate\Cache\Repository
  103. */
  104. protected function createApcDriver(array $config)
  105. {
  106. $prefix = $this->getPrefix($config);
  107. return $this->repository(new ApcStore(new ApcWrapper, $prefix));
  108. }
  109. /**
  110. * Create an instance of the array cache driver.
  111. *
  112. * @param array $config
  113. * @return \Illuminate\Cache\Repository
  114. */
  115. protected function createArrayDriver(array $config)
  116. {
  117. return $this->repository(new ArrayStore($config['serialize'] ?? false));
  118. }
  119. /**
  120. * Create an instance of the file cache driver.
  121. *
  122. * @param array $config
  123. * @return \Illuminate\Cache\Repository
  124. */
  125. protected function createFileDriver(array $config)
  126. {
  127. return $this->repository(
  128. (new FileStore($this->app['files'], $config['path'], $config['permission'] ?? null))
  129. ->setLockDirectory($config['lock_path'] ?? null)
  130. );
  131. }
  132. /**
  133. * Create an instance of the Memcached cache driver.
  134. *
  135. * @param array $config
  136. * @return \Illuminate\Cache\Repository
  137. */
  138. protected function createMemcachedDriver(array $config)
  139. {
  140. $prefix = $this->getPrefix($config);
  141. $memcached = $this->app['memcached.connector']->connect(
  142. $config['servers'],
  143. $config['persistent_id'] ?? null,
  144. $config['options'] ?? [],
  145. array_filter($config['sasl'] ?? [])
  146. );
  147. return $this->repository(new MemcachedStore($memcached, $prefix));
  148. }
  149. /**
  150. * Create an instance of the Null cache driver.
  151. *
  152. * @return \Illuminate\Cache\Repository
  153. */
  154. protected function createNullDriver()
  155. {
  156. return $this->repository(new NullStore);
  157. }
  158. /**
  159. * Create an instance of the Redis cache driver.
  160. *
  161. * @param array $config
  162. * @return \Illuminate\Cache\Repository
  163. */
  164. protected function createRedisDriver(array $config)
  165. {
  166. $redis = $this->app['redis'];
  167. $connection = $config['connection'] ?? 'default';
  168. $store = new RedisStore($redis, $this->getPrefix($config), $connection);
  169. return $this->repository(
  170. $store->setLockConnection($config['lock_connection'] ?? $connection)
  171. );
  172. }
  173. /**
  174. * Create an instance of the database cache driver.
  175. *
  176. * @param array $config
  177. * @return \Illuminate\Cache\Repository
  178. */
  179. protected function createDatabaseDriver(array $config)
  180. {
  181. $connection = $this->app['db']->connection($config['connection'] ?? null);
  182. $store = new DatabaseStore(
  183. $connection,
  184. $config['table'],
  185. $this->getPrefix($config),
  186. $config['lock_table'] ?? 'cache_locks',
  187. $config['lock_lottery'] ?? [2, 100],
  188. $config['lock_timeout'] ?? 86400,
  189. );
  190. return $this->repository($store->setLockConnection(
  191. $this->app['db']->connection($config['lock_connection'] ?? $config['connection'] ?? null)
  192. ));
  193. }
  194. /**
  195. * Create an instance of the DynamoDB cache driver.
  196. *
  197. * @param array $config
  198. * @return \Illuminate\Cache\Repository
  199. */
  200. protected function createDynamodbDriver(array $config)
  201. {
  202. $client = $this->newDynamodbClient($config);
  203. return $this->repository(
  204. new DynamoDbStore(
  205. $client,
  206. $config['table'],
  207. $config['attributes']['key'] ?? 'key',
  208. $config['attributes']['value'] ?? 'value',
  209. $config['attributes']['expiration'] ?? 'expires_at',
  210. $this->getPrefix($config)
  211. )
  212. );
  213. }
  214. /**
  215. * Create new DynamoDb Client instance.
  216. *
  217. * @return \Aws\DynamoDb\DynamoDbClient
  218. */
  219. protected function newDynamodbClient(array $config)
  220. {
  221. $dynamoConfig = [
  222. 'region' => $config['region'],
  223. 'version' => 'latest',
  224. 'endpoint' => $config['endpoint'] ?? null,
  225. ];
  226. if (! empty($config['key']) && ! empty($config['secret'])) {
  227. $dynamoConfig['credentials'] = Arr::only(
  228. $config, ['key', 'secret']
  229. );
  230. }
  231. if (! empty($config['token'])) {
  232. $dynamoConfig['credentials']['token'] = $config['token'];
  233. }
  234. return new DynamoDbClient($dynamoConfig);
  235. }
  236. /**
  237. * Create a new cache repository with the given implementation.
  238. *
  239. * @param \Illuminate\Contracts\Cache\Store $store
  240. * @return \Illuminate\Cache\Repository
  241. */
  242. public function repository(Store $store)
  243. {
  244. return tap(new Repository($store), function ($repository) {
  245. $this->setEventDispatcher($repository);
  246. });
  247. }
  248. /**
  249. * Set the event dispatcher on the given repository instance.
  250. *
  251. * @param \Illuminate\Cache\Repository $repository
  252. * @return void
  253. */
  254. protected function setEventDispatcher(Repository $repository)
  255. {
  256. if (! $this->app->bound(DispatcherContract::class)) {
  257. return;
  258. }
  259. $repository->setEventDispatcher(
  260. $this->app[DispatcherContract::class]
  261. );
  262. }
  263. /**
  264. * Re-set the event dispatcher on all resolved cache repositories.
  265. *
  266. * @return void
  267. */
  268. public function refreshEventDispatcher()
  269. {
  270. array_map([$this, 'setEventDispatcher'], $this->stores);
  271. }
  272. /**
  273. * Get the cache prefix.
  274. *
  275. * @param array $config
  276. * @return string
  277. */
  278. protected function getPrefix(array $config)
  279. {
  280. return $config['prefix'] ?? $this->app['config']['cache.prefix'];
  281. }
  282. /**
  283. * Get the cache connection configuration.
  284. *
  285. * @param string $name
  286. * @return array|null
  287. */
  288. protected function getConfig($name)
  289. {
  290. if (! is_null($name) && $name !== 'null') {
  291. return $this->app['config']["cache.stores.{$name}"];
  292. }
  293. return ['driver' => 'null'];
  294. }
  295. /**
  296. * Get the default cache driver name.
  297. *
  298. * @return string
  299. */
  300. public function getDefaultDriver()
  301. {
  302. return $this->app['config']['cache.default'];
  303. }
  304. /**
  305. * Set the default cache driver name.
  306. *
  307. * @param string $name
  308. * @return void
  309. */
  310. public function setDefaultDriver($name)
  311. {
  312. $this->app['config']['cache.default'] = $name;
  313. }
  314. /**
  315. * Unset the given driver instances.
  316. *
  317. * @param array|string|null $name
  318. * @return $this
  319. */
  320. public function forgetDriver($name = null)
  321. {
  322. $name ??= $this->getDefaultDriver();
  323. foreach ((array) $name as $cacheName) {
  324. if (isset($this->stores[$cacheName])) {
  325. unset($this->stores[$cacheName]);
  326. }
  327. }
  328. return $this;
  329. }
  330. /**
  331. * Disconnect the given driver and remove from local cache.
  332. *
  333. * @param string|null $name
  334. * @return void
  335. */
  336. public function purge($name = null)
  337. {
  338. $name ??= $this->getDefaultDriver();
  339. unset($this->stores[$name]);
  340. }
  341. /**
  342. * Register a custom driver creator Closure.
  343. *
  344. * @param string $driver
  345. * @param \Closure $callback
  346. * @return $this
  347. */
  348. public function extend($driver, Closure $callback)
  349. {
  350. $this->customCreators[$driver] = $callback->bindTo($this, $this);
  351. return $this;
  352. }
  353. /**
  354. * Set the application instance used by the manager.
  355. *
  356. * @param \Illuminate\Contracts\Foundation\Application $app
  357. * @return $this
  358. */
  359. public function setApplication($app)
  360. {
  361. $this->app = $app;
  362. return $this;
  363. }
  364. /**
  365. * Dynamically call the default driver instance.
  366. *
  367. * @param string $method
  368. * @param array $parameters
  369. * @return mixed
  370. */
  371. public function __call($method, $parameters)
  372. {
  373. return $this->store()->$method(...$parameters);
  374. }
  375. }