Client.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. // It may happen we get here with no autoloader set during the Drupal core
  3. // early bootstrap phase, at cache backend init time.
  4. if (!interface_exists('Redis_Client_FactoryInterface')) {
  5. require_once dirname(__FILE__) . '/Client/FactoryInterface.php';
  6. require_once dirname(__FILE__) . '/Client/Manager.php';
  7. }
  8. /**
  9. * This static class only reason to exist is to tie Drupal global
  10. * configuration to OOP driven code of this module: it will handle
  11. * everything that must be read from global configuration and let
  12. * other components live without any existence of it
  13. */
  14. class Redis_Client
  15. {
  16. /**
  17. * Cache implementation namespace.
  18. */
  19. const REDIS_IMPL_CACHE = 'Redis_Cache_';
  20. /**
  21. * Lock implementation namespace.
  22. */
  23. const REDIS_IMPL_LOCK = 'Redis_Lock_';
  24. /**
  25. * Cache implementation namespace.
  26. */
  27. const REDIS_IMPL_QUEUE = 'Redis_Queue_';
  28. /**
  29. * Path implementation namespace.
  30. */
  31. const REDIS_IMPL_PATH = 'Redis_Path_';
  32. /**
  33. * Client factory implementation namespace.
  34. */
  35. const REDIS_IMPL_CLIENT = 'Redis_Client_';
  36. /**
  37. * @var Redis_Client_Manager
  38. */
  39. private static $manager;
  40. /**
  41. * @var string
  42. */
  43. static protected $globalPrefix;
  44. /**
  45. * Get site default global prefix
  46. *
  47. * @return string
  48. */
  49. static public function getGlobalPrefix()
  50. {
  51. // Provide a fallback for multisite. This is on purpose not inside the
  52. // getPrefixForBin() function in order to decouple the unified prefix
  53. // variable logic and custom module related security logic, that is not
  54. // necessary for all backends. We can't just use HTTP_HOST, as multiple
  55. // hosts might be using the same database. Or, more commonly, a site
  56. // might not be a multisite at all, but might be using Drush leading to
  57. // a separate HTTP_HOST of 'default'. Likewise, we can't rely on
  58. // conf_path(), as settings.php might be modifying what database to
  59. // connect to. To mirror what core does with database caching we use
  60. // the DB credentials to inform our cache key.
  61. if (null === self::$globalPrefix) {
  62. if (isset($GLOBALS['db_url']) && is_string($GLOBALS['db_url'])) {
  63. // Drupal 6 specifics when using the cache_backport module, we
  64. // therefore cannot use \Database class to determine database
  65. // settings.
  66. self::$globalPrefix = md5($GLOBALS['db_url']);
  67. } else {
  68. require_once DRUPAL_ROOT . '/includes/database/database.inc';
  69. $dbInfo = Database::getConnectionInfo();
  70. $active = $dbInfo['default'];
  71. self::$globalPrefix = md5($active['host'] . $active['database'] . $active['prefix']['default']);
  72. }
  73. }
  74. return self::$globalPrefix;
  75. }
  76. /**
  77. * Get global default prefix
  78. *
  79. * @param string $namespace
  80. *
  81. * @return string
  82. */
  83. static public function getDefaultPrefix($namespace = null)
  84. {
  85. $ret = null;
  86. if (!empty($GLOBALS['drupal_test_info']['test_run_id'])) {
  87. $ret = $GLOBALS['drupal_test_info']['test_run_id'];
  88. } else {
  89. $prefixes = variable_get('cache_prefix', null);
  90. if (is_string($prefixes)) {
  91. // Variable can be a string which then considered as a default
  92. // behavior.
  93. $ret = $prefixes;
  94. } else if (null !== $namespace && isset($prefixes[$namespace])) {
  95. if (false !== $prefixes[$namespace]) {
  96. // If entry is set and not false an explicit prefix is set
  97. // for the bin.
  98. $ret = $prefixes[$namespace];
  99. } else {
  100. // If we have an explicit false it means no prefix whatever
  101. // is the default configuration.
  102. $ret = '';
  103. }
  104. } else {
  105. // Key is not set, we can safely rely on default behavior.
  106. if (isset($prefixes['default']) && false !== $prefixes['default']) {
  107. $ret = $prefixes['default'];
  108. } else {
  109. // When default is not set or an explicit false this means
  110. // no prefix.
  111. $ret = '';
  112. }
  113. }
  114. }
  115. if (empty($ret)) {
  116. $ret = Redis_Client::getGlobalPrefix();
  117. }
  118. return $ret;
  119. }
  120. /**
  121. * Get client manager
  122. *
  123. * @return Redis_Client_Manager
  124. */
  125. static public function getManager()
  126. {
  127. global $conf;
  128. if (null === self::$manager) {
  129. $className = self::getClass(self::REDIS_IMPL_CLIENT);
  130. $factory = new $className();
  131. // Build server list from conf
  132. $serverList = array();
  133. if (isset($conf['redis_servers'])) {
  134. $serverList = $conf['redis_servers'];
  135. }
  136. if (empty($serverList) || !isset($serverList['default'])) {
  137. // Backward configuration compatibility with older versions
  138. $serverList[Redis_Client_Manager::REALM_DEFAULT] = array();
  139. foreach (array('host', 'port', 'base', 'password', 'socket') as $key) {
  140. if (isset($conf['redis_client_' . $key])) {
  141. $serverList[Redis_Client_Manager::REALM_DEFAULT][$key] = $conf['redis_client_' . $key];
  142. }
  143. }
  144. }
  145. self::$manager = new Redis_Client_Manager($factory, $serverList);
  146. }
  147. return self::$manager;
  148. }
  149. /**
  150. * Find client class name
  151. *
  152. * @return string
  153. */
  154. static public function getClientInterfaceName()
  155. {
  156. global $conf;
  157. if (!empty($conf['redis_client_interface'])) {
  158. return $conf['redis_client_interface'];
  159. } else if (class_exists('Predis\Client')) {
  160. // Transparent and abitrary preference for Predis library.
  161. return $conf['redis_client_interface'] = 'Predis';
  162. } else if (class_exists('Redis')) {
  163. // Fallback on PhpRedis if available.
  164. return $conf['redis_client_interface'] = 'PhpRedis';
  165. } else {
  166. throw new Exception("No client interface set.");
  167. }
  168. }
  169. /**
  170. * For unit test use only
  171. */
  172. static public function reset(Redis_Client_Manager $manager = null)
  173. {
  174. self::$manager = $manager;
  175. }
  176. /**
  177. * Get the client for the 'default' realm
  178. *
  179. * @return mixed
  180. *
  181. * @deprecated
  182. */
  183. public static function getClient()
  184. {
  185. return self::getManager()->getClient();
  186. }
  187. /**
  188. * Get specific class implementing the current client usage for the specific
  189. * asked core subsystem.
  190. *
  191. * @param string $system
  192. * One of the Redis_Client::IMPL_* constant.
  193. * @param string $clientName
  194. * Client name, if fixed.
  195. *
  196. * @return string
  197. * Class name, if found.
  198. *
  199. * @deprecated
  200. */
  201. static public function getClass($system)
  202. {
  203. $class = $system . self::getClientInterfaceName();
  204. if (!class_exists($class)) {
  205. throw new Exception(sprintf("Class '%s' does not exist", $class));
  206. }
  207. return $class;
  208. }
  209. }