ClientFactory.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <?php
  2. namespace Drupal\redis;
  3. use Drupal\Core\Site\Settings;
  4. /**
  5. * Common code and client singleton, for all Redis clients.
  6. */
  7. class ClientFactory {
  8. /**
  9. * Redis default host.
  10. */
  11. const REDIS_DEFAULT_HOST = "127.0.0.1";
  12. /**
  13. * Redis default port.
  14. */
  15. const REDIS_DEFAULT_PORT = 6379;
  16. /**
  17. * Redis default database: will select none (Database 0).
  18. */
  19. const REDIS_DEFAULT_BASE = NULL;
  20. /**
  21. * Redis default password: will not authenticate.
  22. */
  23. const REDIS_DEFAULT_PASSWORD = NULL;
  24. /**
  25. * Cache implementation namespace.
  26. */
  27. const REDIS_IMPL_CACHE = '\\Drupal\\redis\\Cache\\';
  28. /**
  29. * Lock implementation namespace.
  30. */
  31. const REDIS_IMPL_LOCK = '\\Drupal\\redis\\Lock\\';
  32. /**
  33. * Lock implementation namespace.
  34. */
  35. const REDIS_IMPL_FLOOD = '\\Drupal\\redis\\Flood\\';
  36. /**
  37. * Persistent Lock implementation namespace.
  38. */
  39. const REDIS_IMPL_PERSISTENT_LOCK = '\\Drupal\\redis\\PersistentLock\\';
  40. /**
  41. * Client implementation namespace.
  42. */
  43. const REDIS_IMPL_CLIENT = '\\Drupal\\redis\\Client\\';
  44. /**
  45. * Queue implementation namespace.
  46. */
  47. const REDIS_IMPL_QUEUE = '\\Drupal\\redis\\Queue\\';
  48. /**
  49. * Reliable queue implementation namespace.
  50. */
  51. const REDIS_IMPL_RELIABLE_QUEUE = '\\Drupal\\redis\\Queue\\Reliable';
  52. /**
  53. * @var \Drupal\redis\ClientInterface
  54. */
  55. protected static $_clientInterface;
  56. /**
  57. * @var mixed
  58. */
  59. protected static $_client;
  60. public static function hasClient() {
  61. return isset(self::$_client);
  62. }
  63. /**
  64. * Set client proxy.
  65. */
  66. public static function setClient(ClientInterface $interface) {
  67. if (isset(self::$_client)) {
  68. throw new \Exception("Once Redis client is connected, you cannot change client proxy instance.");
  69. }
  70. self::$_clientInterface = $interface;
  71. }
  72. /**
  73. * Lazy instanciate client proxy depending on the actual configuration.
  74. *
  75. * If you are using a lock or cache backend using one of the Redis client
  76. * implementations, this will be overridden at early bootstrap phase and
  77. * configuration will be ignored.
  78. *
  79. * @return ClientInterface
  80. */
  81. public static function getClientInterface()
  82. {
  83. if (!isset(self::$_clientInterface))
  84. {
  85. $settings = Settings::get('redis.connection', []);
  86. if (!empty($settings['interface']))
  87. {
  88. $className = self::getClass(self::REDIS_IMPL_CLIENT, $settings['interface']);
  89. self::$_clientInterface = new $className();
  90. }
  91. elseif (class_exists('Predis\Client'))
  92. {
  93. // Transparent and abitrary preference for Predis library.
  94. $className = self::getClass(self::REDIS_IMPL_CLIENT, 'Predis');
  95. self::$_clientInterface = new $className();
  96. }
  97. elseif (class_exists('Redis'))
  98. {
  99. // Fallback on PhpRedis if available.
  100. $className = self::getClass(self::REDIS_IMPL_CLIENT, 'PhpRedis');
  101. self::$_clientInterface = new $className();
  102. }
  103. else
  104. {
  105. if (!isset(self::$_clientInterface))
  106. {
  107. throw new \Exception("No client interface set.");
  108. }
  109. }
  110. }
  111. return self::$_clientInterface;
  112. }
  113. /**
  114. * Get underlaying library name.
  115. *
  116. * @return string
  117. */
  118. public static function getClientName() {
  119. return self::getClientInterface()->getName();
  120. }
  121. /**
  122. * Get client singleton.
  123. */
  124. public static function getClient() {
  125. if (!isset(self::$_client)) {
  126. $settings = Settings::get('redis.connection', []);
  127. $settings += [
  128. 'host' => self::REDIS_DEFAULT_HOST,
  129. 'port' => self::REDIS_DEFAULT_PORT,
  130. 'base' => self::REDIS_DEFAULT_BASE,
  131. 'password' => self::REDIS_DEFAULT_PASSWORD,
  132. ];
  133. // If using replication, lets create the client appropriately.
  134. if (isset($settings['replication']) && $settings['replication'] === TRUE) {
  135. foreach ($settings['replication.host'] as $key => $replicationHost) {
  136. if (!isset($replicationHost['port'])) {
  137. $settings['replication.host'][$key]['port'] = self::REDIS_DEFAULT_PORT;
  138. }
  139. }
  140. self::$_client = self::getClientInterface()->getClient(
  141. $settings['host'],
  142. $settings['port'],
  143. $settings['base'],
  144. $settings['password'],
  145. $settings['replication.host']);
  146. }
  147. else {
  148. self::$_client = self::getClientInterface()->getClient(
  149. $settings['host'],
  150. $settings['port'],
  151. $settings['base'],
  152. $settings['password']);
  153. }
  154. }
  155. return self::$_client;
  156. }
  157. /**
  158. * Get specific class implementing the current client usage for the specific
  159. * asked core subsystem.
  160. *
  161. * @param string $system
  162. * One of the ClientFactory::IMPL_* constant.
  163. * @param string $clientName
  164. * Client name, if fixed.
  165. *
  166. * @return string
  167. * Class name, if found.
  168. *
  169. * @throws \Exception
  170. * If not found.
  171. */
  172. public static function getClass($system, $clientName = NULL) {
  173. $className = $system . ($clientName ?: self::getClientName());
  174. if (!class_exists($className)) {
  175. throw new \Exception($className . " does not exists");
  176. }
  177. return $className;
  178. }
  179. /**
  180. * For unit testing only reset internals.
  181. */
  182. static public function reset() {
  183. self::$_clientInterface = null;
  184. self::$_client = null;
  185. }
  186. }