UrlPackage.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Asset;
  11. use Symfony\Component\Asset\Context\ContextInterface;
  12. use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
  13. use Symfony\Component\Asset\Exception\InvalidArgumentException;
  14. use Symfony\Component\Asset\Exception\LogicException;
  15. /**
  16. * Package that adds a base URL to asset URLs in addition to a version.
  17. *
  18. * The package allows to use more than one base URLs in which case
  19. * it randomly chooses one for each asset; it also guarantees that
  20. * any given path will always use the same base URL to be nice with
  21. * HTTP caching mechanisms.
  22. *
  23. * When the request context is available, this package can choose the
  24. * best base URL to use based on the current request scheme:
  25. *
  26. * * For HTTP request, it chooses between all base URLs;
  27. * * For HTTPs requests, it chooses between HTTPs base URLs and relative protocol URLs
  28. * or falls back to any base URL if no secure ones are available.
  29. *
  30. * @author Fabien Potencier <fabien@symfony.com>
  31. */
  32. class UrlPackage extends Package
  33. {
  34. private $baseUrls = array();
  35. private $sslPackage;
  36. /**
  37. * @param string|string[] $baseUrls Base asset URLs
  38. * @param VersionStrategyInterface $versionStrategy The version strategy
  39. * @param ContextInterface|null $context Context
  40. */
  41. public function __construct($baseUrls, VersionStrategyInterface $versionStrategy, ContextInterface $context = null)
  42. {
  43. parent::__construct($versionStrategy, $context);
  44. if (!is_array($baseUrls)) {
  45. $baseUrls = (array) $baseUrls;
  46. }
  47. if (!$baseUrls) {
  48. throw new LogicException('You must provide at least one base URL.');
  49. }
  50. foreach ($baseUrls as $baseUrl) {
  51. $this->baseUrls[] = rtrim($baseUrl, '/');
  52. }
  53. $sslUrls = $this->getSslUrls($baseUrls);
  54. if ($sslUrls && $baseUrls !== $sslUrls) {
  55. $this->sslPackage = new self($sslUrls, $versionStrategy);
  56. }
  57. }
  58. /**
  59. * {@inheritdoc}
  60. */
  61. public function getUrl($path)
  62. {
  63. if ($this->isAbsoluteUrl($path)) {
  64. return $path;
  65. }
  66. if (null !== $this->sslPackage && $this->getContext()->isSecure()) {
  67. return $this->sslPackage->getUrl($path);
  68. }
  69. $url = $this->getVersionStrategy()->applyVersion($path);
  70. if ($url && '/' != $url[0]) {
  71. $url = '/'.$url;
  72. }
  73. return $this->getBaseUrl($path).$url;
  74. }
  75. /**
  76. * Returns the base URL for a path.
  77. *
  78. * @param string $path
  79. *
  80. * @return string The base URL
  81. */
  82. public function getBaseUrl($path)
  83. {
  84. if (1 === count($this->baseUrls)) {
  85. return $this->baseUrls[0];
  86. }
  87. return $this->baseUrls[$this->chooseBaseUrl($path)];
  88. }
  89. /**
  90. * Determines which base URL to use for the given path.
  91. *
  92. * Override this method to change the default distribution strategy.
  93. * This method should always return the same base URL index for a given path.
  94. *
  95. * @param string $path
  96. *
  97. * @return int The base URL index for the given path
  98. */
  99. protected function chooseBaseUrl($path)
  100. {
  101. return (int) fmod(hexdec(substr(hash('sha256', $path), 0, 10)), count($this->baseUrls));
  102. }
  103. private function getSslUrls($urls)
  104. {
  105. $sslUrls = array();
  106. foreach ($urls as $url) {
  107. if ('https://' === substr($url, 0, 8) || '//' === substr($url, 0, 2)) {
  108. $sslUrls[] = $url;
  109. } elseif ('http://' !== substr($url, 0, 7)) {
  110. throw new InvalidArgumentException(sprintf('"%s" is not a valid URL', $url));
  111. }
  112. }
  113. return $sslUrls;
  114. }
  115. }