PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/web/core/lib/Drupal/Core/File/FileUrlGenerator.php

https://gitlab.com/mohamed_hussein/prodt
PHP | 239 lines | 108 code | 25 blank | 106 comment | 35 complexity | e939cafa0f8534b4fcdadecb2a5cf69f MD5 | raw file
  1. <?php
  2. namespace Drupal\Core\File;
  3. use Drupal\Component\Utility\UrlHelper;
  4. use Drupal\Core\Extension\ModuleHandlerInterface;
  5. use Drupal\Core\File\Exception\InvalidStreamWrapperException;
  6. use Drupal\Core\StreamWrapper\StreamWrapperManager;
  7. use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
  8. use Drupal\Core\Url;
  9. use Symfony\Component\HttpFoundation\RequestStack;
  10. /**
  11. * Default implementation for the file URL generator service.
  12. */
  13. class FileUrlGenerator implements FileUrlGeneratorInterface {
  14. /**
  15. * The stream wrapper manager.
  16. *
  17. * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
  18. */
  19. protected $streamWrapperManager;
  20. /**
  21. * The request stack.
  22. *
  23. * @var \Symfony\Component\HttpFoundation\RequestStack
  24. */
  25. protected $requestStack;
  26. /**
  27. * The module handler.
  28. *
  29. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  30. */
  31. protected $moduleHandler;
  32. /**
  33. * Constructs a new file URL generator object.
  34. *
  35. * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
  36. * The stream wrapper manager.
  37. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
  38. * The request stack.
  39. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  40. * The module handler.
  41. */
  42. public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager, RequestStack $request_stack, ModuleHandlerInterface $module_handler) {
  43. $this->streamWrapperManager = $stream_wrapper_manager;
  44. $this->requestStack = $request_stack;
  45. $this->moduleHandler = $module_handler;
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. public function generateString(string $uri): string {
  51. return $this->doGenerateString($uri, TRUE);
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public function generateAbsoluteString(string $uri): string {
  57. return $this->doGenerateString($uri, FALSE);
  58. }
  59. /**
  60. * Creates an absolute web-accessible URL string.
  61. *
  62. * @param string $uri
  63. * The URI to a file for which we need an external URL, or the path to a
  64. * shipped file.
  65. * @param bool $relative
  66. * Whether to return an relative or absolute URL.
  67. *
  68. * @return string
  69. * An absolute string containing a URL that may be used to access the
  70. * file.
  71. *
  72. * @throws \Drupal\Core\File\Exception\InvalidStreamWrapperException
  73. * If a stream wrapper could not be found to generate an external URL.
  74. */
  75. protected function doGenerateString(string $uri, bool $relative): string {
  76. // Allow the URI to be altered, e.g. to serve a file from a CDN or static
  77. // file server.
  78. $this->moduleHandler->alter('file_url', $uri);
  79. $scheme = StreamWrapperManager::getScheme($uri);
  80. if (!$scheme) {
  81. $baseUrl = $relative ? base_path() : $this->requestStack->getCurrentRequest()->getSchemeAndHttpHost() . base_path();
  82. return $this->generatePath($baseUrl, $uri);
  83. }
  84. elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') {
  85. // Check for HTTP and data URI-encoded URLs so that we don't have to
  86. // implement getExternalUrl() for the HTTP and data schemes.
  87. return $relative ? $this->transformRelative($uri) : $uri;
  88. }
  89. elseif ($wrapper = $this->streamWrapperManager->getViaUri($uri)) {
  90. // Attempt to return an external URL using the appropriate wrapper.
  91. $externalUrl = $wrapper->getExternalUrl();
  92. return $relative ? $this->transformRelative($externalUrl) : $externalUrl;
  93. }
  94. throw new InvalidStreamWrapperException();
  95. }
  96. /**
  97. * Generate a URL path.
  98. *
  99. * @param string $base_url
  100. * The base URL.
  101. * @param string $uri
  102. * The URI.
  103. *
  104. * @return string
  105. * The URL path.
  106. */
  107. protected function generatePath(string $base_url, string $uri): string {
  108. // Allow for:
  109. // - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
  110. // - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
  111. // http://example.com/bar.jpg by the browser when viewing a page over
  112. // HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
  113. // Both types of relative URIs are characterized by a leading slash, hence
  114. // we can use a single check.
  115. if (mb_substr($uri, 0, 1) == '/') {
  116. return $uri;
  117. }
  118. else {
  119. // If this is not a properly formatted stream, then it is a shipped
  120. // file. Therefore, return the urlencoded URI with the base URL
  121. // prepended.
  122. $options = UrlHelper::parse($uri);
  123. $path = $base_url . UrlHelper::encodePath($options['path']);
  124. // Append the query.
  125. if ($options['query']) {
  126. $path .= '?' . UrlHelper::buildQuery($options['query']);
  127. }
  128. // Append fragment.
  129. if ($options['fragment']) {
  130. $path .= '#' . $options['fragment'];
  131. }
  132. return $path;
  133. }
  134. }
  135. /**
  136. * {@inheritdoc}
  137. */
  138. public function generate(string $uri): Url {
  139. // Allow the URI to be altered, e.g. to serve a file from a CDN or static
  140. // file server.
  141. $this->moduleHandler->alter('file_url', $uri);
  142. $scheme = StreamWrapperManager::getScheme($uri);
  143. if (!$scheme) {
  144. // Allow for:
  145. // - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
  146. // - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
  147. // http://example.com/bar.jpg by the browser when viewing a page over
  148. // HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
  149. // Both types of relative URIs are characterized by a leading slash, hence
  150. // we can use a single check.
  151. if (mb_substr($uri, 0, 2) == '//') {
  152. return Url::fromUri($uri);
  153. }
  154. elseif (mb_substr($uri, 0, 1) == '/') {
  155. return Url::fromUri('base:' . str_replace($this->requestStack->getCurrentRequest()->getBasePath(), '', $uri));
  156. }
  157. else {
  158. // If this is not a properly formatted stream, then it is a shipped
  159. // file. Therefore, return the urlencoded URI.
  160. $options = UrlHelper::parse($uri);
  161. return Url::fromUri('base:' . UrlHelper::encodePath($options['path']), $options);
  162. }
  163. }
  164. elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') {
  165. // Check for HTTP and data URI-encoded URLs so that we don't have to
  166. // implement getExternalUrl() for the HTTP and data schemes.
  167. $options = UrlHelper::parse($uri);
  168. return Url::fromUri(urldecode($options['path']), $options);
  169. }
  170. elseif ($wrapper = $this->streamWrapperManager->getViaUri($uri)) {
  171. $external_url = $wrapper->getExternalUrl();
  172. $options = UrlHelper::parse($external_url);
  173. // @todo Switch to dependency injected request_context service after
  174. // https://www.drupal.org/project/drupal/issues/3256884 is fixed.
  175. if (UrlHelper::externalIsLocal($external_url, \Drupal::service('router.request_context')->getCompleteBaseUrl())) {
  176. // Attempt to return an external URL using the appropriate wrapper.
  177. return Url::fromUri('base:' . $this->transformRelative(urldecode($options['path']), FALSE), $options);
  178. }
  179. else {
  180. return Url::fromUri(urldecode($options['path']), $options);
  181. }
  182. }
  183. throw new InvalidStreamWrapperException();
  184. }
  185. /**
  186. * {@inheritdoc}
  187. */
  188. public function transformRelative(string $file_url, bool $root_relative = TRUE): string {
  189. // Unfortunately, we pretty much have to duplicate Symfony's
  190. // Request::getHttpHost() method because Request::getPort() may return NULL
  191. // instead of a port number.
  192. $request = $this->requestStack->getCurrentRequest();
  193. $host = $request->getHost();
  194. $scheme = $request->getScheme();
  195. $port = $request->getPort() ?: 80;
  196. // Files may be accessible on a different port than the web request.
  197. $file_url_port = parse_url($file_url, PHP_URL_PORT) ?? $port;
  198. if ($file_url_port != $port) {
  199. return $file_url;
  200. }
  201. if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
  202. $http_host = $host;
  203. }
  204. else {
  205. $http_host = $host . ':' . $port;
  206. }
  207. // If this should not be a root-relative path but relative to the drupal
  208. // base path, add it to the host to be removed from the URL as well.
  209. if (!$root_relative) {
  210. $http_host .= $request->getBasePath();
  211. }
  212. return preg_replace('|^https?://' . preg_quote($http_host, '|') . '|', '', $file_url);
  213. }
  214. }