DomainConfigOverrider.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <?php
  2. namespace Drupal\domain_config;
  3. use Drupal\domain\DomainInterface;
  4. use Drupal\Core\Cache\CacheableMetadata;
  5. use Drupal\Core\Config\ConfigFactoryOverrideInterface;
  6. use Drupal\Core\Config\StorageInterface;
  7. use Drupal\Core\Extension\ModuleHandlerInterface;
  8. /**
  9. * Domain-specific config overrides.
  10. *
  11. * @see \Drupal\language\Config\LanguageConfigFactoryOverride for ways
  12. * this might be improved.
  13. */
  14. class DomainConfigOverrider implements ConfigFactoryOverrideInterface {
  15. /**
  16. * The domain negotiator.
  17. *
  18. * @var \Drupal\domain\DomainNegotiatorInterface
  19. */
  20. protected $domainNegotiator;
  21. /**
  22. * A storage controller instance for reading and writing configuration data.
  23. *
  24. * @var \Drupal\Core\Config\StorageInterface
  25. */
  26. protected $storage;
  27. /**
  28. * The module handler.
  29. *
  30. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  31. */
  32. protected $moduleHandler;
  33. /**
  34. * The domain context of the request.
  35. *
  36. * @var \Drupal\domain\DomainInterface
  37. */
  38. protected $domain;
  39. /**
  40. * The language context of the request.
  41. *
  42. * @var \Drupal\Core\Language\LanguageInterface
  43. */
  44. protected $language;
  45. /**
  46. * Drupal language manager.
  47. *
  48. * Using dependency injection for this service causes a circular dependency.
  49. *
  50. * @var \Drupal\Core\Language\LanguageManagerInterface
  51. */
  52. protected $languageManager;
  53. /**
  54. * Indicates that the request context is set.
  55. *
  56. * @var bool
  57. */
  58. protected $contextSet;
  59. /**
  60. * Constructs a DomainConfigSubscriber object.
  61. *
  62. * @param \Drupal\Core\Config\StorageInterface $storage
  63. * The configuration storage engine.
  64. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  65. * The module handler.
  66. */
  67. public function __construct(StorageInterface $storage, ModuleHandlerInterface $module_handler) {
  68. $this->storage = $storage;
  69. $this->moduleHandler = $module_handler;
  70. }
  71. /**
  72. * {@inheritdoc}
  73. */
  74. public function loadOverrides($names) {
  75. // Try to prevent repeating lookups.
  76. static $lookups;
  77. // Key should be a known length, so hash.
  78. $key = md5(implode(':', $names));
  79. if (isset($lookups[$key])) {
  80. return $lookups[$key];
  81. }
  82. // Set the context of the override request.
  83. if (empty($this->contextSet)) {
  84. $this->initiateContext();
  85. }
  86. // Prepare our overrides.
  87. $overrides = [];
  88. // loadOverrides() runs on config entities, which means that if we try
  89. // to run this routine on our own data, then we end up in an infinite loop.
  90. // So ensure that we are _not_ looking up a domain.record.*.
  91. $check = current($names);
  92. $list = explode('.', $check);
  93. if (isset($list[0]) && isset($list[1]) && $list[0] == 'domain' && $list[1] == 'record') {
  94. $lookups[$key] = $overrides;
  95. return $overrides;
  96. }
  97. if (!empty($this->domain)) {
  98. foreach ($names as $name) {
  99. $config_name = $this->getDomainConfigName($name, $this->domain);
  100. // Check to see if the config storage has an appropriately named file
  101. // containing override data.
  102. if ($override = $this->storage->read($config_name['langcode'])) {
  103. $overrides[$name] = $override;
  104. }
  105. // Check to see if we have a file without a specific language.
  106. elseif ($override = $this->storage->read($config_name['domain'])) {
  107. $overrides[$name] = $override;
  108. }
  109. // Apply any settings.php overrides.
  110. if (isset($GLOBALS['config'][$config_name['langcode']])) {
  111. $overrides[$name] = $GLOBALS['config'][$config_name['langcode']];
  112. }
  113. elseif (isset($GLOBALS['config'][$config_name['domain']])) {
  114. $overrides[$name] = $GLOBALS['config'][$config_name['domain']];
  115. }
  116. }
  117. $lookups[$key] = $overrides;
  118. }
  119. return $overrides;
  120. }
  121. /**
  122. * Get configuration name for this hostname.
  123. *
  124. * It will be the same name with a prefix depending on domain and language:
  125. * @code domain.config.DOMAIN_ID.LANGCODE @endcode
  126. *
  127. * @param string $name
  128. * The name of the config object.
  129. * @param \Drupal\domain\DomainInterface $domain
  130. * The domain object.
  131. *
  132. * @return array
  133. * The domain-language, and domain-specific config names.
  134. */
  135. protected function getDomainConfigName($name, DomainInterface $domain) {
  136. return [
  137. 'langcode' => 'domain.config.' . $domain->id() . '.' . $this->language->getId() . '.' . $name,
  138. 'domain' => 'domain.config.' . $domain->id() . '.' . $name,
  139. ];
  140. }
  141. /**
  142. * {@inheritdoc}
  143. */
  144. public function getCacheSuffix() {
  145. $suffix = $this->domain ? $this->domain->id() : '';
  146. $suffix .= $this->language ? $this->language->getId() : '';
  147. return ($suffix) ? $suffix : NULL;
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
  153. return NULL;
  154. }
  155. /**
  156. * {@inheritdoc}
  157. */
  158. public function getCacheableMetadata($name) {
  159. if (empty($this->contextSet)) {
  160. $this->initiateContext();
  161. }
  162. $metadata = new CacheableMetadata();
  163. if (!empty($this->domain)) {
  164. $metadata->addCacheContexts(['url.site', 'languages:language_interface']);
  165. }
  166. return $metadata;
  167. }
  168. /**
  169. * Sets domain and language contexts for the request.
  170. *
  171. * We wait to do this in order to avoid circular dependencies
  172. * with the locale module.
  173. */
  174. protected function initiateContext() {
  175. // Prevent infinite lookups by caching the request. Since the _construct()
  176. // is called for each lookup, this is more efficient.
  177. $this->contextSet = TRUE;
  178. // Get the language context. Note that injecting the language manager
  179. // into the service created a circular dependency error, so we load from
  180. // the core service manager.
  181. $this->languageManager = \Drupal::languageManager();
  182. $this->language = $this->languageManager->getCurrentLanguage();
  183. // The same issue is true for the domainNegotiator.
  184. $this->domainNegotiator = \Drupal::service('domain.negotiator');
  185. // Get the domain context.
  186. $this->domain = $this->domainNegotiator->getActiveDomain();
  187. // If we have fired too early in the bootstrap, we must force the routine to
  188. // run.
  189. if (empty($this->domain)) {
  190. $this->domain = $this->domainNegotiator->getActiveDomain(TRUE);
  191. // Ensure the module hook cache is set properly.
  192. // See https://www.drupal.org/project/domain/issues/3025541
  193. $this->moduleHandler->resetImplementations();
  194. }
  195. }
  196. }