annotate core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\language\Plugin\LanguageNegotiation;
Chris@0 4
Chris@0 5 use Drupal\Core\Language\LanguageInterface;
Chris@0 6 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
Chris@0 7 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
Chris@0 8 use Drupal\Core\Render\BubbleableMetadata;
Chris@0 9 use Drupal\Core\Url;
Chris@0 10 use Drupal\language\LanguageNegotiationMethodBase;
Chris@0 11 use Drupal\language\LanguageSwitcherInterface;
Chris@0 12 use Symfony\Component\HttpFoundation\Request;
Chris@0 13
Chris@0 14 /**
Chris@0 15 * Class for identifying language via URL prefix or domain.
Chris@0 16 *
Chris@0 17 * @LanguageNegotiation(
Chris@0 18 * id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID,
Chris@0 19 * types = {\Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE,
Chris@0 20 * \Drupal\Core\Language\LanguageInterface::TYPE_CONTENT,
Chris@0 21 * \Drupal\Core\Language\LanguageInterface::TYPE_URL},
Chris@0 22 * weight = -8,
Chris@0 23 * name = @Translation("URL"),
Chris@0 24 * description = @Translation("Language from the URL (Path prefix or domain)."),
Chris@0 25 * config_route_name = "language.negotiation_url"
Chris@0 26 * )
Chris@0 27 */
Chris@0 28 class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {
Chris@0 29
Chris@0 30 /**
Chris@0 31 * The language negotiation method id.
Chris@0 32 */
Chris@0 33 const METHOD_ID = 'language-url';
Chris@0 34
Chris@0 35 /**
Chris@0 36 * URL language negotiation: use the path prefix as URL language indicator.
Chris@0 37 */
Chris@0 38 const CONFIG_PATH_PREFIX = 'path_prefix';
Chris@0 39
Chris@0 40 /**
Chris@0 41 * URL language negotiation: use the domain as URL language indicator.
Chris@0 42 */
Chris@0 43 const CONFIG_DOMAIN = 'domain';
Chris@0 44
Chris@0 45 /**
Chris@0 46 * {@inheritdoc}
Chris@0 47 */
Chris@0 48 public function getLangcode(Request $request = NULL) {
Chris@0 49 $langcode = NULL;
Chris@0 50
Chris@0 51 if ($request && $this->languageManager) {
Chris@0 52 $languages = $this->languageManager->getLanguages();
Chris@0 53 $config = $this->config->get('language.negotiation')->get('url');
Chris@0 54
Chris@0 55 switch ($config['source']) {
Chris@0 56 case LanguageNegotiationUrl::CONFIG_PATH_PREFIX:
Chris@0 57 $request_path = urldecode(trim($request->getPathInfo(), '/'));
Chris@0 58 $path_args = explode('/', $request_path);
Chris@0 59 $prefix = array_shift($path_args);
Chris@0 60
Chris@0 61 // Search prefix within added languages.
Chris@0 62 $negotiated_language = FALSE;
Chris@0 63 foreach ($languages as $language) {
Chris@0 64 if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
Chris@0 65 $negotiated_language = $language;
Chris@0 66 break;
Chris@0 67 }
Chris@0 68 }
Chris@0 69
Chris@0 70 if ($negotiated_language) {
Chris@0 71 $langcode = $negotiated_language->getId();
Chris@0 72 }
Chris@0 73 break;
Chris@0 74
Chris@0 75 case LanguageNegotiationUrl::CONFIG_DOMAIN:
Chris@0 76 // Get only the host, not the port.
Chris@0 77 $http_host = $request->getHost();
Chris@0 78 foreach ($languages as $language) {
Chris@0 79 // Skip the check if the language doesn't have a domain.
Chris@0 80 if (!empty($config['domains'][$language->getId()])) {
Chris@0 81 // Ensure that there is exactly one protocol in the URL when
Chris@0 82 // checking the hostname.
Chris@0 83 $host = 'http://' . str_replace(['http://', 'https://'], '', $config['domains'][$language->getId()]);
Chris@0 84 $host = parse_url($host, PHP_URL_HOST);
Chris@0 85 if ($http_host == $host) {
Chris@0 86 $langcode = $language->getId();
Chris@0 87 break;
Chris@0 88 }
Chris@0 89 }
Chris@0 90 }
Chris@0 91 break;
Chris@0 92 }
Chris@0 93 }
Chris@0 94
Chris@0 95 return $langcode;
Chris@0 96 }
Chris@0 97
Chris@0 98 /**
Chris@0 99 * {@inheritdoc}
Chris@0 100 */
Chris@0 101 public function processInbound($path, Request $request) {
Chris@0 102 $config = $this->config->get('language.negotiation')->get('url');
Chris@0 103
Chris@0 104 if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
Chris@0 105 $parts = explode('/', trim($path, '/'));
Chris@0 106 $prefix = array_shift($parts);
Chris@0 107
Chris@0 108 // Search prefix within added languages.
Chris@0 109 foreach ($this->languageManager->getLanguages() as $language) {
Chris@0 110 if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
Chris@0 111 // Rebuild $path with the language removed.
Chris@0 112 $path = '/' . implode('/', $parts);
Chris@0 113 break;
Chris@0 114 }
Chris@0 115 }
Chris@0 116 }
Chris@0 117
Chris@0 118 return $path;
Chris@0 119 }
Chris@0 120
Chris@0 121 /**
Chris@0 122 * {@inheritdoc}
Chris@0 123 */
Chris@0 124 public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
Chris@0 125 $url_scheme = 'http';
Chris@0 126 $port = 80;
Chris@0 127 if ($request) {
Chris@0 128 $url_scheme = $request->getScheme();
Chris@0 129 $port = $request->getPort();
Chris@0 130 }
Chris@0 131 $languages = array_flip(array_keys($this->languageManager->getLanguages()));
Chris@0 132 // Language can be passed as an option, or we go for current URL language.
Chris@0 133 if (!isset($options['language'])) {
Chris@0 134 $language_url = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL);
Chris@0 135 $options['language'] = $language_url;
Chris@0 136 }
Chris@0 137 // We allow only added languages here.
Chris@0 138 elseif (!is_object($options['language']) || !isset($languages[$options['language']->getId()])) {
Chris@0 139 return $path;
Chris@0 140 }
Chris@0 141 $config = $this->config->get('language.negotiation')->get('url');
Chris@0 142 if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
Chris@0 143 if (is_object($options['language']) && !empty($config['prefixes'][$options['language']->getId()])) {
Chris@0 144 $options['prefix'] = $config['prefixes'][$options['language']->getId()] . '/';
Chris@0 145 if ($bubbleable_metadata) {
Chris@0 146 $bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]);
Chris@0 147 }
Chris@0 148 }
Chris@0 149 }
Chris@0 150 elseif ($config['source'] == LanguageNegotiationUrl::CONFIG_DOMAIN) {
Chris@0 151 if (is_object($options['language']) && !empty($config['domains'][$options['language']->getId()])) {
Chris@0 152
Chris@0 153 // Save the original base URL. If it contains a port, we need to
Chris@0 154 // retain it below.
Chris@0 155 if (!empty($options['base_url'])) {
Chris@0 156 // The colon in the URL scheme messes up the port checking below.
Chris@0 157 $normalized_base_url = str_replace(['https://', 'http://'], '', $options['base_url']);
Chris@0 158 }
Chris@0 159
Chris@0 160 // Ask for an absolute URL with our modified base URL.
Chris@0 161 $options['absolute'] = TRUE;
Chris@0 162 $options['base_url'] = $url_scheme . '://' . $config['domains'][$options['language']->getId()];
Chris@0 163
Chris@0 164 // In case either the original base URL or the HTTP host contains a
Chris@0 165 // port, retain it.
Chris@0 166 if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
Chris@0 167 list(, $port) = explode(':', $normalized_base_url);
Chris@0 168 $options['base_url'] .= ':' . $port;
Chris@0 169 }
Chris@0 170 elseif (($url_scheme == 'http' && $port != 80) || ($url_scheme == 'https' && $port != 443)) {
Chris@0 171 $options['base_url'] .= ':' . $port;
Chris@0 172 }
Chris@0 173
Chris@0 174 if (isset($options['https'])) {
Chris@0 175 if ($options['https'] === TRUE) {
Chris@0 176 $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
Chris@0 177 }
Chris@0 178 elseif ($options['https'] === FALSE) {
Chris@0 179 $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
Chris@0 180 }
Chris@0 181 }
Chris@0 182
Chris@0 183 // Add Drupal's subfolder from the base_path if there is one.
Chris@0 184 $options['base_url'] .= rtrim(base_path(), '/');
Chris@0 185 if ($bubbleable_metadata) {
Chris@0 186 $bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.site']);
Chris@0 187 }
Chris@0 188 }
Chris@0 189 }
Chris@0 190 return $path;
Chris@0 191 }
Chris@0 192
Chris@0 193 /**
Chris@0 194 * {@inheritdoc}
Chris@0 195 */
Chris@0 196 public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
Chris@0 197 $links = [];
Chris@0 198 $query = $request->query->all();
Chris@0 199
Chris@0 200 foreach ($this->languageManager->getNativeLanguages() as $language) {
Chris@0 201 $links[$language->getId()] = [
Chris@0 202 // We need to clone the $url object to avoid using the same one for all
Chris@0 203 // links. When the links are rendered, options are set on the $url
Chris@0 204 // object, so if we use the same one, they would be set for all links.
Chris@0 205 'url' => clone $url,
Chris@0 206 'title' => $language->getName(),
Chris@0 207 'language' => $language,
Chris@0 208 'attributes' => ['class' => ['language-link']],
Chris@0 209 'query' => $query,
Chris@0 210 ];
Chris@0 211 }
Chris@0 212
Chris@0 213 return $links;
Chris@0 214 }
Chris@0 215
Chris@0 216 }